欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  移动技术

Android 自定View实现仿QQ运动步数圆弧及动画效果

程序员文章站 2024-03-04 13:40:11
在之前的android超精准计步器开发-dylan计步中的首页用到了一个自定义控件,和qq运动的界面有点类似,还有动画效果,下面就来讲一下这个view是如何绘制的。 1....

在之前的android超精准计步器开发-dylan计步中的首页用到了一个自定义控件,和qq运动的界面有点类似,还有动画效果,下面就来讲一下这个view是如何绘制的。

1.先看效果图

Android 自定View实现仿QQ运动步数圆弧及动画效果

2.效果图分析

功能说明:黄色的代表用户设置的总计划锻炼步数,红色的代表用户当前所走的步数。

初步分析:完全自定义view重写ondraw()方法,画圆弧。

3.画一个圆弧必备知识

在canvas中有一个画圆弧的方法

drawarc(rectf oval, float startangle, float sweepangle, boolean usecenter, paint paint)//画弧,

参数一是rectf对象,一个矩形区域椭圆形的界限用于定义在形状、大小、电弧,

参数二是起始角(度)在电弧的开始,圆弧起始角度,单位为度。

参数三圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。

参数四是如果是true(真)的话,在绘制圆弧时将圆心包括在内,通常用来绘制扇形;如果是false(假)这将是一个弧线。

参数五是paint对象;

对于这个方法,大家可以看一下我手绘的草图,比较烂,表达一下这几个参数的意思和绘制过程,画得不好望大家见谅!

Android 自定View实现仿QQ运动步数圆弧及动画效果

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运动步数圆弧及动画效果,希望对大家有所帮助