前言
Android开发中,常常自定义View实现自己想要的效果,当然自定义View也是Android开发中比较难的部分,涉及到的知识有Canvas(画布),Paint(画笔)等,自定义控件分为三种:一是直接继承自View,完全的自定义;二是在原有控件的基础上进行改造,达到自己想要的效果;还有一种就是自定义组合控件,将已有的控件根据自己的需要进行组合实现的效果。本人对自定义View也是一知半解,简单记录下自己学习自定义View(继承自View)的过程,方便日后翻阅。
使用技术
1、继承View
2、Canvas
3、paint
效果图:
1.分析组件
自定义view首先我们要分析组件是由几部分组成,然后在依次顺序使用canvas画出组件,首先可以看出该组件由一个背景外部圆,一个圆弧,以及圆弧端点是由两个圆组成,内部是三个文字。分析完毕,我们就可以先定义组件属性了
2.组件属性
1.在values目录下新建attrs.xml文件,用来编写组件属性
<&#63;xml version="1.0" encoding="utf-8"&#63;>
2.自定义view继承View并实现构造方法
public class ProgressView extends View { /** * 在java代码里new的时候会用到 * @param context */ public ProgressView(Context context) { super(context); init(context, null); } /** * 在xml布局文件中使用时自动调用 * @param context */ public ProgressView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); init(context,attrs); } /** * 不会自动调用,如果有默认style时,在第二个构造函数中调用 * @param context * @param attrs * @param defStyleAttr */ public ProgressView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } }
2.初始化属性
/** * 初始化属性 * @param context * @param attrs */ private void init(Context context,AttributeSet attrs){ this.mCOntext= context; if(attrs!=null){ TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.ProgressView); title = array.getString(R.styleable.ProgressView_title); num = array.getString(R.styleable.ProgressView_num); unit = array.getString(R.styleable.ProgressView_unit); titleTextsize = array.getDimension(R.styleable.ProgressView_titleTextsize,24); numTextsize = array.getDimension(R.styleable.ProgressView_numTextsize,48); unitTextsize = array.getDimension(R.styleable.ProgressView_unitTextsize,24); titleTextColor = array.getColor(R.styleable.ProgressView_titleTextColor, Color.parseColor("#656d78")); numTextColor = array.getColor(R.styleable.ProgressView_numTextColor, Color.parseColor("#4fc1e9")); unitTextColor = array.getColor(R.styleable.ProgressView_unitTextColor, Color.parseColor("#4fc1e9")); backCircleWidth = array.getDimension(R.styleable.ProgressView_backCircleWidth, 12); outerCircleWidth = array.getDimension(R.styleable.ProgressView_outerCircleWidth, 20); backCircleColor = array.getColor(R.styleable.ProgressView_backCircleColor, Color.parseColor("#e6e9ed")); outerCircleColor = array.getColor(R.styleable.ProgressView_outerCircleColor, Color.parseColor("#4fc1e9")); endCircleWidth = array.getDimension(R.styleable.ProgressView_endCircleWidth,24); endCircleColor = array.getColor(R.styleable.ProgressView_endCircleColor, Color.parseColor("#4fc1e9")); edgeDistance = array.getDimension(R.styleable.ProgressView_edgeDistance, 12); currentPercent = array.getFloat(R.styleable.ProgressView_currentPercent, 0); if(currentPercent>1||currentPercent<0){ currentPercent = currentPercent>1&#63;1:0; } //初始化画笔 backCirclePaint = new Paint(); backCirclePaint.setAntiAlias(true); backCirclePaint.setStrokeWidth(backCircleWidth); backCirclePaint.setColor(backCircleColor); backCirclePaint.setStyle(Paint.Style.STROKE); outerCirclePaint = new Paint(); outerCirclePaint.setAntiAlias(true); outerCirclePaint.setStrokeWidth(outerCircleWidth); outerCirclePaint.setColor(outerCircleColor); outerCirclePaint.setStyle(Paint.Style.STROKE); endBigCirclePaint = new Paint(); endBigCirclePaint.setAntiAlias(true); endBigCirclePaint.setStrokeWidth(endCircleWidth); endBigCirclePaint.setColor(endCircleColor); endBigCirclePaint.setStyle(Paint.Style.STROKE); endSmallCirclePaint = new Paint(); endSmallCirclePaint.setAntiAlias(true); endSmallCirclePaint.setColor(Color.WHITE); endSmallCirclePaint.setStyle(Paint.Style.FILL); titlePaint = new Paint(); //通过设置Flag来应用抗锯齿效果 titlePaint.setFlags(Paint.ANTI_ALIAS_FLAG); titlePaint.setAntiAlias(true); //设置文字居中 //titlePaint.setTextAlign(Paint.Align.CENTER); titlePaint.setColor(titleTextColor); titlePaint.setTextSize(titleTextsize); numPaint = new Paint(); numPaint.setAntiAlias(true); numPaint.setFlags(Paint.ANTI_ALIAS_FLAG); //设置文字居中 //numPaint.setTextAlign(Paint.Align.CENTER); numPaint.setColor(numTextColor); numPaint.setTextSize(numTextsize); unitPaint = new Paint(); unitPaint.setAntiAlias(true); unitPaint.setFlags(Paint.ANTI_ALIAS_FLAG); //unitPaint.setTextAlign(Paint.Align.CENTER); unitPaint.setColor(unitTextColor); unitPaint.setTextSize(unitTextsize); //释放 array.recycle(); } }
3.获取组件高度宽度,重写onMeasure方法
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); ///获取总宽度,是包含padding值 //处理WAP_CONTENT int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec); width = MeasureSpec.getSize(widthMeasureSpec); int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec); height = MeasureSpec.getSize(heightMeasureSpec); if (widthSpecMode == MeasureSpec.AT_MOST && heightSpecMode == MeasureSpec.AT_MOST) { //默认大小 200*200 setMeasuredDimension(200,200); }else if (widthSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(height, height); }else if (heightSpecMode == MeasureSpec.AT_MOST) { setMeasuredDimension(width, width); } }
4.重写onDraw()绘制组件各部分
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //圆心 int centerX = width / 2; int centerY = height / 2; //计算半径 float radius = centerX - edgeDistance; //画背景圆 drawBackCircle(canvas,centerX,centerY,radius); //绘制圆弧进度 drawProgress(canvas,centerX,centerY); //绘制标题 drawText(canvas); }```
###### 4.1绘制背景圆
```java /** * 绘制背景圆 * @param canvas * @param x 圆心位置x * @param y 圆心位置y * @param radius 半径 */ private void drawBackCircle(Canvas canvas,int x,int y,float radius){ canvas.drawCircle(x,y,radius,backCirclePaint); }
4.2 绘制圆弧进度
1.注意:圆弧上端点进度为0或者100不显示,此外端点的位置使用sin和cos来确定坐标;
/** * 绘制圆弧进度 */ private void drawProgress(Canvas canvas,int x,int y){ //圆弧的范围 RectF rectF = new RectF(edgeDistance, edgeDistance, width - edgeDistance, height - edgeDistance); //定义的圆弧的形状和大小的范围 // 置圆弧是从哪个角度来顺时针绘画的 //设置圆弧扫过的角度 //设置我们的圆弧在绘画的时候,是否经过圆形 这里不需要 //画笔 canvas.drawArc(rectF, -90, 360 * currentPercent, false, outerCirclePaint); //绘制端圆 //进度在0~100%的时候才会画终点小圆,可以自由改动 if(currentPercent>0&¤tPercent<1){ //绘制外层大圆 canvas.drawCircle(x + rectF.width() / 2 * (float) Math.sin(360 * currentPercent * Math.PI / 180), y - rectF.width() / 2 * (float) Math.cos(360 * currentPercent * Math.PI / 180), endCircleWidth / 2, endBigCirclePaint); //绘制内层圆点 canvas.drawCircle(x + rectF.width() / 2 * (float) Math.sin(360 * currentPercent * Math.PI / 180), y - rectF.width() / 2 * (float) Math.cos(360 * currentPercent * Math.PI / 180), endCircleWidth / 4, endSmallCirclePaint); } }
4.3 绘制文字
/** * 绘制标题 * @param canvas */ private void drawText(Canvas canvas) { Rect textRect = new Rect(); //返回的则是当前文本所需要的最小宽度,也就是整个文本外切矩形的宽度 titlePaint.getTextBounds(title, 0, title.length(), textRect);//25 50 175 //高度平分四部分 float h = height/ 4; //文字居中 canvas.drawText(title, width / 2 - textRect.width() / 2, h + textRect.height() / 2, titlePaint); numPaint.getTextBounds(num, 0, num.length(), textRect); canvas.drawText(num, width / 2 - textRect.width() / 2, h*2 + textRect.height() / 2, numPaint); unitPaint.getTextBounds(unit, 0, unit.length(), textRect); canvas.drawText(unit, width / 2 - textRect.width() / 2, 3*h + textRect.height() / 2, unitPaint); }
4.4提供外部修改进度方法以及进度过度
/** * 设置进度 */ public void setProgress(final float progress){ new Thread(new Runnable() { @Override public void run() { for(int i=0;i<=progress*100;i++){ Message msg = new Message(); msg.what = 1; msg.obj = i; try { Thread.sleep(20); handler.sendMessage(msg); } catch (InterruptedException e) { e.printStackTrace(); } } } }).start(); } private Handler handler = new Handler(new Handler.Callback(){ @Override public boolean handleMessage(@NonNull Message msg) { if(msg.what==1){ currentPercent = ((float)Integer.valueOf(msg.obj+"")/100); System.out.println("更新"+currentPercent); invalidate(); } return false; } });
总结:使用Canvas的drawArc方法绘制圆弧及drawText绘制文本信息等;
github地址:项目地址
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。