Android 自定View实现仿QQ运动步数圆弧及动画效果
在之前的android超精准计步器开发-dylan计步中的首页用到了一个自定义控件,和qq运动的界面有点类似,还有动画效果,下面就来讲一下这个view是如何绘制的。
1.先看效果图
2.效果图分析
功能说明:黄色的代表用户设置的总计划锻炼步数,红色的代表用户当前所走的步数。
初步分析:完全自定义view重写ondraw()方法,画圆弧。
3.画一个圆弧必备知识
在canvas中有一个画圆弧的方法
drawarc(rectf oval, float startangle, float sweepangle, boolean usecenter, paint paint)//画弧,
参数一是rectf对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,
参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。
参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。
参数四是如果是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果是false(假)这将是一个弧线。
参数五是paint对象;
对于这个方法,大家可以看一下我手绘的草图,比较烂,表达一下这几个参数的意思和绘制过程,画得不好望大家见谅!
4.绘图的准备工作
(1).获取中心点坐标
/**中心点的x坐标*/ float centerx = (getwidth()) / 2;
(2).建立一个圆弧外的参考矩形
/**指定圆弧的外轮廓矩形区域*/ rectf rectf = new rectf(0 + borderwidth, borderwidth, 2 * centerx - borderwidth, 2 * centerx - borderwidth);
5.绘图的主要步骤
(1).【第一步】绘制整体的黄色圆弧
/** * 1.绘制总步数的黄色圆弧 * * @param canvas 画笔 * @param rectf 参考的矩形 */ private void drawarcyellow(canvas canvas, rectf rectf) { paint paint = new paint(); /** 默认画笔颜色,黄色 */ paint.setcolor(getresources().getcolor(r.color.yellow)); /** 结合处为圆弧*/ paint.setstrokejoin(paint.join.round); /** 设置画笔的样式 paint.cap.round ,cap.square等分别为圆形、方形*/ paint.setstrokecap(paint.cap.round); /** 设置画笔的填充样式 paint.style.fill :填充内部;paint.style.fill_and_stroke :填充内部和描边; paint.style.stroke :仅描边*/ paint.setstyle(paint.style.stroke); /**抗锯齿功能*/ paint.setantialias(true); /**设置画笔宽度*/ paint.setstrokewidth(borderwidth); /**绘制圆弧的方法 * drawarc(rectf oval, float startangle, float sweepangle, boolean usecenter, paint paint)//画弧, 参数一是rectf对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧, 参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。 参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。 参数四是如果这是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果它是false(假)这将是一个弧线, 参数五是paint对象; */ canvas.drawarc(rectf, startangle, anglelength, false, paint); }
(2).【第二步】绘制当前进度的红色圆弧
/** * 2.绘制当前步数的红色圆弧 */ private void drawarcred(canvas canvas, rectf rectf) { paint paintcurrent = new paint(); paintcurrent.setstrokejoin(paint.join.round); paintcurrent.setstrokecap(paint.cap.round);//圆角弧度 paintcurrent.setstyle(paint.style.stroke);//设置填充样式 paintcurrent.setantialias(true);//抗锯齿功能 paintcurrent.setstrokewidth(borderwidth);//设置画笔宽度 paintcurrent.setcolor(getresources().getcolor(r.color.red));//设置画笔颜色 canvas.drawarc(rectf, startangle, currentanglelength, false, paintcurrent); }
(3).【第三步】绘制当前进度的红色数字
/** * 3.圆环中心的步数 */ private void drawtextnumber(canvas canvas, float centerx) { paint vtextpaint = new paint(); vtextpaint.settextalign(paint.align.center); vtextpaint.setantialias(true);//抗锯齿功能 vtextpaint.settextsize(numbertextsize); typeface font = typeface.create(typeface.sans_serif, typeface.normal); vtextpaint.settypeface(font);//字体风格 vtextpaint.setcolor(getresources().getcolor(r.color.red)); rect bounds_number = new rect(); vtextpaint.gettextbounds(stepnumber, 0, stepnumber.length(), bounds_number); canvas.drawtext(stepnumber, centerx, getheight() / 2 + bounds_number.height() / 2, vtextpaint); }
(4).【第四步】绘制”步数”的红色数字
/** * 4.圆环中心[步数]的文字 */ private void drawtextstepstring(canvas canvas, float centerx) { paint vtextpaint = new paint(); vtextpaint.settextsize(diptopx(16)); vtextpaint.settextalign(paint.align.center); vtextpaint.setantialias(true);//抗锯齿功能 vtextpaint.setcolor(getresources().getcolor(r.color.grey)); string stepstring = "步数"; rect bounds = new rect(); vtextpaint.gettextbounds(stepstring, 0, stepstring.length(), bounds); canvas.drawtext(stepstring, centerx, getheight() / 2 + bounds.height() + getfontheight(numbertextsize), vtextpaint); }
6.动画是如何实现的->valueanimator
valueanimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,而初始值和结束值之间的动画过渡就是由valueanimator这个类来负责计算的。它的内部使用一种时间循环的机制来计算值与值之间的动画过渡, 我们只需要将初始值和结束值提供给valueanimator,并且告诉它动画所需运行的时长,那么valueanimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。
/*为进度设置动画 * @param start 初始值 * @param current 结束值 * @param length 动画时长 */ private void setanimation(float start, float current, int length) { valueanimator progressanimator = valueanimator.offloat(start, current); progressanimator.setduration(length); progressanimator.settarget(currentanglelength); progressanimator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { /**每次在初始值和结束值之间产生的一个平滑过渡的值,逐步去更新进度*/ currentanglelength = (float) animation.getanimatedvalue(); invalidate(); } }); progressanimator.start(); }
7.整个自定义steparcview的源码
import android.animation.valueanimator; import android.content.context; import android.graphics.canvas; import android.graphics.paint; import android.graphics.rect; import android.graphics.rectf; import android.graphics.typeface; import android.util.attributeset; import android.view.view; import cn.bluemobi.dylan.step.r; /** * created by dylanandroid on 2016/5/26. * 显示步数的圆弧 */ public class steparcview extends view { /** * 圆弧的宽度 */ private float borderwidth = 38f; /** * 画步数的数值的字体大小 */ private float numbertextsize = 0; /** * 步数 */ private string stepnumber = "0"; /** * 开始绘制圆弧的角度 */ private float startangle = 135; /** * 终点对应的角度和起始点对应的角度的夹角 */ private float anglelength = 270; /** * 所要绘制的当前步数的红色圆弧终点到起点的夹角 */ private float currentanglelength = 0; /** * 动画时长 */ private int animationlength = 3000; public steparcview(context context) { super(context); } public steparcview(context context, attributeset attrs) { super(context, attrs); } public steparcview(context context, attributeset attrs, int defstyleattr) { super(context, attrs, defstyleattr); } @override protected void ondraw(canvas canvas) { super.ondraw(canvas); /**中心点的x坐标*/ float centerx = (getwidth()) / 2; /**指定圆弧的外轮廓矩形区域*/ rectf rectf = new rectf(0 + borderwidth, borderwidth, 2 * centerx - borderwidth, 2 * centerx - borderwidth); /**【第一步】绘制整体的黄色圆弧*/ drawarcyellow(canvas, rectf); /**【第二步】绘制当前进度的红色圆弧*/ drawarcred(canvas, rectf); /**【第三步】绘制当前进度的红色数字*/ drawtextnumber(canvas, centerx); /**【第四步】绘制"步数"的红色数字*/ drawtextstepstring(canvas, centerx); } /** * 1.绘制总步数的黄色圆弧 * * @param canvas 画笔 * @param rectf 参考的矩形 */ private void drawarcyellow(canvas canvas, rectf rectf) { paint paint = new paint(); /** 默认画笔颜色,黄色 */ paint.setcolor(getresources().getcolor(r.color.yellow)); /** 结合处为圆弧*/ paint.setstrokejoin(paint.join.round); /** 设置画笔的样式 paint.cap.round ,cap.square等分别为圆形、方形*/ paint.setstrokecap(paint.cap.round); /** 设置画笔的填充样式 paint.style.fill :填充内部;paint.style.fill_and_stroke :填充内部和描边; paint.style.stroke :仅描边*/ paint.setstyle(paint.style.stroke); /**抗锯齿功能*/ paint.setantialias(true); /**设置画笔宽度*/ paint.setstrokewidth(borderwidth); /**绘制圆弧的方法 * drawarc(rectf oval, float startangle, float sweepangle, boolean usecenter, paint paint)//画弧, 参数一是rectf对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧, 参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。 参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。 参数四是如果这是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果它是false(假)这将是一个弧线, 参数五是paint对象; */ canvas.drawarc(rectf, startangle, anglelength, false, paint); } /** * 2.绘制当前步数的红色圆弧 */ private void drawarcred(canvas canvas, rectf rectf) { paint paintcurrent = new paint(); paintcurrent.setstrokejoin(paint.join.round); paintcurrent.setstrokecap(paint.cap.round);//圆角弧度 paintcurrent.setstyle(paint.style.stroke);//设置填充样式 paintcurrent.setantialias(true);//抗锯齿功能 paintcurrent.setstrokewidth(borderwidth);//设置画笔宽度 paintcurrent.setcolor(getresources().getcolor(r.color.red));//设置画笔颜色 canvas.drawarc(rectf, startangle, currentanglelength, false, paintcurrent); } /** * 3.圆环中心的步数 */ private void drawtextnumber(canvas canvas, float centerx) { paint vtextpaint = new paint(); vtextpaint.settextalign(paint.align.center); vtextpaint.setantialias(true);//抗锯齿功能 vtextpaint.settextsize(numbertextsize); typeface font = typeface.create(typeface.sans_serif, typeface.normal); vtextpaint.settypeface(font);//字体风格 vtextpaint.setcolor(getresources().getcolor(r.color.red)); rect bounds_number = new rect(); vtextpaint.gettextbounds(stepnumber, 0, stepnumber.length(), bounds_number); canvas.drawtext(stepnumber, centerx, getheight() / 2 + bounds_number.height() / 2, vtextpaint); } /** * 4.圆环中心[步数]的文字 */ private void drawtextstepstring(canvas canvas, float centerx) { paint vtextpaint = new paint(); vtextpaint.settextsize(diptopx(16)); vtextpaint.settextalign(paint.align.center); vtextpaint.setantialias(true);//抗锯齿功能 vtextpaint.setcolor(getresources().getcolor(r.color.grey)); string stepstring = "步数"; rect bounds = new rect(); vtextpaint.gettextbounds(stepstring, 0, stepstring.length(), bounds); canvas.drawtext(stepstring, centerx, getheight() / 2 + bounds.height() + getfontheight(numbertextsize), vtextpaint); } /** * 获取当前步数的数字的高度 * * @param fontsize 字体大小 * @return 字体高度 */ public int getfontheight(float fontsize) { paint paint = new paint(); paint.settextsize(fontsize); rect bounds_number = new rect(); paint.gettextbounds(stepnumber, 0, stepnumber.length(), bounds_number); return bounds_number.height(); } /** * dip 转换成px * * @param dip * @return */ private int diptopx(float dip) { float density = getcontext().getresources().getdisplaymetrics().density; return (int) (dip * density + 0.5f * (dip >= 0 ? 1 : -1)); } /** * 所走的步数进度 * * @param totalstepnum 设置的步数 * @param currentcounts 所走步数 */ public void setcurrentcount(int totalstepnum, int currentcounts) { stepnumber = currentcounts + ""; settextsize(currentcounts); /**如果当前走的步数超过总步数则圆弧还是270度,不能成为园*/ if (currentcounts > totalstepnum) { currentcounts = totalstepnum; } /**所走步数占用总共步数的百分比*/ float scale = (float) currentcounts / totalstepnum; /**换算成弧度最后要到达的角度的长度-->弧长*/ float currentanglelength = scale * anglelength; /**开始执行动画*/ setanimation(0, currentanglelength, animationlength); } /** * 为进度设置动画 * valueanimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的, * 而初始值和结束值之间的动画过渡就是由valueanimator这个类来负责计算的。 * 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡, * 我们只需要将初始值和结束值提供给valueanimator,并且告诉它动画所需运行的时长, * 那么valueanimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。 * * @param last * @param current */ private void setanimation(float last, float current, int length) { valueanimator progressanimator = valueanimator.offloat(last, current); progressanimator.setduration(length); progressanimator.settarget(currentanglelength); progressanimator.addupdatelistener(new valueanimator.animatorupdatelistener() { @override public void onanimationupdate(valueanimator animation) { currentanglelength = (float) animation.getanimatedvalue(); invalidate(); } }); progressanimator.start(); } /** * 设置文本大小,防止步数特别大之后放不下,将字体大小动态设置 * * @param num */ public void settextsize(int num) { string s = string.valueof(num); int length = s.length(); if (length <= 4) { numbertextsize = diptopx(50); } else if (length > 4 && length <= 6) { numbertextsize = diptopx(40); } else if (length > 6 && length <= 8) { numbertextsize = diptopx(30); } else if (length > 8) { numbertextsize = diptopx(25); } } }
8.用法说明
xml中
<cn.bluemobi.dylan.step.view.steparcview android:id="@+id/sv " android:layout_width="200dp" android:layout_height="200dp" android:layout_centerhorizontal="true" android:layout_margintop="50dp" />
activity中
steparcview sv = (steparcview) findviewbyid(r.id.sv); sv.setcurrentcount(7000, 1000);
以上所述是小编给大家介绍的android 仿qq运动步数圆弧及动画效果,希望对大家有所帮助