先看效果
观察效果图,分为底层弧(黑色),顶层弧(青色)和文本,顶层颜色的填充会随着文本的变化而变化
一、创建自定义View
继承View,添加构造函数
public class StepView extends View {
public StepView(Context context) {
super(context);
}
public StepView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
}
public StepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public StepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
}
可以看到,自定义View中有四个构造函数
1、public View(Context context) 可以在代码中初始化自定义的View
2、public View(Context context, @Nullable AttributeSet attrs)可以在布局文件中使用自定义的View
3、public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr)在布局文件中使用Style属性时
4、public View(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes)仅在defStyleAttr为0或defStyleAttr不为0但Theme中没有为defStyleAttr属性赋值时起作用
二、创建与使用自定义属性
需要自定义底层弧的颜色,顶层弧的颜色,弧的宽度、文本的颜色、文本的字体大小、最大值(样例中设置10000)和当前值(样例中设置6000)
1、在values文件夹下新建资源文件attrs
<resources>
<declare-styleable name="StepView">
<attr name="currentStep" format="integer" />
<attr name="stepSize" format="dimension" />
<attr name="maxStep" format="integer" />
<attr name="stepTextColor" format="reference" />
<attr name="arcSize" format="dimension" />
<attr name="maxArcColor" format="reference" />
<attr name="currentArcColor" format="reference" />
</declare-styleable>
</resources>
format的值有很多种,需要根据自身的需要选择
属性 | 说明 |
---|---|
reference | 引用值 |
color | 颜色值 |
boolean | 布尔值 |
dimension | 尺寸值 |
float | 浮点值 |
integer | 整型值 |
string | 字符串 |
fraction | 百分数值 |
enum | 枚举 |
flag | 位 |
2、在布局中使用自定义属性
<com.example.stepview.StepView
android:id="@+id/sv"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:arcSize="20sp"
app:currentArcColor="@color/teal_200"
app:currentStep="500"
app:maxArcColor="@color/black"
app:maxStep="700"
app:stepSize="50sp"
app:stepTextColor="@color/black" />
三、获取自定义属性的值
在布局文件中使用了自定义的属性,需要我们在View里面获取
1、检索属性信息,R.styleable后面跟着的是自定义View
2、获取属性
arcSize = typedArray.getDimensionPixelSize(R.styleable.StepView_arcSize, 50);
maxArcColor = typedArray.getColor(R.styleable.StepView_maxArcColor, getResources().getColor(R.color.black));
根据属性的值类型来获取对应的值,比如在xml中设置了
app:arcSize="20sp"
需要用getDimensionPixelSize()来获取值
四、测量onMeasure()
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
参数widthMeasureSpec和heightMeasureSpec是32位的数,前2位是模式,后30位是值。
模式:
1、AT_MOST:子元素至多达到指定大小的值,布局中指定的宽高为warp_content
2、EXACTLY:父元素决定自元素的确切大小,子元素将被限定在给定的边界里而忽略它本身大小,布局中指定的宽高为match_parent或者是固定的值
3、UNSPECIFIED:父元素部队自元素施加任何束缚,子元素可以得到任意想要的大小
所以如果模式是AT_MOST的话需要进行处理
// 如果设置了宽高为warp_content,给它一个默认的宽高
if (widthMode == MeasureSpec.AT_MOST) {
width = 100;
}
if (heightMode == MeasureSpec.AT_MOST) {
height = 100;
}
int a = Math.min(width, height);
setMeasuredDimension(a, a);
五、绘制onDraw()
1、初始化画笔
需要用到三个画笔,分别画底层弧、顶层弧和文字
顶层弧的画笔初始化:
mUpPaint = new Paint();
mUpPaint.setColor(maxArcColor); // 画笔颜色
mUpPaint.setStrokeWidth(arcSize); // 画笔宽度,即弧的宽度
mUpPaint.setStyle(Paint.Style.STROKE); // 画笔样式选择描边
mUpPaint.setStrokeCap(Paint.Cap.ROUND); // 笔画的样式,为笔划突出为半圆,中心位于路径的末端
mUpPaint.setAntiAlias(true); //抗锯齿
文本画笔:
mTextPaint = new Paint();
mTextPaint.setColor(stepTextColor);
mTextPaint.setTextSize(stepSize);
mTextPaint.setAntiAlias(true);
2、 画底层弧
需要用到Canvas,Canvas提供了很多方法,画圆,画文字等等,这里需要画一个弧,选择drawArc(RectF oval,float startAngle,float sweepAngle,boolean useCenter,Panit paint)
参数说明:
oval:用于定义圆弧形状和大小的椭圆形边界,new的时候入参左、上、右、下的值,给定一个区域绘制
startAngle:圆弧开始的起始角度
sweepAngle:顺时针测量的扫描角度
useCenter:如果为true,则将椭圆的中心包括在圆弧中
paint:画笔
// 画底层弧
RectF rectF = new RectF(arcSize, arcSize, getWidth() - arcSize, getHeight());
canvas.drawArc(rectF, 135, 270, false, mUpPaint);
3、画顶层弧
顶层和底层的一样
// 画顶层弧
float x = ((float) currentStep / maxStep) * 270;
canvas.drawArc(rectF, 135, x, false, mDownPaint);
4、画文字
使用canvas提供的drwaText(String text,float x,float y,Paint paint)
参数说明:
text:要绘制的文本
x:绘制的文本原点的x坐标
y:正在绘制的文本的基线的y坐标
paint:画笔
注意:y的值是基线的坐标,不是文字底部的坐标,baseline到top的值是负数,所以y的值应该是getHeight/2 +(bottom-top)/2-bottom
// 画文字
Rect rect = new Rect();
mTextPaint.getTextBounds(String.valueOf(currentStep), 0, String.valueOf(currentStep).length(), rect);
int dx = getWidth() / 2 - rect.width() / 2;
Paint.FontMetricsInt fontMetricsInt = mTextPaint.getFontMetricsInt();
int dy = getHeight() / 2 + (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
canvas.drawText(String.valueOf(currentStep), dx, dy, mTextPaint);
六、属性动画
在自定义View中开放一个方法,可以让外面把值传过来
public void setCurrentStep(int currentStep) {
this.currentStep = currentStep;
invalidate();
}
调用了invalidate(),会走到onDraw方法里,重新绘制
ValueAnimator valueAnimator = ValueAnimator.ofInt(0, 6000);// 开始值和结束值
valueAnimator.setDuration(5000);// 动画时长
valueAnimator.setInterpolator(new DecelerateInterpolator());// 设置插值器
// 监听
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
int animatedValue = (int) animation.getAnimatedValue();
sv.setCurrentStep(animatedValue);
}
});
valueAnimator.start();// 开始执行