Android:自定义View画个圆再画个弧,圆弧两端再画个圆

设计图

这是设计给的设计图,也是做出来的效果图。不看教程的可以直接拉到最后参考代码
在这里插入图片描述

叽叽喳喳的分析

别怪我废话多,因为画这个图老费劲了,你说平时敲代码百度搜搜,参考参考,啥都有了。可是这圆弧两端再加俩圆可愁死我了,啥都搜不到。第三方的那些框架长得也不像啊。于是自己进行数学分析。
开始画:

1.绘制步骤

首先画一个灰色的圆圈,作为背景(灰色圆)
在这里插入图片描述
然后是覆盖一段圆弧作为进度条(蓝色圆弧)
在这里插入图片描述
然后是最小圆的绘制,进度条开始端(姜黄圆)
在这里插入图片描述
然后是进度条末端圆的绘制(明黄圆)
在这里插入图片描述

2.分析,下图是我分析的产物,逻辑设计图,哈哈哈哈:

注意: 有一个需要提醒一下的是,绘制的时候,需要设置笔触宽度,也就是setStrokeWidth这个方法。笔触的宽度,并不是往笔触内侧增加宽度的,而是往外侧增加一半,往内侧增加一半。
举例: 直线,红色那条线才是真正的落笔处。黄色和绿色是两侧拓宽的宽度。
如果线宽为10,则绿色和黄色各自的宽度为5
在这里插入图片描述
圆的绘制,同理
在这里插入图片描述
说到这里是因为想告诉大家,在绘制的过程中,各圆的圆心位置的确定,还需要考虑到笔触的宽度。

为了让大家看明白,我专门花了几个辅助理解的圆圈
也分别用小红点标出了主要绘制的四个圆(灰\蓝\姜黄\明黄)的圆心位置(灰原和蓝弧大小一样,圆心位置也是重合的)。
脑瓜疼的,就是那个明黄点圆心位置的确定,因为它是动态的。

如下图,红色那个圆环就是我上文中提到的灰圆和蓝弧所在位置。下面表述,皆用红环来表示。红环之间的那条黑线才是真正落笔之处。
所以姜黄圆和明黄圆的圆心都应该在这条黑线上。
首先已知整个图的宽度width,设笔触的宽度为stroke
姜黄圆根据图示,它应该包含在红环里,所以它的直径,应该为红环笔触(也就是灰原和蓝弧的笔触)的宽度。
minRadius=stroke/2;
而明黄圆的直径,我这里想画一个比姜黄圆的直径大一倍的圆,所以它的直径是姜黄圆的两倍。
yellowRadius=stroke=2minRadius;
则红环的半径为(即红环中黑线到圆心的距离),radius=(width-4
minRadius)/2;
综上,第一个圆的圆心坐标为(半径+2minRadius,半径+2minRadius),最小圆圆心为(半径+2minRadius,2minRadius)
最重要的明黄圆的圆心,利用三角函数
x=(radius + 2 * minRadius + radius * Math.sin(360 * progress / max * Math.PI / 180))
y=(radius + 2 * minRadius - radius * Math.cos(360 * progress / max * Math.PI / 180))

这样就可以了。。感觉自己废话连篇
为了![在这里插入图片描述](https://img-blog.csdnimg.cn/20190524131208154.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2NoYW5naHV6aWNoYW5nY2hhbmc=,size_16,color_FFFFFF,t_70
三角函数的求圆心示意图
a和c的夹角α我们知道,斜边c也知道,根据sinα=b/c;cosα=a/c。a和b就可以用已知条件求出来了。
over.
在这里插入图片描述

常识

Paint.Style.STROKE 只绘制图形轮廓(描边)
Paint.Style.FILL 只绘制图形内容
Paint.Style.FILL_AND_STROKE 既绘制轮廓也绘制内容

不看教程的可以直接拉到最后参考代码

代码写得是不是很蠢啊,求大佬给意见>~<
最后要插入代码:
mark一个画弧链接
未完待续–》有空的时候,记得加自定义属性,把笔触什么的设置为变量

package com.snap.awesomeserial.ui.widget;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.graphics.Rect;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;

public class CircleProgressView extends View {
    int progress = 0;
    private String text = "0%";
    private int max = 100;

    public CircleProgressView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    public CircleProgressView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public CircleProgressView(Context context) {
        super(context);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Paint paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Style.STROKE);
        int stroke=10;//笔触,是最小圆半径的两倍
        int minRadius=stroke/2;
        float width = getMeasuredWidth();
        float radius = (width - minRadius*4) / 2;//最大圆的半径
        paint.setStrokeWidth(stroke);
        paint.setColor(0xFFDBDBDB);//灰色
        canvas.drawCircle(radius + 2*minRadius, radius + 2*minRadius, radius, paint);
        paint.setStrokeWidth(stroke);
        RectF oval = new RectF(2*minRadius, 2*minRadius, radius * 2 + 2*minRadius, radius * 2 + 2*minRadius);
        paint.setColor(0xFF4080F4);//蓝色
        //这是画蓝弧,第三个参数false是指,不连接到圆心
        canvas.drawArc(oval, -90, 360 * progress / max, false, paint);
        paint.setStrokeWidth(stroke);
        paint.setStyle(Style.FILL);//设置画笔类型为填充
        paint.setColor(0xFFD3623E);//姜黄
        RectF point = new RectF(radius + minRadius,minRadius, radius + 3*minRadius, 3*minRadius);
        //画一个跨度360的弧,那肯定是个圆了,然后第三个参数为true,表示连接到圆心,这就变成了一个实心圆。你也可以直接drawCircle
        canvas.drawArc(point, 0, 360, true, paint);//画一个点
        paint.setStrokeWidth(2*stroke);
        paint.setColor(0xFFEDBC40);//明黄
        canvas.drawCircle((float) (radius + 2*minRadius + radius * Math.sin(360 * progress / max * Math.PI / 180)), (float) (radius + 2*minRadius - radius * Math.cos(360 * progress / max * Math.PI / 180)), stroke, paint);
        paint.setStyle(Paint.Style.STROKE);
        paint.setTextSize(14);
        paint.setStrokeWidth(1.0f);
        Rect bounds = new Rect();
        paint.getTextBounds(text, 0, text.length(), bounds);
        paint.setColor(Color.GRAY);
        paint.setStyle(Style.FILL);
        canvas.drawText(text, width / 2 - bounds.width() / 2,
                width / 2 + bounds.height() / 2, paint);
    }

    /**
     * 初始设置当前进度的最大值-默认100
     *
     * @param max
     */
    public void setMax(int max) {
        this.max = max;
    }

    /**
     * 更新进度和文字
     *
     * @param progress
     * @param text
     */
    public void setProgressAndText(int progress, String text) {
        this.progress = progress;
        this.text = text;
        postInvalidate();
    }

}

教程就是把其他人当傻子,所以即使调用简单,我也要写一遍。
随便哪个xml文件

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_height="match_parent"
    android:background="#000000">
    <!--这里是调用-->
    <com.snap.awesomeserial.ui.widget.CircleProgressView
        android:id="@+id/circleView"
        android:layout_width="500dp"
        android:layout_height="500dp" />
  </LinearLayout>

然后xml对应的activity里

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        circleView = (CircleProgressView) findViewById(R.id.circleView);
        circleView.setMax(100);
        int progress=75;
        String text ="当前温度"+progress+"C";
        circleView.setProgressAndText(progress, text);
    }

好啦成品图
在这里插入图片描述

发布了72 篇原创文章 · 获赞 28 · 访问量 7万+

猜你喜欢

转载自blog.csdn.net/changhuzichangchang/article/details/90514683