Android 自定义圆形进度View
- 2020 年 3 月 26 日
- 笔记
接下来我们来实现一个这样的基本显示效果,在进行前我们先来看看需要哪些基本知识:
1怎么使用attrs定义和获取属性
2 使用Paint画圆与文本绘制
3 认真分析UI的界面逻辑
一 attrs.xml定义属性
1 定义一个class继承自View
public class CirCleView extends View { public CirCleView(Context context) { this(context, null); } public CirCleView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public CirCleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { this(context, attrs, defStyleAttr, 0); } public CirCleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { super(context, attrs, defStyleAttr, defStyleRes); }
2 在values下面创建一个attrs.xml资源文件
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="CirCleView"> <attr name="mBackCircleWidth" format="dimension" /> <attr name="mBackCircleColor" format="color" /> <attr name="mPercent" format="string" /> <attr name="mPadding" format="dimension" /> <attr name="mOuterCircleColorWitdth" format="dimension"/> <attr name="mOuterCircleColor" format="color" /> <attr name="mTitle" format="string" /> <attr name="mTitleTextSize" format="dimension" /> <attr name="mTitleTextColor" format="color" /> </declare-styleable> </resources>
3 在自定义view中获取这些属性
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CirCleView); backCircleWidth = typedArray.getDimension(R.styleable.CirCleView_mBackCircleWidth, 1); backCircleColor = typedArray.getColor(R.styleable.CirCleView_mBackCircleColor, Color.parseColor("#ff0000")); outerCircleColor = typedArray.getColor(R.styleable.CirCleView_mOuterCircleColor, Color.parseColor("#00ff00")); outerCircleWidth = typedArray.getDimension(R.styleable.CirCleView_mOuterCircleColorWitdth, 2); mPadding = typedArray.getDimension(R.styleable.CirCleView_mPadding, 0); String mPercent = typedArray.getString(R.styleable.CirCleView_mPercent); currentPercent = Float.parseFloat(mPercent); title = typedArray.getString(R.styleable.CirCleView_mTitle); titleTextColor = typedArray.getColor(R.styleable.CirCleView_mTitleTextColor, Color.parseColor("#ffffff")); titleTextSize = typedArray.getDimension(R.styleable.CirCleView_mTitleTextSize, 20); typedArray.recycle();
获取属性中最后记得调用recycle
Recycles the TypedArray, to be re-used by a later caller. After calling
this function you must not ever touch the typed array again.
二 Paint的基本操作
1 根据获取的属性值或者class获取的默认值对Paint进行设置
backCirclePaint = new Paint(); backCirclePaint.setColor(backCircleColor); backCirclePaint.setAntiAlias(true); backCirclePaint.setStrokeWidth(backCircleWidth); backCirclePaint.setStyle(Paint.Style.STROKE); outerCirclePaint = new Paint(); outerCirclePaint.setColor(outerCircleColor); outerCirclePaint.setAntiAlias(true); outerCirclePaint.setStrokeWidth(outerCircleWidth); outerCirclePaint.setStyle(Paint.Style.STROKE); outerCirclePaint.setStrokeCap(Paint.Cap.ROUND); mTitlePaint = new Paint(); mTitlePaint.setTextSize(titleTextSize); mTitlePaint.setColor(titleTextColor);
2 在public void draw(Canvas canvas) 中完成操作绘制操作
无论是原的绘制还是文本的绘制首先我们要计算他们的位置,而要计算这些我们需要调用系统提供的测绘方法获取宽高并保存下来留待后续使用
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); height = MeasureSpec.getSize(heightMeasureSpec); }
2.1 底层圆的绘制
底层的圆作为衬托的背景,为了烘托出后续我们的进度弧形,因此它是一个整圆
// 画圆 float backCircleR = (Math.min(width, height) - 2 * mPadding) / 2; canvas.drawCircle(width / 2, height / 2, backCircleR, backCirclePaint);
思路很简单我们去除padding之后算出圆心和半径直接画圆即可。
此处唯一需要注意的细节是半径的处理要取宽高的最小值
2.2 进度圆的绘制
//画进度圆弧 RectF rectF = new RectF(mPadding, mPadding, width - mPadding, height - mPadding); //顺时针走,从水平最左侧开始也就是3点钟方位为0起点 canvas.drawArc(rectF, -90, 360 * currentPercent, false, outerCirclePaint);
其实这里已经不能完全称之为圆,因为我们是画弧线而圆是进度达到100%的一种表现形式而已。这里其实是有一个大坑的—-在我们数学中是按照象限沿着逆时针来走的而此处却跟我们的常识有出入:从我们钟表上的3点位置顺时针作为起点开始
2.3 指示文本的绘制
文本绘制这个逻辑其实也算是比较简单的,我们要根据文本的长度和字体来算出它所占用的空间大小最后在View的width和height的限定下房到何时位置即可
Rect titleRect = new Rect(); mTitlePaint.getTextBounds(title, 0, title.length(), titleRect); canvas.drawText(title, width / 2 - titleRect.width() / 2, height / 4 + titleRect.height() / 2, mTitlePaint); DecimalFormat df = new DecimalFormat("#%");//乘以100后以百分比形式输出,此处输出"12%" String percentMsg = df.format(currentPercent); // percentMsg = String.valueOf(currentPercent * 100) + "%"; mTitlePaint.getTextBounds(percentMsg, 0, percentMsg.length(), titleRect); canvas.drawText(percentMsg, width / 2 - titleRect.width() / 2, height / 2 + titleRect.height() / 2, mTitlePaint);
这里我们对于小数的计算遇到了点麻烦

此时设置的进度为0.3
<com.example.androidgo.CirCleView android:background="@color/RGB_E6E9ED" android:layout_width="200dp" app:mBackCircleColor="@color/colorAccent" app:mBackCircleWidth="@dimen/px_to_dip_12" app:mOuterCircleColor="@color/RGB_FF8209" app:mOuterCircleColorWitdth="10dp" app:mPadding="5dp" app:mPercent="0.3" app:mTitleTextColor="@color/colorPrimaryDark" app:mTitleTextSize="20sp" app:mTitle="当前进度" android:layout_height="200dp" />
大家可以把进行绘制处的percentMsg的转化注释打开看看效果,也可以看看
DecimalFormat的使用

长按二维吗查看源代码
