自定义View之刻度盘
实现思路:
1.根据显示区域和UI效果计算角度,例如:
2.自定义属性
3.绘制onDraw,通过移动坐标原点和旋转画布的方式,使每一次绘制进度条坐标一致。
4.定义progress属性,使用属性动画方式添加动画效果。
一、自定义属性的声明与获取
1.1 自定义xml属性(Values/attrs.xml)
<resources>
<declare-styleable name="DriveDialView">
<attr name="mvGraduationWidth" format="dimension|reference" />
<attr name="mvGraduationHeight" format="dimension|reference" />
<attr name="mvGraduationRadius" format="dimension|reference"/>
<attr name="mvGraduationSelColor" format="color|reference"/>
<attr name="mvGraduationColor" format="color|reference"/>
<attr name="mvProgress" format="integer"/>
<attr name="mvInstitution" format="integer"/>
</declare-styleable>
</resources>
说明:
- mvGraduationWidth:进度条宽
- mvGraduationHeight:进度条高
- mvGraduationRadius:进度条圆角
- mvGraduationSelColor:已完成进度颜色
- mvGraduationColor:未完成进度颜色
- mvProgress:当前进度
- mvInstitution:总进度
1.2 在layout布局文件中使用
<com.tiddlerliu.oktest.widget.DriveDialView
android:id="@+id/car_drive_dial"
android:layout_width="208dp"
android:layout_height="208dp"
app:mvGraduationSelColor="@color/white"
app:mvGraduationColor="#30B4B8"
app:mvGraduationWidth="11dp"
app:mvGraduationHeight="5dp"
app:mvGraduationRadius="2dp"
app:mvInstitution="1000"
android:layout_gravity="center_horizontal" />
1.3 获取自定义属性
1.3.1 创建构造方法
/**
* 刻度盘
*/
public class DriveDialView extends View {
/**
* 使用new创建时调用
* @param context
*/
public DriveDialView(Context context) {
this(context, null);
}
/**
* 在xml中添加控件时调用
* @param context
* @param attrs {属性集}
*/
public DriveDialView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
/**
* 在xml添加控件,并使用了自定义属性集时调用
* @param context
* @param attrs
* @param defStyleAttr
*/
public DriveDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
}
1.3.2 声明自定义属性并赋初始值
/**
* 总刻度数
*/
private final static int SCALE_COUNT = 28;
/**
* 每个刻度条需要旋转的角度
*/
private static float mGraduationAngle = 8;
/**
* 刻度条宽
*/
private int mGraduationWidth = 22;
/**
* 刻度条高
*/
private int mGraduationHeight = 10;
/**
* 刻度条圆角
*/
private int mGraduationRadius = 4;
/**
* 刻度条完成进度颜色
*/
private int mGraduationSelColor = Color.WHITE;
/**
* 刻度条未完成进度颜色
*/
private int mGraduationColor = Color.parseColor("#30B4B8");
/**
* 定义进度
*/
float progress = 0;
/**
* 定义总进度
*/
int institution = 1000;
1.3.3 获取自定义属性值
public DriveDialView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray typedArray = getContext().obtainStyledAttributes(attrs,
R.styleable.DriveDialView, defStyleAttr, 0);
if (typedArray.hasValue(R.styleable.DriveDialView_mvGraduationWidth)) {
/*
* getDimension如果是dimen是dp或sp的单位,将其乘以density,如果是px,则不乘,返回类型为float。
* getDimensionPixelSize总体类似,但是当单位是px时,也会乘以density,返回类型为int。
*/
mGraduationWidth = (int) typedArray.getDimension(
R.styleable.DriveDialView_mvGraduationWidth, mGraduationWidth);
}
if(typedArray.hasValue(R.styleable.DriveDialView_mvGraduationHeight)){
mGraduationHeight = (int) typedArray.getDimension(R.styleable.DriveDialView_mvGraduationHeight,mGraduationHeight);
}
if(typedArray.hasValue(R.styleable.DriveDialView_mvGraduationRadius)){
mGraduationRadius = (int) typedArray.getDimension(R.styleable.DriveDialView_mvGraduationRadius,mGraduationRadius);
}
mGraduationSelColor = typedArray.getColor(
R.styleable.DriveDialView_mvGraduationSelColor, mGraduationSelColor);
mGraduationColor = typedArray.getColor(
R.styleable.DriveDialView_mvGraduationColor, mGraduationColor);
progress = (float) typedArray.getInt(R.styleable.DriveDialView_mvProgress,(int)progress);
institution = typedArray.getInt(R.styleable.DriveDialView_mvInstitution,institution);
typedArray.recycle();
}
二、 onDraw()
1 移动坐标原点到中心位置
2 旋转画布
3 调整画布
因为左右两边都有一部分间隔,并不是从0刻度开始绘制,此处需要调整画布。
4.绘制每个进度条
根据当前进度判断画笔颜色,每个进度条绘制完成后画布需要旋转一定角度。
5.再次调整画布
右边也有部分间隔,再次调整画布使左右对称。
//定义画笔
Paint mPaint = new Paint();
@Override
protected void onDraw(Canvas canvas) {
//移动坐标原点到中心位置
canvas.translate((getWidth()-getPaddingLeft()-getPaddingRight()) / 2, (getWidth()-getPaddingLeft()-getPaddingRight()) / 2);
//旋转画布
canvas.rotate(-202f);
//调整画布
canvas.rotate(mGraduationAngle / 2);
//刻度
//设置画笔
mPaint.setStyle(Paint.Style.FILL);//填充
mPaint.setStrokeWidth(mGraduationHeight / 2);
//确定刻度条矩形区域
RectF rectF = new RectF(getWidth() / 2 - mGraduationWidth, 0, getWidth() / 2, mGraduationHeight);
for (int i = 0; i < SCALE_COUNT; i++) {
//渐变色 刻度盘划过部分
mPaint.setColor(mGraduationColor);
//刻度盘 未划过部分
if (i < (int) (progress * SCALE_COUNT / institution)) {
mPaint.setColor(mGraduationSelColor);
}
canvas.drawRoundRect(rectF, mGraduationRadius, mGraduationRadius, mPaint);
canvas.rotate(mGraduationAngle);
}
canvas.rotate(mGraduationAngle / 2);
canvas.save();
canvas.restore();
}
三、动画
3.1 DriveDialView类中添加方法
//定义属性动画
private ObjectAnimator animator;
public void setProgress(float progress) {
this.progress = progress;
invalidate();
}
public void setAnimator(ObjectAnimator animator) {
this.animator = animator;
animator.start();
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
if(animator != null){
animator.end();
}
}
3.2 使用属性动画
ObjectAnimator animator = ObjectAnimator.ofFloat(mViewDriveDial, "progress", 0, 850);
animator.setDuration(1000);
animator.setInterpolator(new FastOutSlowInInterpolator());
mViewDriveDial.setAnimator(animator);
3.3 不使用动画,直接显示进度
mViewDriveDial.setProgress(850);