目录
- 设计图
- 叽叽喳喳的分析
- 1.绘制步骤
- 2.分析,下图是我分析的产物,逻辑设计图,哈哈哈哈:
- 常识
- 不看教程的可以直接拉到最后参考代码
设计图
这是设计给的设计图,也是做出来的效果图。不看教程的可以直接拉到最后参考代码
叽叽喳喳的分析
别怪我废话多,因为画这个图老费劲了,你说平时敲代码百度搜搜,参考参考,啥都有了。可是这圆弧两端再加俩圆可愁死我了,啥都搜不到。第三方的那些框架长得也不像啊。于是自己进行数学分析。
开始画:
1.绘制步骤
首先画一个灰色的圆圈,作为背景(灰色圆)
然后是覆盖一段圆弧作为进度条(蓝色圆弧)
然后是最小圆的绘制,进度条开始端(姜黄圆)
然后是进度条末端圆的绘制(明黄圆)
2.分析,下图是我分析的产物,逻辑设计图,哈哈哈哈:
注意: 有一个需要提醒一下的是,绘制的时候,需要设置笔触宽度,也就是setStrokeWidth这个方法。笔触的宽度,并不是往笔触内侧增加宽度的,而是往外侧增加一半,往内侧增加一半。
举例: 直线,红色那条线才是真正的落笔处。黄色和绿色是两侧拓宽的宽度。
如果线宽为10,则绿色和黄色各自的宽度为5
圆的绘制,同理
说到这里是因为想告诉大家,在绘制的过程中,各圆的圆心位置的确定,还需要考虑到笔触的宽度。
为了让大家看明白,我专门花了几个辅助理解的圆圈
也分别用小红点标出了主要绘制的四个圆(灰\蓝\姜黄\明黄)的圆心位置(灰原和蓝弧大小一样,圆心位置也是重合的)。
脑瓜疼的,就是那个明黄点圆心位置的确定,因为它是动态的。
如下图,红色那个圆环就是我上文中提到的灰圆和蓝弧所在位置。下面表述,皆用红环来表示。红环之间的那条黑线才是真正落笔之处。
所以姜黄圆和明黄圆的圆心都应该在这条黑线上。
首先已知整个图的宽度width,设笔触的宽度为stroke
姜黄圆根据图示,它应该包含在红环里,所以它的直径,应该为红环笔触(也就是灰原和蓝弧的笔触)的宽度。
minRadius=stroke/2;
而明黄圆的直径,我这里想画一个比姜黄圆的直径大一倍的圆,所以它的直径是姜黄圆的两倍。
yellowRadius=stroke=2minRadius;
则红环的半径为(即红环中黑线到圆心的距离),radius=(width-4minRadius)/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))
这样就可以了。。感觉自己废话连篇
三角函数的求圆心示意图
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);
}
好啦成品图