Android自定义View实现仿驾考宝典显示分数效果(收藏)
程序员文章站
2023-12-16 20:36:58
小编最近发现,一些炫酷的view效果,通过需要自定义view和属性动画结合在一起,才能更容易的实现。
实现的效果图如下:
所用的知识有:
(1)自定义view中...
小编最近发现,一些炫酷的view效果,通过需要自定义view和属性动画结合在一起,才能更容易的实现。
实现的效果图如下:
所用的知识有:
(1)自定义view中的 path ,主要用来绘制指示块。
(2)属性动画-valueanimator,并设置属性动画的监听器。
(3)根据属性动画是否结束的标志,决定是否绘制分数对应的描述文本内容。
实现步骤:
继承自view,在构造函数中获取自定义属性和初始化操作(初始化画笔)
private void obtainattrs(context context, attributeset attrs) { typedarray typedarray = context.obtainstyledattributes(attrs, r.styleable.scoreview); linelength = typedarray.getdimension(r.styleable.scoreview_linelength, dp2px(10)); linecolor = typedarray.getcolor(r.styleable.scoreview_linecolor, color.white); typedarray.recycle(); } private void init() { arrowpaint = createpaint(color.white, 0, paint.style.fill, 0); arcpaint = createpaint(linecolor, dp2px(1), paint.style.stroke, 0); bgpaint = createpaint(linecolor, dp2px(1), paint.style.fill, 0); reachprogresspaint = createpaint(color.white, dp2px(1), paint.style.fill, 0); arcreachpaint = createpaint(color.white, dp2px(1), paint.style.stroke, 0); scoretextpaint = createpaint(color.white, 0, paint.style.stroke, dp2px(26)); desctextpaint = createpaint(color.white, 0, paint.style.stroke, dp2px(16)); }
其中初始化画笔抽取到一个函数中:
private paint createpaint(int color, int strokewidth, paint.style style, float textsize) { paint paint = new paint(paint.anti_alias_flag); paint.setcolor(color); paint.setstrokewidth(strokewidth); paint.setstyle(style); paint.setstrokejoin(paint.join.round); paint.setstrokecap(paint.cap.round); paint.settextsize(textsize); return paint; }
覆盖 onsizechanged() ,得到控件的宽高,并决定要绘制区域的大小(控件默认设置了内边距)
@override protected void onsizechanged(int w, int h, int oldw, int oldh) { super.onsizechanged(w, h, oldw, oldh); viewwidth = w; viewheight = h; halfview = math.min(viewwidth, viewheight) / 2; //宽度或高度中最小值的一半,即决定圆心的位置。 radius = (math.min(viewwidth, viewheight) - 2 * default_padding) / 2; //绘制园的半径是宽高除去默认内边距 }
核心绘制代码,覆盖ondraw()方法,根据动画是否结束的标志,决定是否绘制分数对应的文本。
@override protected void ondraw(canvas canvas) { super.ondraw(canvas); drawarcbackground(canvas); drawarcprogress(canvas); drawscoretext(canvas); if (isanimend) { drawdesctext(canvas); } }
(1)绘制圆弧背景和灰色刻度背景。
private void drawarcbackground(canvas canvas) { canvas.save(); canvas.translate(halfview, halfview); //绘制圆弧 rectf rectf = new rectf(dp2px(5) - radius, dp2px(5) - radius, radius - dp2px(5), radius - dp2px(5)); canvas.drawarc(rectf, 120, 300, false, arcpaint); //绘制刻度线 canvas.rotate(30); for (int i = 0; i < 100; i++) { canvas.drawline(0, radius - dp2px(15), 0, radius - dp2px(15) - linelength, bgpaint); canvas.rotate(degree); } canvas.restore(); }
(2) 绘制刻度,根据valueanimator进行动画的当前值curvalue,来动态改变绘制指示块和已达进度圆弧,从而实现从0开始移动到设定值的动画效果。
private void drawarcprogress(canvas canvas) { canvas.save(); canvas.translate(halfview, halfview); //绘制圆弧 rectf rectf = new rectf(dp2px(5) - radius, dp2px(5) - radius, radius - dp2px(5), radius - dp2px(5)); canvas.drawarc(rectf, 120, curvalue * degree, false, arcreachpaint); //绘制指示方块,方块是从0开始移动某一个位置的 canvas.rotate(30 + degree * curvalue); path path = new path(); path.moveto(dp2px(5), radius); path.lineto(dp2px(5), radius - dp2px(10)); path.lineto(0, radius - dp2px(15)); path.lineto(-dp2px(5), radius - dp2px(10)); path.lineto(-dp2px(5), radius); path.close(); canvas.drawpath(path, arrowpaint); //绘制已经达到的刻度 canvas.restore(); canvas.save(); canvas.translate(halfview, halfview); canvas.rotate(30); for (int i = 0; i < curvalue; i++) { canvas.drawline(0, radius - dp2px(15), 0, radius - dp2px(15) - linelength, reachprogresspaint); canvas.rotate(degree); } canvas.restore(); }
(3) 绘制分数文本。
private void drawscoretext(canvas canvas) { canvas.save(); canvas.translate(halfview, halfview); string scoretext = curvalue + "分"; float textlength = scoretextpaint.measuretext(scoretext); canvas.drawtext(scoretext, -textlength / 2, 0, scoretextpaint); canvas.restore(); }
(4) 动画结束时,绘制最终分数对应的提示信息,该信息只有在动画结束后,才会显示出来。
private void drawdesctext(canvas canvas) { canvas.save(); canvas.translate(halfview, halfview); string desc = ""; if (curvalue >= 90) { desc = "车神"; } else { desc = "马路杀手"; } float desclength = desctextpaint.measuretext(desc); canvas.drawtext(desc, -desclength / 2, dp2px(30), desctextpaint); canvas.restore(); isanimend = false; // isanimend置为false,防止再次点击start时,就显示动画结束时才能显示的内容 }
(5)提供对外设置最大值的接口,决定最后的分数。
/** * 对外提供的接口,用于设置进度的最大值 * * @param value */ public void setmaxvalue(int value) { if (valueanimator == null) { valueanimator = valueanimator.ofint(0, value); } valueanimator.setinterpolator(new linearinterpolator()); valueanimator.setduration(30 * value); valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { curvalue = (int) animation.getanimatedvalue(); log.i("debug", "curvalue=" + curvalue); invalidate(); } }); valueanimator.addlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { super.onanimationend(animation); isanimend = true; //标记动画结束 log.i("debug", "onanimationend"); log.i("debug", "onanimationend curvalue = " + curvalue); invalidate(); } }); valueanimator.start(); }
完整代码:
public class scoreview extends view { private final int default_padding = dp2px(5); private final int default_width = dp2px(200); //默认宽度为200dp private final int default_height = dp2px(200); //默认高度为200dp private int viewwidth; //view宽度 private int viewheight; //view高度 private int halfview; //view宽度或高度的一半 private int radius; //绘制圆形区域的半径 private paint bgpaint; private paint arrowpaint; //指示块画笔 private paint arcpaint; //圆弧画笔 private paint arcreachpaint; //圆弧画笔 private paint reachprogresspaint; //已达刻度 private paint scoretextpaint; //绘制分数文本 private paint desctextpaint; //绘制描述文本 private float degree = 3f; //相邻刻度之间夹角大小为3度,角度制,不是弧度制 private float linelength; private int linecolor; private int curvalue; //动画进行的当前值 private valueanimator valueanimator; private boolean isanimend = false; public scoreview(context context) { this(context, null); } public scoreview(context context, attributeset attrs) { super(context, attrs); obtainattrs(context, attrs); init(); } private void obtainattrs(context context, attributeset attrs) { typedarray typedarray = context.obtainstyledattributes(attrs, r.styleable.scoreview); linelength = typedarray.getdimension(r.styleable.scoreview_linelength, dp2px(10)); linecolor = typedarray.getcolor(r.styleable.scoreview_linecolor, color.white); typedarray.recycle(); } private void init() { arrowpaint = createpaint(color.white, 0, paint.style.fill, 0); arcpaint = createpaint(linecolor, dp2px(1), paint.style.stroke, 0); bgpaint = createpaint(linecolor, dp2px(1), paint.style.fill, 0); reachprogresspaint = createpaint(color.white, dp2px(1), paint.style.fill, 0); arcreachpaint = createpaint(color.white, dp2px(1), paint.style.stroke, 0); scoretextpaint = createpaint(color.white, 0, paint.style.stroke, dp2px(26)); desctextpaint = createpaint(color.white, 0, paint.style.stroke, dp2px(16)); } @override protected void onmeasure(int widthmeasurespec, int heightmeasurespec) { super.onmeasure(widthmeasurespec, heightmeasurespec); setmeasureddimension(measuresize(widthmeasurespec, default_width), measuresize(heightmeasurespec, default_height)); } private int measuresize(int measurespec, int defaultsize) { int measuresize = defaultsize; int mode = measurespec.getmode(measurespec); int size = measurespec.getsize(measurespec); if (mode == measurespec.exactly) { measuresize = size; } else { if (mode == measurespec.at_most) { measuresize = math.min(defaultsize, size); } } return measuresize; } @override protected void onsizechanged(int w, int h, int oldw, int oldh) { super.onsizechanged(w, h, oldw, oldh); viewwidth = w; viewheight = h; halfview = math.min(viewwidth, viewheight) / 2; //宽度或高度中最小值的一半,即圆形的位置 radius = (math.min(viewwidth, viewheight) - 2 * default_padding) / 2; //半径是宽高除去默认内边距的 } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); drawarcbackground(canvas); drawarcprogress(canvas); drawscoretext(canvas); if (isanimend) { drawdesctext(canvas); } } private void drawscoretext(canvas canvas) { canvas.save(); canvas.translate(halfview, halfview); string scoretext = curvalue + "分"; float textlength = scoretextpaint.measuretext(scoretext); canvas.drawtext(scoretext, -textlength / 2, 0, scoretextpaint); canvas.restore(); } /** * 绘制文本 * * @param canvas */ private void drawdesctext(canvas canvas) { canvas.save(); canvas.translate(halfview, halfview); string desc = ""; if (curvalue >= 90) { desc = "车神"; } else { desc = "马路杀手"; } float desclength = desctextpaint.measuretext(desc); canvas.drawtext(desc, -desclength / 2, dp2px(30), desctextpaint); canvas.restore(); isanimend = false; // isanimend置为false,防止再次点击start时,就显示动画结束时才能显示的内容 } /** * 绘制进度 * * @param canvas */ private void drawarcprogress(canvas canvas) { canvas.save(); canvas.translate(halfview, halfview); //绘制圆弧 rectf rectf = new rectf(dp2px(5) - radius, dp2px(5) - radius, radius - dp2px(5), radius - dp2px(5)); canvas.drawarc(rectf, 120, curvalue * degree, false, arcreachpaint); //绘制指示方块,方块是从0开始移动某一个位置的 canvas.rotate(30 + degree * curvalue); path path = new path(); path.moveto(dp2px(5), radius); path.lineto(dp2px(5), radius - dp2px(10)); path.lineto(0, radius - dp2px(15)); path.lineto(-dp2px(5), radius - dp2px(10)); path.lineto(-dp2px(5), radius); path.close(); canvas.drawpath(path, arrowpaint); //绘制已经达到的刻度 canvas.restore(); canvas.save(); canvas.translate(halfview, halfview); canvas.rotate(30); for (int i = 0; i < curvalue; i++) { canvas.drawline(0, radius - dp2px(15), 0, radius - dp2px(15) - linelength, reachprogresspaint); canvas.rotate(degree); } canvas.restore(); } /** * 绘制背景 * * @param canvas */ private void drawarcbackground(canvas canvas) { canvas.save(); canvas.translate(halfview, halfview); //绘制圆弧 rectf rectf = new rectf(dp2px(5) - radius, dp2px(5) - radius, radius - dp2px(5), radius - dp2px(5)); canvas.drawarc(rectf, 120, 300, false, arcpaint); //绘制刻度线 canvas.rotate(30); for (int i = 0; i < 100; i++) { canvas.drawline(0, radius - dp2px(15), 0, radius - dp2px(15) - linelength, bgpaint); canvas.rotate(degree); } canvas.restore(); } /** * 创建画笔 * * @param color * @param strokewidth * @param style * @param textsize * @return */ private paint createpaint(int color, int strokewidth, paint.style style, float textsize) { paint paint = new paint(paint.anti_alias_flag); paint.setcolor(color); paint.setstrokewidth(strokewidth); paint.setstyle(style); paint.setstrokejoin(paint.join.round); paint.setstrokecap(paint.cap.round); paint.settextsize(textsize); return paint; } /** * dp转换成px * * @param dpvalue * @return */ private int dp2px(int dpvalue) { return (int) typedvalue.applydimension(typedvalue.complex_unit_dip, dpvalue, getresources().getdisplaymetrics()); } /** * 对外提供的接口,用于设置进度的最大值 * * @param value */ public void setmaxvalue(int value) { if (valueanimator == null) { valueanimator = valueanimator.ofint(0, value); } valueanimator.setinterpolator(new linearinterpolator()); valueanimator.setduration(30 * value); valueanimator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { curvalue = (int) animation.getanimatedvalue(); log.i("debug", "curvalue=" + curvalue); invalidate(); } }); valueanimator.addlistener(new animatorlisteneradapter() { @override public void onanimationend(animator animation) { super.onanimationend(animation); isanimend = true; //标记动画结束 log.i("debug", "onanimationend"); log.i("debug", "onanimationend curvalue = " + curvalue); invalidate(); } }); valueanimator.start(); } }
以上所述是小编给大家介绍的android自定义view实现仿驾考宝典显示分数效果(收藏),希望对大家有所帮助