【Android自定义View实战之仿QQ运动步数圆弧及动画,Dylan计步中的控件StepArcView】幽映每白日,清辉照衣裳。这篇文章主要讲述Android自定义View实战之仿QQ运动步数圆弧及动画,Dylan计步中的控件StepArcView相关的知识,希望能为你提供帮助。
转载请注明出处: http://blog.csdn.net/linglongxin24/article/details/52936609 【Dylanandroid的csdn博客】
在之前的Android超精准计步器开发-Dylan计步中的首页用到了一个自定义控件, 和QQ运动的界面有点类似, 还有动画效果, 下面就来讲一下这个View是如何绘制的。1.先看效果图
文章图片
2.效果图分析
- 功能说明: 黄色的代表用户设置的总计划锻炼步数, 红色的代表用户当前所走的步数。
- 初步分析: 完全自定义View重写onDraw()方法, 画圆弧。
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中
StepArcViewsv =
(StepArcView) findViewById(R.id.sv);
sv.setCurrentCount(7000, 1000);
推荐阅读
- Eclipse Android开发环境的搭建步骤及注意事项
- Android service进程保护
- Android自动化测试--Espresso框架使用
- android activity中监听View测量完成的4种方式
- 理解boot.img与逆向分析Android/linux内核
- 「android」as过滤svn文件
- [函數] Firemonkey Android 取得系统参数设定的字型大小
- 安卓系统环境的搭建及框架介绍
- 亚马逊面试题分享|S89