项目年费控件UI图
image.png
实现效果
年费.gif
控件分析(从里到外进行分析,最后分析指针)
1、中间有个红色圆点,抽取颜色和半径
2、白色圆环,抽取圆环宽度、圆环颜色
3、包围白色圆环的圆环宽度和颜色
4、指针指向的圆环颜色和宽度
5、进度圆弧的宽度、颜色和底色
6、最外边的圆弧颜色和宽度
7、指针颜色、长度和宽度
8、进度圆弧和最外层圆弧的距离
9、包围白色圆环的圆环和指针指向的圆环距离
10、绘制进度圆弧的开始角度为-225,扫描的最大度数为270
11、绘制指针时、先把画布旋转一定的角度再绘制
自定义属性
<declare-styleable name="YearCostView">
<!--centerPoint-->
<attr name="center_point_color" format="color" />
<attr name="center_point_radius" format="dimension" />
<!--中间圆环的颜色和宽度-->
<attr name="center_ring_color" format="color" />
<attr name="center_ring_width" format="dimension" />
<!--包裹中心圆环的圆环-->
<attr name="wraper_center_ring_color" format="color" />
<attr name="wraper_center_ring_width" format="dimension" />
<!--指针-->
<attr name="line_width" format="dimension" />
<attr name="line_color" format="color" />
<!--inner圆环-->
<attr name="inner_ring_color" format="color" />
<attr name="inner_ring_width" format="dimension" />
<!--target圆环-->
<attr name="target_ring_color" format="color" />
<attr name="target_ring_width" format="dimension" />
<attr name="target_bottom_ring_color" format="color" />
<!--out圆环-->
<attr name="out_ring_color" format="color" />
<attr name="out_ring_width" format="dimension" />
<!--距离-->
<attr name="inner_distance" format="dimension" />
<attr name="out_distance" format="dimension" />
</declare-styleable>
获取自定义属性
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.YearCostView, defStyleAttr, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
//中心圆点
case R.styleable.YearCostView_center_point_radius:
centerPointRadius = a.getLayoutDimension(attr, centerPointRadius);
break;
case R.styleable.YearCostView_center_point_color:
centerPointColor = a.getColor(attr, Color.parseColor("#f05b48"));
break;
//中心圆环
case R.styleable.YearCostView_center_ring_width:
centerRingWidth = a.getLayoutDimension(attr, centerRingWidth);
break;
case R.styleable.YearCostView_center_ring_color:
centerRingColor = a.getColor(attr, ContextCompat.getColor(getContext(), android.R.color.white));
break;
//包裹中心圆环的圆环
case R.styleable.YearCostView_wraper_center_ring_width:
wraperCenterRingWidth = a.getLayoutDimension(attr, wraperCenterRingWidth);
break;
case R.styleable.YearCostView_wraper_center_ring_color:
wraperCenterRingColor = a.getColor(attr, Color.parseColor("#35383c"));
break;
//指针
case R.styleable.YearCostView_line_width:
lineWidth = a.getLayoutDimension(attr, lineWidth);
break;
case R.styleable.YearCostView_line_color:
lineColor = a.getColor(attr, Color.parseColor("#f05b48"));
break;
//inner ring
case R.styleable.YearCostView_inner_ring_width:
innerRingWidth = a.getLayoutDimension(attr, innerRingWidth);
break;
case R.styleable.YearCostView_inner_ring_color:
innerRingColor = a.getColor(attr, Color.parseColor("#25292c"));
break;
//target ring
case R.styleable.YearCostView_target_ring_width:
targetRingWidth = a.getLayoutDimension(attr, targetRingWidth);
break;
case R.styleable.YearCostView_target_ring_color:
targetRingColor = a.getColor(attr, Color.parseColor("#f05b48"));
break;
case R.styleable.YearCostView_target_bottom_ring_color:
targetBottomRingColor = a.getColor(attr, Color.parseColor("#303438"));
break;
//out ring
case R.styleable.YearCostView_out_ring_width:
outRingWidth = a.getLayoutDimension(attr, outRingWidth);
break;
case R.styleable.YearCostView_out_ring_color:
outRingColor = a.getColor(attr, Color.parseColor("#75777a"));
break;
//距离
case R.styleable.YearCostView_out_distance:
outDistance = a.getLayoutDimension(attr, outDistance);
break;
case R.styleable.YearCostView_inner_distance:
innerDistance = a.getLayoutDimension(attr, innerDistance);
break;
}
}
a.recycle();
自定义控件完整代码
/**
* 类描述:年费View
* 作者:xues
* 时间:2017年09月12日
*/
public class YearCostView extends View {
//中心圆环(白色圆环)
private Paint centerRingPaint;
private int centerRingWidth = 2 * 6;
private int centerRingColor;
private RectF centerRingRectF;
//包裹中心圆环的圆环
private int wraperCenterRingColor;
private Paint wraperCenterRingPaint;
private int wraperCenterRingWidth = 4 * 6;
private RectF wraperCenterRingRectF;
//在目标圆环里边的圆环
private int innerRingColor;
private Paint innerRingPaint;
private int innerRingWidth = 1 * 6;
private RectF innerRingRectF;
//目标圆环(红色圆环)
private int targetRingColor;
private Paint targetRingPaint;
private int targetRingWidth = 16 * 6;
private RectF targetRingRectF;
//目标圆环压着的圆环(红色圆环压着的圆环)
private int targetBottomRingColor;
private Paint targetBottomRingPaint;
private int targetBottomRingWidth = 16 * 6;
private RectF targetBottomRingRectF;
//在目标圆环外面的圆环
private int outRingColor;
private Paint outRingPaint;
private int outRingWidth = 1 * 6;
private RectF outRingRectF;
//指针
private int lineColor;
private Paint linePaint;
private int lineWidth = 2 * 6;
//中心红点
private int centerPointColor;
private Paint centerPointPaint;
private int centerPointRadius = 1 * 6;
//圆环与圆环之间的距离
private int innerDistance = 120;
private int outDistance = 8;
private RotateAnimation mSweepAnim;//扫描动画
public YearCostView(Context context) {
this(context, null);
}
public YearCostView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public YearCostView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.YearCostView, defStyleAttr, 0);
int n = a.getIndexCount();
for (int i = 0; i < n; i++) {
int attr = a.getIndex(i);
switch (attr) {
//中心圆点
case R.styleable.YearCostView_center_point_radius:
centerPointRadius = a.getLayoutDimension(attr, centerPointRadius);
break;
case R.styleable.YearCostView_center_point_color:
centerPointColor = a.getColor(attr, Color.parseColor("#f05b48"));
break;
//中心圆环
case R.styleable.YearCostView_center_ring_width:
centerRingWidth = a.getLayoutDimension(attr, centerRingWidth);
break;
case R.styleable.YearCostView_center_ring_color:
centerRingColor = a.getColor(attr, ContextCompat.getColor(getContext(), android.R.color.white));
break;
//包裹中心圆环的圆环
case R.styleable.YearCostView_wraper_center_ring_width:
wraperCenterRingWidth = a.getLayoutDimension(attr, wraperCenterRingWidth);
break;
case R.styleable.YearCostView_wraper_center_ring_color:
wraperCenterRingColor = a.getColor(attr, Color.parseColor("#35383c"));
break;
//指针
case R.styleable.YearCostView_line_width:
lineWidth = a.getLayoutDimension(attr, lineWidth);
break;
case R.styleable.YearCostView_line_color:
lineColor = a.getColor(attr, Color.parseColor("#f05b48"));
break;
//inner ring
case R.styleable.YearCostView_inner_ring_width:
innerRingWidth = a.getLayoutDimension(attr, innerRingWidth);
break;
case R.styleable.YearCostView_inner_ring_color:
innerRingColor = a.getColor(attr, Color.parseColor("#25292c"));
break;
//target ring
case R.styleable.YearCostView_target_ring_width:
targetRingWidth = a.getLayoutDimension(attr, targetRingWidth);
break;
case R.styleable.YearCostView_target_ring_color:
targetRingColor = a.getColor(attr, Color.parseColor("#f05b48"));
break;
case R.styleable.YearCostView_target_bottom_ring_color:
targetBottomRingColor = a.getColor(attr, Color.parseColor("#303438"));
break;
//out ring
case R.styleable.YearCostView_out_ring_width:
outRingWidth = a.getLayoutDimension(attr, outRingWidth);
break;
case R.styleable.YearCostView_out_ring_color:
outRingColor = a.getColor(attr, Color.parseColor("#75777a"));
break;
//距离
case R.styleable.YearCostView_out_distance:
outDistance = a.getLayoutDimension(attr, outDistance);
break;
case R.styleable.YearCostView_inner_distance:
innerDistance = a.getLayoutDimension(attr, innerDistance);
break;
}
}
a.recycle();
initView();
}
/**
* 初始化参数
*/
private void initView() {
initPaint();
//初始化动画并设置动画持续时间为1000毫秒==1秒
mSweepAnim = new RotateAnimation();
mSweepAnim.setDuration(1000);
}
float centerX;//中心点x
//初始化矩形
private void initRectF() {
//中心圆环
float leftOrTop = centerX - centerRingWidth / 2F - centerPointRadius;
float rightOrBottom = centerX + centerRingWidth / 2F + centerPointRadius;
centerRingRectF = new RectF(leftOrTop, leftOrTop, rightOrBottom, rightOrBottom);
//包围中心圆环的圆环
leftOrTop = centerX - centerRingWidth - centerPointRadius - wraperCenterRingWidth / 2F;
rightOrBottom = centerX + centerRingWidth + centerPointRadius + wraperCenterRingWidth / 2F;
wraperCenterRingRectF = new RectF(leftOrTop, leftOrTop, rightOrBottom, rightOrBottom);
//inner圆环
leftOrTop = centerX - centerPointRadius - centerRingWidth - wraperCenterRingWidth - innerDistance - innerRingWidth / 2F;
rightOrBottom = centerX + centerPointRadius + centerRingWidth + wraperCenterRingWidth + innerDistance + innerRingWidth / 2F;
innerRingRectF = new RectF(leftOrTop, leftOrTop, rightOrBottom, rightOrBottom);
//目标圆环
leftOrTop = centerX - centerPointRadius - centerRingWidth - wraperCenterRingWidth - innerDistance - innerRingWidth - outDistance - targetRingWidth / 2F;
rightOrBottom = centerX + centerPointRadius + centerRingWidth + wraperCenterRingWidth + innerDistance + innerRingWidth + outDistance + targetRingWidth / 2F;
targetRingRectF = new RectF(leftOrTop, leftOrTop, rightOrBottom, rightOrBottom);
targetBottomRingRectF = new RectF(leftOrTop, leftOrTop, rightOrBottom, rightOrBottom);
//out圆环
leftOrTop = centerX - centerPointRadius - centerRingWidth - wraperCenterRingWidth - innerDistance - innerRingWidth - outDistance * 2 - targetRingWidth - outRingWidth / 2F;
rightOrBottom = centerX + centerPointRadius + centerRingWidth + wraperCenterRingWidth + innerDistance + innerRingWidth + outDistance * 2 + targetRingWidth + outRingWidth / 2F;
outRingRectF = new RectF(leftOrTop, leftOrTop, rightOrBottom, rightOrBottom);
}
/**
* 从里边向外初始化画笔
*/
private void initPaint() {
//中心点
centerPointPaint = new Paint();
centerPointPaint.setAntiAlias(true);
centerPointPaint.setStrokeWidth(2 * centerPointRadius);
centerPointPaint.setColor(centerPointColor);
centerPointPaint.setStyle(Paint.Style.FILL);
//中心圆环
centerRingPaint = new Paint();
centerRingPaint.setAntiAlias(true);
centerRingPaint.setStrokeWidth(centerRingWidth);
centerRingPaint.setColor(centerRingColor);
centerRingPaint.setStyle(Paint.Style.STROKE);
//包裹中心圆环的圆环
wraperCenterRingPaint = new Paint();
wraperCenterRingPaint.setAntiAlias(true);
wraperCenterRingPaint.setStrokeWidth(wraperCenterRingWidth);
wraperCenterRingPaint.setColor(wraperCenterRingColor);
wraperCenterRingPaint.setStyle(Paint.Style.STROKE);
//inner圆环
innerRingPaint = new Paint();
innerRingPaint.setAntiAlias(true);
innerRingPaint.setStrokeWidth(innerRingWidth);
innerRingPaint.setColor(innerRingColor);
innerRingPaint.setStyle(Paint.Style.STROKE);
//目标圆环压住的圆环
targetBottomRingPaint = new Paint();
targetBottomRingPaint.setAntiAlias(true);
targetBottomRingPaint.setStrokeWidth(targetRingWidth);
targetBottomRingPaint.setColor(targetBottomRingColor);
targetBottomRingPaint.setStyle(Paint.Style.STROKE);
//目标圆环
targetRingPaint = new Paint();
targetRingPaint.setAntiAlias(true);
targetRingPaint.setStrokeWidth(targetRingWidth);
targetRingPaint.setColor(targetRingColor);
targetRingPaint.setStyle(Paint.Style.STROKE);
//外部圆环
outRingPaint = new Paint();
outRingPaint.setAntiAlias(true);
outRingPaint.setStrokeWidth(outRingWidth);
outRingPaint.setColor(outRingColor);
outRingPaint.setStyle(Paint.Style.STROKE);
//指针
linePaint = new Paint();
linePaint.setAntiAlias(true);
linePaint.setStrokeWidth(lineWidth);
linePaint.setColor(lineColor);
linePaint.setStyle(Paint.Style.FILL);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int wSize = 0;
//match_parent or set value
if (widthMode == MeasureSpec.EXACTLY) {
wSize = widthSize;
} else {
//wrap_content
if (widthMode == MeasureSpec.AT_MOST) {
wSize = 2 * (centerPointRadius + centerRingWidth + wraperCenterRingWidth + innerDistance + innerRingWidth + outDistance * 2 + targetRingWidth);
}
}
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
int hSize = 0;
//match_parent or set value
if (heightMode == MeasureSpec.EXACTLY) {
hSize = heightSize;
} else {
//wrap_content
if (heightMode == MeasureSpec.AT_MOST) {
hSize = 2 * (centerPointRadius + centerRingWidth + wraperCenterRingWidth + innerDistance + innerRingWidth + outDistance * 2 + targetRingWidth);
;
}
}
centerX = widthSize / 2f;
initRectF();//初始化灰色圆环和多颜色圆环矩形
setMeasuredDimension(wSize, hSize);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//绘制中心点
canvas.save();
canvas.restore();
//绘制包裹中心圆环的圆环
canvas.save();
canvas.drawArc(wraperCenterRingRectF, 0, 360, false, wraperCenterRingPaint);
canvas.restore();
//绘制inner圆环
canvas.save();
canvas.drawArc(innerRingRectF, 0, 360, false, innerRingPaint);
canvas.restore();
//绘制指针
canvas.save();
canvas.translate(centerX, centerX);
canvas.rotate(mSweepAngle + 135);
canvas.drawLine(-2, -2, centerPointRadius + centerRingWidth + wraperCenterRingWidth + innerDistance + innerRingWidth, 0, linePaint);
canvas.restore();
//绘制中心圆环
canvas.save();
canvas.drawArc(centerRingRectF, 0, 360, false, centerRingPaint);
canvas.restore();
//绘制红色圆点
canvas.save();
canvas.drawCircle(centerX, centerX, centerPointRadius, centerPointPaint);
canvas.restore();
//绘制目标圆环压住的圆环
canvas.save();
canvas.drawArc(targetBottomRingRectF, -225, 270, false, targetBottomRingPaint);
canvas.restore();
//绘制目标圆环
canvas.save();
canvas.drawArc(targetRingRectF, -225, mSweepAngle, false, targetRingPaint);
canvas.restore();
//绘制out圆环
canvas.save();
canvas.drawArc(outRingRectF, -226, 272, false, outRingPaint);
canvas.restore();
}
float mSweepAngle;
/**
* 自定义旋转动画
*/
public class RotateAnimation extends Animation {
/**
* Initializes expand collapse animation, has two types, collapse (1) and expand (0).
*/
public RotateAnimation() {
}
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {
super.applyTransformation(interpolatedTime, t);
//interpolatedTime 范围:0到1
mSweepAngle = BigDecimalUtil.mul(interpolatedTime, BigDecimalUtil.mul(BigDecimalUtil.div(curMoney, totalMoney), 270F));//当前时间扫描的角度
postInvalidate();//重绘
}
}
float curMoney, totalMoney;
/**
* 设置价格
*
* @param curMoney 当前价格
* @param totalMoney 总金额
*/
public void setMoney(float curMoney, float totalMoney) {
this.curMoney = curMoney;
this.totalMoney = totalMoney;
startAnimation(mSweepAnim);
}
}
使用篇
在布局中使用自定义控件
<包名.YearCostView
android:id="@+id/yearCostView"
android:layout_width="@dimen/px480dp"
android:layout_height="@dimen/px480dp"
android:layout_gravity="center"
android:background="#191d21"
app:center_point_color="#f05b48"
app:center_point_radius="@dimen/px6dp"
app:center_ring_color="#ffffff"
app:center_ring_width="@dimen/px6dp"
app:inner_distance="@dimen/px100dp"
app:inner_ring_color="#25292c"
app:inner_ring_width="@dimen/px2dp"
app:line_color="#f05b48"
app:line_width="@dimen/px6dp"
app:out_distance="@dimen/px10dp"
app:out_ring_color="#75777a"
app:out_ring_width="@dimen/px2dp"
app:target_bottom_ring_color="#303438"
app:target_ring_color="#f05b48"
app:target_ring_width="@dimen/px64dp"
app:wraper_center_ring_color="#35383c"
app:wraper_center_ring_width="@dimen/px12dp" />
在Activity中设置价格
yearCostView.setMoney(135,270);//左侧为当前金额,右侧为总金额
网友评论