本例子是实现了一个圆形的进度条功能,同时还可以控制进度的速度。
先上效果图:
Android自定义View的实现步骤:
1、自定义View的属性
2、在自定义View的构造方法中获取属性
3、[ 重写onMeasure ] 方法
4、重写 onDraw方法
第三步并不是必须的 但是大部分自定义控件都是需要的。
从上图我们可以看出需要自定义的一些属性:圆环的底色,进度条颜色,进度字体大小,进度字体颜色,速度,圆环的宽度 。这就是我们需要的属性。
<declare-styleable name="MyView01">
<attr name="fisrtColor" format="color" />
<attr name="secondColor" format="color" />
<attr name="speed" format="integer" />
<attr name="roundWidth" format="dimension" />
<attr name="progressTextColor" format="color" />
<attr name="progressTextSize" format="dimension" />
</declare-styleable>
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyView01, defStyleAttr, 0);
int count = typedArray.getIndexCount();
for (int i = 0; i < count; i++) {
int index = typedArray.getIndex(i);
switch (index) {
case R.styleable.MyView01_fisrtColor:
mFirstColor = typedArray.getColor(index, Color.BLUE);
break;
case R.styleable.MyView01_secondColor:
mSecondColor = typedArray.getColor(index, Color.CYAN);
break;
case R.styleable.MyView01_progressTextColor:
mProgressTextColor = typedArray.getColor(index, Color.GREEN);
break;
case R.styleable.MyView01_roundWidth:
mRoundWidth = typedArray.getDimensionPixelSize(index,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
break;
case R.styleable.MyView01_progressTextSize:
mProgressTextSize = typedArray.getDimensionPixelSize(index,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()));
break;
case R.styleable.MyView01_speed:
mSpeed = typedArray.getInt(index, 20);
break;
}
}
typedArray.recycle();
获取了属性之后 就要在onDraw方法中进行绘制了。
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mRoundWidth);
mPaint.setAntiAlias(true);
mPaint.setColor(mFirstColor);
//获取view的高宽
int width = getWidth();
int height = getHeight();
int radius = width / 2 - mRoundWidth / 2; //半径
canvas.drawCircle(width / 2, height / 2, radius, mPaint);//画底色
mRectF.left = mRoundWidth / 2;
mRectF.right = getWidth() - mRoundWidth / 2;
mRectF.top = mRoundWidth / 2;
mRectF.bottom = getHeight() - mRoundWidth / 2;
mPaint.setColor(mSecondColor);
mPaint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawArc(mRectF, -90, mProgress, false, mPaint); //画圆弧
String drawText = mProgress * 100 / 360 + " %";
mTextPaint.getTextBounds(drawText, 0, drawText.length(), mRect);
canvas.drawText(drawText, getWidth() / 2 - mRect.width() / 2, getHeight() / 2 + mRect.height() / 2, mTextPaint);//进度字体
}
为了自定义的控件宽高可以使用 ,我们还要重写onMeasure方法,默认宽高设置为200
int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
int heigth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,200, getResources().getDisplayMetrics());
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(width, widthSize);
}
}
if (heigthMode == MeasureSpec.EXACTLY) {
heigth = heightSize;
} else {
if (heigthMode == MeasureSpec.AT_MOST) {
heigth = Math.min(heigth, heightSize);
}
}
setMeasuredDimension(width, heigth);
}
这样整个控件就绘制完成了。
但是现在的控件还不是动态的,要让其动起来还需要最后一步:
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
mProgress++;
if (mProgress > 360) {
mProgress = 1;
}
postInvalidate();
Thread.sleep(mSpeed);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
这样就大功告成。
在布局文件中使用:
<com.ppdai.viewdemo.view.MyView01
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="20dp"
android:layout_marginTop="20dp"
app:fisrtColor="@color/colorAccent"
app:progressTextColor="@color/colorPrimaryDark"
app:progressTextSize="16sp"
app:roundWidth="10dp"
app:secondColor="@color/colorPrimary"
app:speed="50" />
ok,最后贴上全部的View代码:
public class MyView01 extends View {
private int mFirstColor;
private int mSecondColor;
private int mSpeed;
private int mRoundWidth;
private Paint mPaint;
private RectF mRectF;
private int mProgress = 1;
private TextPaint mTextPaint;
private int mProgressTextColor;
private Rect mRect;
private int mProgressTextSize;
public MyView01(Context context) {
this(context, null);
}
public MyView01(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyView01(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyView01, defStyleAttr, 0);
int count = typedArray.getIndexCount();
for (int i = 0; i < count; i++) {
int index = typedArray.getIndex(i);
switch (index) {
case R.styleable.MyView01_fisrtColor:
mFirstColor = typedArray.getColor(index, Color.BLUE);
break;
case R.styleable.MyView01_secondColor:
mSecondColor = typedArray.getColor(index, Color.CYAN);
break;
case R.styleable.MyView01_progressTextColor:
mProgressTextColor = typedArray.getColor(index, Color.GREEN);
break;
case R.styleable.MyView01_roundWidth:
mRoundWidth = typedArray.getDimensionPixelSize(index,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 10, getResources().getDisplayMetrics()));
break;
case R.styleable.MyView01_progressTextSize:
mProgressTextSize = typedArray.getDimensionPixelSize(index,
(int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 20, getResources().getDisplayMetrics()));
break;
case R.styleable.MyView01_speed:
mSpeed = typedArray.getInt(index, 20);
break;
}
}
typedArray.recycle();
new Thread(new Runnable() {
@Override
public void run() {
try {
while (true) {
mProgress++;
if (mProgress > 360) {
mProgress = 1;
}
postInvalidate();
Thread.sleep(mSpeed);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
mPaint = new Paint();
mRectF = new RectF();
mTextPaint = new TextPaint();
mTextPaint.setColor(mProgressTextColor);
mTextPaint.setTextSize(mProgressTextSize);
mTextPaint.setStrokeWidth(0);
mTextPaint.setAntiAlias(true);
mRect = new Rect();
}
int width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 200, getResources().getDisplayMetrics());
int heigth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,200, getResources().getDisplayMetrics());
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heigthMode = MeasureSpec.getMode(heightMeasureSpec);
if (widthMode == MeasureSpec.EXACTLY) {
width = widthSize;
} else {
if (widthMode == MeasureSpec.AT_MOST) {
width = Math.min(width, widthSize);
}
}
if (heigthMode == MeasureSpec.EXACTLY) {
heigth = heightSize;
} else {
if (heigthMode == MeasureSpec.AT_MOST) {
heigth = Math.min(heigth, heightSize);
}
}
setMeasuredDimension(width, heigth);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mRoundWidth);
mPaint.setAntiAlias(true);
mPaint.setColor(mFirstColor);
//获取view的高宽
int width = getWidth();
int height = getHeight();
int radius = width / 2 - mRoundWidth / 2; //半径
canvas.drawCircle(width / 2, height / 2, radius, mPaint);//画底色
mRectF.left = mRoundWidth / 2;
mRectF.right = getWidth() - mRoundWidth / 2;
mRectF.top = mRoundWidth / 2;
mRectF.bottom = getHeight() - mRoundWidth / 2;
mPaint.setColor(mSecondColor);
mPaint.setStrokeCap(Paint.Cap.ROUND);
canvas.drawArc(mRectF, -90, mProgress, false, mPaint); //画圆弧
String drawText = mProgress * 100 / 360 + " %";
mTextPaint.getTextBounds(drawText, 0, drawText.length(), mRect);
canvas.drawText(drawText, getWidth() / 2 - mRect.width() / 2, getHeight() / 2 + mRect.height() / 2, mTextPaint);
}
}