自定义View的一个练习(中间带百分比显示的圆环形ProgressBar)

自定义View实战(实现带百分比文字的圆环形Progress Bar) 效果
首先,贼布局文件中使用:


效果是这样的:
自定义View的一个练习(中间带百分比显示的圆环形ProgressBar)
文章图片

实现过程
继承View类 要自定义一个View,首先肯定需要自己新建一个类,继承系统的View类。这里我新建了RoundProgressBar类,继承View。
自定义属性名称和类型 然后,定义需要用到的自定义属性。在res/values目录下面,新建attrs.xml,然后在里面定义自己需要用到的属性。当然,也可以沿用系统本来就有的属性,例如,文字大小,“android:textSize”。attrs.xml文件内容如下:

重写自定义View类的回调方法,根据自己需要实现 大概的思路是这样子:
自定义View的一个练习(中间带百分比显示的圆环形ProgressBar)
文章图片

其中有一些可以优化的地方,例如,在onDraw方法中尽量不要去new一些对象,如果这些对象是会被复用的,尽可能的提取出来再初始化的时候,或者在onSizeChange方法中进行初始化。这样就可以提高这个自定义控件的性能。
RoundProgressBar.java的代码如下:
package com.zhenjie.customview; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.os.Bundle; import android.os.Parcelable; import android.support.annotation.Nullable; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; public class RoundProgressBar extends View {private int mRadius, mColor, mLineWidth, mTextSize, mProgress; private Paint mPaint; private RectF arcRectF; private Rect bound = new Rect(); private void initPaint() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setColor(mColor); mPaint.setTextSize(mTextSize); }public RoundProgressBar(Context context, @Nullable AttributeSet attrs) { super(context, attrs); TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.RoundProgressBar); mRadius = (int) ta.getDimension(R.styleable.RoundProgressBar_radius, dp2px(30)); mColor = ta.getColor(R.styleable.RoundProgressBar_textColor, 0xffff0000); mLineWidth = (int) ta.getDimension(R.styleable.RoundProgressBar_line_width, dp2px(3)); mTextSize = (int) ta.getDimension(R.styleable.RoundProgressBar_android_textSize, dp2px(16)); mProgress = ta.getInt(R.styleable.RoundProgressBar_android_progress, 0); ta.recycle(); initPaint(); }private float dp2px(int dpVal) { return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpVal, getResources().getDisplayMetrics()); }@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int widthMode = MeasureSpec.getMode(widthMeasureSpec); int widthSize = MeasureSpec.getSize(widthMeasureSpec); int width = 0; if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { int needWidth = measureWidth() + getPaddingLeft() + getPaddingRight(); if (widthMode == MeasureSpec.AT_MOST) { width = Math.min(needWidth, widthSize); } else { width = needWidth; } } int height = 0; int heightMode = MeasureSpec.getMode(heightMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { int needHeight = measureHeight() + getPaddingTop() + getPaddingBottom(); if (heightMode == MeasureSpec.AT_MOST) { height = Math.min(needHeight, heightSize); } else { height = needHeight; } } width = Math.min(width,height); setMeasuredDimension(width,width); }private int measureHeight() { return mRadius*2; }private int measureWidth() { return mRadius*2; }@Override protected void onDraw(Canvas canvas) { mPaint.setStyle(Paint.Style.STROKE); mPaint.setStrokeWidth(mLineWidth * 1.0f / 4); int width = getWidth(); int height = getHeight(); canvas.drawCircle(width / 2, height / 2, width / 2 - getPaddingLeft() - mPaint.getStrokeWidth() / 2, mPaint); mPaint.setStrokeWidth(mLineWidth); canvas.save(); canvas.translate(getPaddingLeft(), getPaddingTop()); float angle = mProgress * 1.0f / 100 * 360; //外接矩形 canvas.drawArc(arcRectF, 270, angle, false, mPaint); canvas.restore(); String text = mProgress + "%"; mPaint.setStrokeWidth(0); mPaint.setTextAlign(Paint.Align.CENTER); mPaint.getTextBounds(text,0,text.length(),bound); int y = getHeight()/2; int textHeight = bound.height(); canvas.drawText(text,0,text.length(),getWidth()/2, y+textHeight/2,mPaint); }public static final String INSTANCE = "instance"; public static final String KEY_PROGRESS = "key_progress"; @Nullable @Override protected Parcelable onSaveInstanceState() { Bundle bundle = new Bundle(); bundle.putInt(KEY_PROGRESS, mProgress); bundle.putParcelable(INSTANCE, super.onSaveInstanceState()); return bundle; }@Override protected void onRestoreInstanceState(Parcelable state) { if (state instanceof Bundle) { Bundle bundle = (Bundle) state; Parcelable parcelable = bundle.getParcelable(INSTANCE); super.onRestoreInstanceState(parcelable); mProgress = bundle.getInt(KEY_PROGRESS); return; } }public void setProgress(int progress){ mProgress = progress; invalidate(); }public int getProgress(){ return mProgress; }@Override protected void onSizeChanged(int w, int h, int oldw, int oldh) { arcRectF = new RectF(0,0,getWidth()-getPaddingLeft()*2,getHeight()-getPaddingTop()*2); super.onSizeChanged(w, h, oldw, oldh); } }

使用示例 【自定义View的一个练习(中间带百分比显示的圆环形ProgressBar)】布局文件就是文章一开始的那个,MainActivity的代码如下:
import android.animation.ObjectAnimator; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; public class MainActivity extends AppCompatActivity {private RoundProgressBar progressBar; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); progressBar = findViewById(R.id.progress_bar); progressBar.setOnClickListener((v) -> { ObjectAnimator.ofInt(progressBar,"progress",0,100).setDuration(3000).start(); }); } }

    推荐阅读