自定义视图的实现方式大概有三种,组合控件,继承控件和自绘控件
组合控件:使用时只用系统原生的各个控件就行了,例如实际开发中经常会自己定义一个titlebar,需要一个返回键和title,这样的就是组合控件
继承控件:继承系统的原生控件,在他的基础上再添加一些新功能,例如登陆时的验证码60秒倒计时,我们就可以去自定义一个button去实现这个功能
自绘控件:页面上展示的东西都是通过代码自己去绘制出来的
这次带来的是通过自定义view去实现下载时圆环进度以及进度数的一个progressView
其余的倒不用管,想要绘制视图必须在的onDraw();方法中。
先看下最终实现的效果
我们先去继承一个视图,然后实现它的构造方法
public class ProgressBarView extends View {
public ProgressBarView(Context context) {
super(context);
}
public ProgressBarView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public ProgressBarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
然后再值目录下创建一个attrs.xml文件
<declare-styleable name="CirclesProgressBar">
<attr name="circlesWidth" format="dimension" />
<attr name="circlesColor" format="color" />
<attr name="textCrude" format="dimension" />
<attr name="textColor" format="color" />
<attr name="textSize" format="dimension" />
<attr name="currentProgressColor" format="color" />
<attr name="currentProgress" format="integer" />
<attr name="isPercent" format="boolean" />
<attr name="style" format="integer" />
<attr name="currentScheduleWidth" format="dimension" />
</declare-styleable>
这个是用来初始化一些变量的,例如进度初始化的颜色,字体颜色,字体大小之类的,而且可以直接在xml中引用
//接下来在视图中写一些必须要的变量以及两种不同风格的风格
//空心
public static final int STROKE = 1;
//实心
public static final int FILL = 0;
private Paint mPaint;
private int style;
//圆环的宽度
private float circlesWidth;
//圆环的颜色
private int circlesColor;
//进度字体的粗细程度
private float textCrude;
//字体颜色
private int textColor;
//字体大小
private float textSize;
//设置字体
private Typeface mTypeface;
//当前进度
private int currentProgress;
//进度颜色
private int currentProgressColor;
//进度圆环的宽度
private float currentScheduleWidth;
//是否显示百分比
private boolean isPercent;
接着去初始化这些变量,指定一些数据,例如字体的大小圆环的颜色之类的
private void init(Context context, AttributeSet attrs) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CirclesProgressBar);
//初始化圆环变量
circlesWidth = mTypedArray.getDimension(R.styleable.CirclesProgressBar_circlesWidth, ProgressDefaults.CIRCLES_WIDTH);
circlesColor = mTypedArray.getColor(R.styleable.CirclesProgressBar_circlesColor, ProgressDefaults.CIRCLES_COLOR);
textCrude = mTypedArray.getDimension(R.styleable.CirclesProgressBar_textCrude, ProgressDefaults.TEXT_CRUDE);
textColor = mTypedArray.getColor(R.styleable.CirclesProgressBar_textColor, ProgressDefaults.TEXT_COLOR);
textSize = mTypedArray.getDimension(R.styleable.CirclesProgressBar_textSize, ProgressDefaults.TEXT_SIZE);
currentProgress = mTypedArray.getInt(R.styleable.CirclesProgressBar_currentProgress, ProgressDefaults.CURRENT_PROGRESS);
currentProgressColor = mTypedArray.getColor(R.styleable.CirclesProgressBar_currentProgressColor, ProgressDefaults.CURRENT_PROGRESS_COLOR);
isPercent = mTypedArray.getBoolean(R.styleable.CirclesProgressBar_isPercent, ProgressDefaults.IS_PERCENT);
style = mTypedArray.getInt(R.styleable.CirclesProgressBar_style, ProgressDefaults.STYLE);
currentScheduleWidth = mTypedArray.getDimension(R.styleable.CirclesProgressBar_currentScheduleWidth, ProgressDefaults.CURRENT_SCHEDULE_WIDTH);
mTypedArray.recycle();
}
然后重写的onDraw();方法去绘制我们需要的圆环以及进度数据
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int anInt = getWidth() / 2;
int circlesRadius = (int) (anInt - circlesWidth / 2);//半径
//圆环
mPaint.setColor(circlesColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(circlesWidth);
canvas.drawCircle(anInt, anInt, circlesRadius, mPaint);
//百分比
if (isPercent && style == STROKE) {
mPaint.setStrokeWidth(textCrude);
mPaint.setColor(textColor);
mPaint.setTextSize(textSize);
mPaint.setTypeface(mTypeface);
int percent = (int) (((float) currentProgress / (float) ProgressDefaults.PROGRESS_BAR_MAX) * 100);
float textWidth = mPaint.measureText(percent + "%");
canvas.drawText(percent + "%", anInt - textWidth / 2, anInt + textSize / 2, mPaint);
}
//进度的圆环
mPaint.setColor(currentProgressColor);
mPaint.setStrokeWidth(currentScheduleWidth);
RectF rectF = new RectF(anInt - circlesRadius, anInt - circlesRadius, anInt + circlesRadius, anInt + circlesRadius);
//选择风格
switch (style) {
case STROKE:
if (currentProgress != 0) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(rectF, 0, 360 * currentProgress / ProgressDefaults.PROGRESS_BAR_MAX, false, mPaint);
}
break;
case FILL:
if (currentProgress != 0) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawArc(rectF, 0, 360 * currentProgress / ProgressDefaults.PROGRESS_BAR_MAX, true, mPaint);
}
break;
}
Log.i("onDraw---->", "onDraw current");
}
然后为了能在代码中方便的改变一些数据还需要一些set,get方法。
整体的代码如下:
public class ProgressBarView extends View {
//空心
public static final int STROKE = 1;
//实心
public static final int FILL = 0;
private Paint mPaint;
private int style;
//圆环的宽度
private float circlesWidth;
//圆环的颜色
private int circlesColor;
//进度字体的粗细程度
private float textCrude;
//字体颜色
private int textColor;
//字体大小
private float textSize;
//设置字体
private Typeface mTypeface;
//当前进度
private int currentProgress;
//进度颜色
private int currentProgressColor;
//进度圆环的宽度
private float currentScheduleWidth;
//是否显示百分比
private boolean isPercent;
public ProgressBarView(Context context) {
super(context);
}
public ProgressBarView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public ProgressBarView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
mPaint = new Paint();
mPaint.setAntiAlias(true);
TypedArray mTypedArray = context.obtainStyledAttributes(attrs, R.styleable.CirclesProgressBar);
//初始化圆环变量
circlesWidth = mTypedArray.getDimension(R.styleable.CirclesProgressBar_circlesWidth, ProgressDefaults.CIRCLES_WIDTH);
circlesColor = mTypedArray.getColor(R.styleable.CirclesProgressBar_circlesColor, ProgressDefaults.CIRCLES_COLOR);
textCrude = mTypedArray.getDimension(R.styleable.CirclesProgressBar_textCrude, ProgressDefaults.TEXT_CRUDE);
textColor = mTypedArray.getColor(R.styleable.CirclesProgressBar_textColor, ProgressDefaults.TEXT_COLOR);
textSize = mTypedArray.getDimension(R.styleable.CirclesProgressBar_textSize, ProgressDefaults.TEXT_SIZE);
currentProgress = mTypedArray.getInt(R.styleable.CirclesProgressBar_currentProgress, ProgressDefaults.CURRENT_PROGRESS);
currentProgressColor = mTypedArray.getColor(R.styleable.CirclesProgressBar_currentProgressColor, ProgressDefaults.CURRENT_PROGRESS_COLOR);
isPercent = mTypedArray.getBoolean(R.styleable.CirclesProgressBar_isPercent, ProgressDefaults.IS_PERCENT);
style = mTypedArray.getInt(R.styleable.CirclesProgressBar_style, ProgressDefaults.STYLE);
currentScheduleWidth = mTypedArray.getDimension(R.styleable.CirclesProgressBar_currentScheduleWidth, ProgressDefaults.CURRENT_SCHEDULE_WIDTH);
mTypedArray.recycle();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
int anInt = getWidth() / 2;
int circlesRadius = (int) (anInt - circlesWidth / 2);//半径
//圆环
mPaint.setColor(circlesColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(circlesWidth);
canvas.drawCircle(anInt, anInt, circlesRadius, mPaint);
//百分比
if (isPercent && style == STROKE) {
mPaint.setStrokeWidth(textCrude);
mPaint.setColor(textColor);
mPaint.setTextSize(textSize);
mPaint.setTypeface(mTypeface);
int percent = (int) (((float) currentProgress / (float) ProgressDefaults.PROGRESS_BAR_MAX) * 100);
float textWidth = mPaint.measureText(percent + "%");
canvas.drawText(percent + "%", anInt - textWidth / 2, anInt + textSize / 2, mPaint);
}
//进度的圆环
mPaint.setColor(currentProgressColor);
mPaint.setStrokeWidth(currentScheduleWidth);
RectF rectF = new RectF(anInt - circlesRadius, anInt - circlesRadius, anInt + circlesRadius, anInt + circlesRadius);
//选择风格
switch (style) {
case STROKE:
if (currentProgress != 0) {
mPaint.setStyle(Paint.Style.STROKE);
canvas.drawArc(rectF, 0, 360 * currentProgress / ProgressDefaults.PROGRESS_BAR_MAX, false, mPaint);
}
break;
case FILL:
if (currentProgress != 0) {
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
canvas.drawArc(rectF, 0, 360 * currentProgress / ProgressDefaults.PROGRESS_BAR_MAX, true, mPaint);
}
break;
}
Log.i("onDraw---->", "onDraw current");
}
public synchronized void setCurrentProgress(int currentProgress) {
if (currentProgress < ProgressDefaults.CURRENT_PROGRESS) {
currentProgress = ProgressDefaults.CURRENT_PROGRESS;
}
if (currentProgress > ProgressDefaults.PROGRESS_BAR_MAX) {
currentProgress = ProgressDefaults.PROGRESS_BAR_MAX;
}
if (currentProgress <= ProgressDefaults.PROGRESS_BAR_MAX) {
this.currentProgress = currentProgress;
postInvalidate();
}
}
public synchronized float getCurrentProgress() {
return currentProgress;
}
public void setScheduleWidth(float currentScheduleWidth) {
this.currentScheduleWidth = currentScheduleWidth;
}
public float getScheduleWidth() {
return currentScheduleWidth;
}
public void setStyle(int style) {
this.style = style;
}
public int getStyle() {
return style;
}
public void setPercent(boolean isPercent) {
this.isPercent = isPercent;
}
public boolean getPercent() {
return isPercent;
}
public void setCurrentProgressColor(int currentProgressColor) {
if (currentProgressColor < ProgressDefaults.MIN) {
currentProgressColor = ProgressDefaults.CURRENT_PROGRESS_COLOR;
}
this.currentProgressColor = currentProgressColor;
}
public int getCurrentProgressColor() {
return currentProgressColor;
}
public void setTypeface(Typeface typeface) {
this.mTypeface = typeface;
}
public void setCirclesWidth(float circlesWidth) {
if (circlesWidth < ProgressDefaults.MIN) {
circlesWidth = ProgressDefaults.CIRCLES_WIDTH;
}
this.circlesWidth = circlesWidth;
}
public float getCirclesWidth() {
return circlesWidth;
}
public void setCirclesColor(int circlesColor) {
if (circlesColor < ProgressDefaults.MIN) {
circlesColor = ProgressDefaults.CIRCLES_COLOR;
}
this.circlesColor = circlesColor;
}
public int getCirclesColor() {
return circlesColor;
}
public void setTextCrude(float textCrude) {
if (textCrude < ProgressDefaults.MIN) {
textCrude = ProgressDefaults.TEXT_CRUDE;
}
this.textCrude = textCrude;
}
public float getTextCrude() {
return textCrude;
}
public void setTextColor(int textColor) {
if (textColor < ProgressDefaults.MIN) {
textColor = ProgressDefaults.TEXT_COLOR;
}
this.textColor = textColor;
}
public int getTextColor() {
return textColor;
}
public void setTextSize(float textSize) {
if (textSize < ProgressDefaults.MIN) {
textSize = ProgressDefaults.TEXT_SIZE;
}
this.textSize = textSize;
}
}
ProgressDefaults这个类主要的就是我们初始化的一些变量
public class ProgressDefaults {
/**
* 判断数据的最小值
*/
public static final int MIN = 1;
/**
* 最大进度
*/
public static final int PROGRESS_BAR_MAX = 100;
/**
* 默认进度
*/
public static final int CURRENT_PROGRESS = 0;
/**
* 默认圆环宽度
*/
public static final float CIRCLES_WIDTH = 5;
/**
* 默认进度宽度
*/
public static final float CURRENT_SCHEDULE_WIDTH = 6;
/**
* 默认圆环颜色
*/
public static final int CIRCLES_COLOR = Color.GRAY;
/**
* 默认字体的粗细程度
*/
public static final float TEXT_CRUDE = 0;
/**
* 默认字体颜色
*/
public static final int TEXT_COLOR = Color.GRAY;
/**
* 默认字体大小
*/
public static final float TEXT_SIZE = 25;
/**
* 默认进度的颜色
*/
public static final int CURRENT_PROGRESS_COLOR = Color.YELLOW;
/**
* 是否显示百分比
*/
public static final boolean IS_PERCENT = true;
/**
* 默认风格
*/
public static final int STYLE = 1;
}