写在前面的话,希望通过写博客的方式记录代码成长,督促自己进步。加油!因为再简单的事,不去实际实践,不去记录,都不是存在于你的脑子里的。
听说自定义控件要撸的很熟,所以趁着空闲时间我也来撸一把算了。
1.开门见山,先看看要实现的控件的模样。
2.上自定义控件MyView代码:
1)需要继承view,给出三个构造函数,在构造函数中做一些初始化的工作。
public MyView(Context context) { this(context, null); } public MyView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); initAttrs(context, attrs); // 初始化点击事件 initListener(); } /** * 初始化属性 */ private void initAttrs(Context context, AttributeSet attrs) { TypedArray a = null; try { a = context.obtainStyledAttributes(attrs, R.styleable.MyProcessView); roundColor = a.getColor(R.styleable.MyProcessView_roundColor, getResources().getColor(android.R.color.darker_gray)); roundProgressColor = a.getColor(R.styleable.MyProcessView_roundProgressColor, getResources().getColor(android.R.color.holo_orange_dark)); textColor = a.getColor(R.styleable.MyProcessView_textColor, getResources().getColor(android.R.color.holo_blue_dark)); textSize = a.getDimension(R.styleable.MyProcessView_textSize, 30f); } finally { a.recycle(); } } /** * 初始化点击事件 */ private void initListener() { setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { restartAnimate(); } }); }
动画相关方法:
/** * 重新开启动画 */ private void restartAnimate() { if (mLastProgress > 0) { // 取消动画 cancelAnimate(); // 重置进度 setProgress(0f); // 重新开启动画 runAnimate(mLastProgress); } } /** * 设置当前显示的进度条 * * @param progress */ public void setProgress(float progress) { this.progress = progress; // 使用 postInvalidate 比 postInvalidat() 好,线程安全 postInvalidate(); } /** * 开始执行动画 * * @param targetProgress 最终到达的进度 */ public void runAnimate(float targetProgress) { // 运行之前,先取消上一次动画 cancelAnimate(); mLastProgress = targetProgress; mAnimator = ValueAnimator.ofObject(new FloatEvaluator(), 0, targetProgress); // 设置差值器 mAnimator.setInterpolator(new AccelerateDecelerateInterpolator()); mAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float value = (float) animation.getAnimatedValue(); setProgress(value); } }); mAnimator.setDuration((long) (targetProgress * 33)); mAnimator.start(); } /** * 取消动画 */ public void cancelAnimate() { if (mAnimator != null && mAnimator.isRunning()) { mAnimator.cancel(); } }
2).将自定义控件的属性写在attrs.xml中。
<?xml version="1.0" encoding="utf-8"?> <resources> <declare-styleable name="MyProcessView"> <attr name="roundColor" format="color"></attr> <attr name="roundProgressColor" format="color"></attr> <attr name="textColor" format="color"></attr> <attr name="textSize" format="dimension"></attr> </declare-styleable> </resources>3).重写自定义控件的方法,onLayout(),决定view在viewGroup中的位置。
@Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); mWidth = getMeasuredWidth(); mHeight = getMeasuredHeight(); initValue(); setCirclePaint(); setTextPaint(); } private void initValue() { mStrokeWidth = mWidth / 15f; } /** * 设置圆环画笔 */ private void setCirclePaint() { // 创建圆环画笔 mCirclePaint = new Paint(); mCirclePaint.setAntiAlias(true); mCirclePaint.setColor(roundColor); mCirclePaint.setStyle(Paint.Style.STROKE); // 边框风格 mCirclePaint.setStrokeWidth(mStrokeWidth); } /** * 设置文字画笔 */ private void setTextPaint() { mTextPaint = new Paint(); mTextPaint.setAntiAlias(true); mTextPaint.setColor(textColor); mTextPaint.setTextSize(textSize); }
4).重写自定义控件的方法,onDraw(),如何绘制这个view。
@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // 第一步:绘制一个圆环 mCirclePaint.setStrokeWidth(mStrokeWidth); mCirclePaint.setColor(roundColor); float circleX = mWidth / 2.0f; float circleY = mHeight / 2.0f; float circleRadius = mWidth / 2.0f - mStrokeWidth / 2.0f; canvas.drawCircle(circleX, circleY, circleRadius, mCirclePaint); // 第二步:绘制文字 String text = ((int) (progress / maxProgress * 100)) + "%"; Rect bounds = new Rect(); mTextPaint.getTextBounds(text, 0, text.length(), bounds); canvas.drawText(text, mWidth / 2 - bounds.width() / 2, mHeight / 2 + bounds.height() / 2, mTextPaint); // 第三步:绘制动态进度圆环 mCirclePaint.setDither(true); mCirclePaint.setStrokeJoin(Paint.Join.BEVEL); mCirclePaint.setStrokeCap(Paint.Cap.ROUND); // 设置笔触为圆形 mCirclePaint.setStrokeWidth(mStrokeWidth); mCirclePaint.setColor(roundProgressColor); //精度不一样。Rect是使用int类型作为数值,RectF是使用float类型作为数值 RectF oval = new RectF(0 + mStrokeWidth / 2, 0 + mStrokeWidth / 2, mWidth - mStrokeWidth / 2, mHeight - mStrokeWidth / 2); canvas.drawArc(oval, 0, progress / maxProgress * 360, false, mCirclePaint); }3.实际使用这个自定义控件
public class MainActivity extends AppCompatActivity{ private MyRoundProcess mProcessView; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initViews(); } private void initViews() { mProcessView = (MyRoundProcess) findViewById(R.id.my_round_process); mProcessView.runAnimate(90); } @Override protected void onDestroy() { super.onDestroy(); if (mProcessView != null){ mProcessView.cancelAnimate(); } } }