自定义View实战(实现带百分比文字的圆环形Progress Bar)
效果
首先,贼布局文件中使用:
效果是这样的:
文章图片
实现过程
继承View类 要自定义一个View,首先肯定需要自己新建一个类,继承系统的View类。这里我新建了RoundProgressBar类,继承View。
自定义属性名称和类型 然后,定义需要用到的自定义属性。在res/values目录下面,新建attrs.xml,然后在里面定义自己需要用到的属性。当然,也可以沿用系统本来就有的属性,例如,文字大小,“android:textSize”。attrs.xml文件内容如下:
重写自定义View类的回调方法,根据自己需要实现 大概的思路是这样子:
文章图片
其中有一些可以优化的地方,例如,在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();
});
}
}