Android自定义view流程,次要目标是总结实现过程中的思路以及一些须要留神的中央。
首先,咱们先来看一张效果图:
实现逻辑
- 从新指定View宽高
- 绘制外圆圆弧背景及进度
- 绘制中圆圆弧背景及进度
- 绘制内圆圆弧背景及进度
知识点
onMeasure
- 用于测量View的大小。创立时View无需测量,当将这个View放入一个容器(父控件)时候才须要测量,而测量方法由父控件调用。当控件的父控件要搁置该控件的时候,父控件会调用子控件的onMeasure办法确定子控件须要的空间大小,而后传入widthMeasureSpec和heightMeasureSpec来通知子控件可取得的空间大小,子控件通过这两个参数就能够测量本身的宽高了。
setMeasuredDimension
- 用于从新设置View宽高
Canvas#drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)
- 绘制以oval为边界的圆弧
onDraw
用来确定View长什么样。onDraw绘制过程如下:
- Draw the background(绘制背景)
- If necessary, save the canvas’ layers to prepare for fading(如果须要,为保留这层为边缘的滑动成果作筹备)
- Draw view’s content(绘制内容)
- Draw children(绘制子View)
- If necessary, draw the fading edges and restore layers(如果须要,绘制边缘成果并且保留图层)
- Draw decorations (scrollbars for instance)(绘制边框,比方scrollbars,TextView)
次要代码
@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 依据父控件传递的widthMeasureSpec和heightMeasureSpec调用MeasureSpec.getSize测量本身宽高 int measureWidth = MeasureSpec.getSize(widthMeasureSpec); int measureHeight = MeasureSpec.getSize(heightMeasureSpec); int finalWidth = measureWidth; int finalHeight = measureHeight; // 依据本身宽高从新计算新的宽高,使新的宽高比为2:1 if (measureWidth >= measureHeight * 2) { finalWidth = measureHeight * 2; } else { finalHeight = measureWidth / 2; } // 设置View新的宽高 setMeasuredDimension(finalWidth, finalHeight);}
/** * 绘制圆弧 * @param canvas * @param progress 进度 * @param color 进度色彩 * @param radius 圆弧半径 */private void drawArc(Canvas canvas, float progress, int color, float radius){ // 圆心 mXCenter = getWidth() / 2; mYCenter = getHeight() ; mPaint.setColor(mBackgroundArcColor); // 结构边界矩形 RectF oval = new RectF(); oval.left = (mXCenter - radius); oval.top = (mYCenter - radius); oval.right = mXCenter + radius; oval.bottom = radius * 2 + (mYCenter - radius); //绘制圆弧背景 canvas.drawArc(oval, -180, 180, false, mPaint); //绘制圆弧进度 float showDegree = progress / 100 * 180; mPaint.setColor(color); canvas.drawArc(oval, -180, showDegree, false, mPaint);}
@Overrideprotected void onDraw(Canvas canvas) { super.onDraw(canvas); // 初始半径 float originalRadius = getWidth() * .5f; // 画笔半宽 float halfArcStokeWidth = mArcStrokeWidth * .5f; // 外圆环半径=初始半径-画笔半宽 float outSideArcRadius = originalRadius - halfArcStokeWidth; drawArc(canvas, mOutsideProgress, mOutsideArcColor, outSideArcRadius); // 中圆环半径=外圆的半径-圆环偏移值-画笔半宽 float middleArcRadius = outSideArcRadius - mArcOffset - halfArcStokeWidth; drawArc(canvas, mMiddleProgress, mMiddleArcColor, middleArcRadius); // 内圆环半径=中圆的半径-圆环偏移值-画笔半宽 float insideArcRadius = middleArcRadius - mArcOffset - halfArcStokeWidth; drawArc(canvas, mInsideProgress, mInsideArcColor, insideArcRadius);}
全副代码
ThreeArcView.java
public class ThreeArcView extends View { //圆弧画笔 private Paint mPaint; //背景圆环色彩 private int mBackgroundArcColor; //外圆环色彩 private int mOutsideArcColor; //中圆环色彩 private int mMiddleArcColor; //内圆环色彩 private int mInsideArcColor; //外圆展现弧度 private float mOutsideProgress; //中圆展现弧度 private float mMiddleProgress; //内圆展现弧度 private float mInsideProgress; //圆弧宽度 private float mArcStrokeWidth; //圆偏移值 private float mArcOffset; // 圆心x坐标 private int mXCenter; // 圆心y坐标 private int mYCenter; public ThreeArcView(Context context) { this(context, null); } public ThreeArcView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public ThreeArcView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(context, attrs); initVariable(); } private void initAttrs(Context context, AttributeSet attrs) { TypedArray typeArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ThreeArcView, 0, 0); mArcStrokeWidth = typeArray.getDimension(R.styleable.ThreeArcView_ts_strokeWidth, dp2px(context, 20)); // 圆环背景色彩 mBackgroundArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_bgArcColor, 0xFFFFFFFF); // 圆环色彩 mOutsideArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_outsideBgColor, 0xFFFFFFFF); mMiddleArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_middleBgColor, 0xFFFFFFFF); mInsideArcColor = typeArray.getColor(R.styleable.ThreeArcView_ts_insideBgColor, 0xFFFFFFFF); // 圆进度 mOutsideProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_outsideProgress, 0f); mMiddleProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_middleProgress, 0f); mInsideProgress = typeArray.getFloat(R.styleable.ThreeArcView_ts_insideProgress, 0f); // 圆环偏移值 mArcOffset = typeArray.getDimension(R.styleable.ThreeArcView_ts_radiusOffset, dp2px(context, 20)); typeArray.recycle(); // 偏移值不能小于画笔宽度的一半,否则会产生笼罩 if (mArcOffset < mArcStrokeWidth / 2){ mArcOffset = mArcStrokeWidth / 2; } } private void initVariable() { //背景圆弧画笔设置 mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(mArcStrokeWidth); mPaint.setStrokeCap(Paint.Cap.ROUND);//开启显示边缘为圆形 } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { // 别离获取冀望的宽度和高度,并取其中较小的尺寸作为该控件的宽和高 int measureWidth = MeasureSpec.getSize(widthMeasureSpec); int measureHeight = MeasureSpec.getSize(heightMeasureSpec); //裁剪出一个 (宽:高) = (2:1) 的矩形 int finalWidth = measureWidth; int finalHeight = measureHeight; if (measureWidth >= measureHeight * 2) { finalWidth = measureHeight * 2; } else { finalHeight = measureWidth / 2; } setMeasuredDimension(finalWidth, finalHeight); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 初始半径 float originalRadius = getWidth() * .5f; // 画笔半宽 float halfArcStokeWidth = mArcStrokeWidth * .5f; // 外圆环半径=初始半径-画笔半宽 float outSideArcRadius = originalRadius - halfArcStokeWidth; drawArc(canvas, mOutsideProgress, mOutsideArcColor, outSideArcRadius); // 中圆环半径=外圆的半径-圆环偏移值-画笔半宽 float middleArcRadius = outSideArcRadius - mArcOffset - halfArcStokeWidth; drawArc(canvas, mMiddleProgress, mMiddleArcColor, middleArcRadius); // 内圆环半径=中圆的半径-圆环偏移值-画笔半宽 float insideArcRadius = middleArcRadius - mArcOffset - halfArcStokeWidth; drawArc(canvas, mInsideProgress, mInsideArcColor, insideArcRadius); } /** * 绘制圆弧 * @param canvas * @param progress 进度 * @param color 进度色彩 * @param radius 圆弧半径 */ private void drawArc(Canvas canvas, float progress, int color, float radius){ // 圆心 mXCenter = getWidth() / 2; mYCenter = getHeight() ; mPaint.setColor(mBackgroundArcColor); // 结构边界矩形 RectF oval = new RectF(); oval.left = (mXCenter - radius); oval.top = (mYCenter - radius); oval.right = mXCenter + radius; oval.bottom = radius * 2 + (mYCenter - radius); //绘制圆弧背景 canvas.drawArc(oval, -180, 180, false, mPaint); //绘制圆弧进度 float showDegree = progress / 100 * 180; mPaint.setColor(color); canvas.drawArc(oval, -180, showDegree, false, mPaint); } private void setOutSideProgress(float progress){ this.mOutsideProgress = progress; postInvalidate(); } private void setMiddleProgress(float progress){ this.mMiddleProgress = progress; postInvalidate(); } private void setInsideProgress(float progress){ this.mInsideProgress = progress; postInvalidate(); } public void setProgress(float outSideProgress, float middleProgress, float insideProgress) { mOutsideProgress = outSideProgress; mMiddleProgress = middleProgress; mInsideProgress = insideProgress; postInvalidate(); } public int dp2px(Context context, float dipValue) { final float scale = context.getResources().getDisplayMetrics().density; return (int) (dipValue * scale + 0.5f); } public int sp2px(Context context, float spValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (spValue * fontScale + 0.5f); } public int px2sp(Context context, float pxValue) { final float fontScale = context.getResources().getDisplayMetrics().scaledDensity; return (int) (pxValue / fontScale + 0.5f); }}
styes.xml
<declare-styleable name="ThreeArcView"> <!-- 画笔宽度 --> <attr name="ts_strokeWidth" format="dimension" /> <!-- 圆弧背景色 --> <attr name="ts_bgArcColor" format="color" /> <!-- 外圆进度色彩 --> <attr name="ts_outsideBgColor" format="color" /> <!-- 中圆进度色彩 --> <attr name="ts_middleBgColor" format="color" /> <!-- 内圆进度色彩 --> <attr name="ts_insideBgColor" format="color" /> <!-- 外圆进度 --> <attr name="ts_outsideProgress" format="float" /> <!-- 中圆进度 --> <attr name="ts_middleProgress" format="float" /> <!-- 内圆进度 --> <attr name="ts_insideProgress" format="float" /> <!-- 圆偏移值 --> <attr name="ts_radiusOffset" format="dimension" /></declare-styleable>
OK,本文到此结束,若发现问题,欢送一起留言一起探讨,感激~
相干教程
Android根底系列教程:
Android根底课程U-小结_哔哩哔哩_bilibili
Android根底课程UI-布局_哔哩哔哩_bilibili
Android根底课程UI-控件_哔哩哔哩_bilibili
Android根底课程UI-动画_哔哩哔哩_bilibili
Android根底课程-activity的应用_哔哩哔哩_bilibili
Android根底课程-Fragment应用办法_哔哩哔哩_bilibili
Android根底课程-热修复/热更新技术原理_哔哩哔哩_bilibili
本文转自 https://juejin.cn/post/6847902216959819790,如有侵权,请分割删除。