您现在的位置是:首页 > 文章详情

Android进阶之自定义View(2)高仿钉钉运动步数实现可动的进度圆环(下)

日期:2018-08-08点击:168

接着上篇Android进阶之自定义控件(2)高仿钉钉运动步数实现可动的进度圆环(上)的基础,我们来实现钉钉运动的效果:

《一》View效果分析:

对钉钉运动的效果进行分析:
1、圆弧应该是从135°起,绘制了270°。
2、步数小于10000步时,背景圆弧为灰色,进度圆弧为蓝色渐变色;步数大于10000步时,进度圆弧为渐变的黄色
3、需要绘制中间的步数及步数上面的名次
4、实现步数及进度的动态变化


img_445f948ff9eeda54b7ef69ce88949e08.gif
dingding.gif

有了上篇的基础,实现钉钉运动步数的效果,就很简单了。还是先看一下我最终实现的高仿版效果,给大家点信心。变化的速度可以配置:


img_5bc54771437b86bd35ffcba0dd7e868d.gif
高仿钉钉运动效果.gif

《二》实现步骤分解:

(1)绘制灰色背景圆弧,135°起,绘制了270°
canvas.drawArc(oval, 135, 270, false, mPaint); 
(2)绘制进度圆弧
 canvas.drawArc(oval, 0 + 135, mCurrentStep/ mMaxStep * 270, false, mProgressPaint); 
(3)绘制文字很简单,只需要以正中间的文字为标准,往上移动一个textRect.height()和文字之间的间距textPadding即可。上篇这些基础的知识及需要注意的小细节都有详细的讲解,不多做解释。到此步骤,实现的效果如下:
 //绘制中间的步数的文字 Rect textRect = new Rect(); String mShowText = (int) mCurrentStep + ""; mTextPaint.setTextSize(sp2px(mTextSize)); mTextPaint.getTextBounds(mShowText, 0, mShowText.length(), textRect); canvas.drawText(mShowText, getWidth() / 2 - textRect.width() / 2, getHeight() / 2 + textRect.height() / 2, mTextPaint); //绘制排名的文字,在步数文字的上方 String mRandText = "第4名";//使用时,名次动态传入即可,此处写死为了测试 mTextPaint.setTextSize(sp2px(mRandTextSize)); mTextPaint.getTextBounds(mRandText, 0, mRandText.length(), textRect); canvas.drawText(mRandText, getWidth() / 2 - textRect.width() / 2, getHeight() / 2 + textRect.height() / 2 - (textRect.height() + textPadding), mTextPaint); 
img_9a6b19fb03602eb457fb23b653dc7c06.png
image.png
(4)可以看到,上图还是静态的效果,接下来我们通过两种方式来让进度圆弧和文字动起来。

1、方法一:还是上篇讲解过的方法,通过开分线程的方式,不断更新进度值,不断重绘。

 new Thread(new Runnable() { @Override public void run() { setCurrentStep(0); float changeProgress = currentStep; for (float i = 0; i < changeProgress; i++) { setCurrentStep(getCurrentStep() + rate); SystemClock.sleep(20); // invalidate();//invalidate()必须在主线程中执行,此处不能使用 // postInvalidate();//强制重绘,postInvalidate()可以在主线程也可以在分线程中执行 changeProgress = changeProgress - rate; } //由于上面的循环结束时,可能计算后最终无法到达mCurrentProgress的值,所以在循环结束后,将mCurrentProgress重新设置 setCurrentStep(currentStep); } }).start(); 

2、方法二:使用属性动画和差值器实现

 ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, currentStep); valueAnimator.setDuration(1000); valueAnimator.setInterpolator(new DecelerateInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentStep = (float) animation.getAnimatedValue(); setCurrentStep((int) currentStep); } }); valueAnimator.start(); 
(5)最后一步了,来看看渐变色的进度圆弧如何处理。

处理渐变有两个类:
1、线性渐变:LinearGradient
2、扫描式渐变:SweepGradient ——在中心点画一个扫描梯度的着色器
很明显,在这里我们需要用到SweepGradient来处理,如下,给画笔设置Shader,简单的设置如下。


img_2a1bc7e1de4e71eb96ff1e6edbe1ab3f.png
image.png
int[] mGradientColorArray = new int[]{ContextCompat.getColor(mContext, R.color.color_5e9fff), ContextCompat.getColor(mContext, R.color.color_4fd8f5), ContextCompat.getColor(mContext, R.color.color_5e9fff)}; SweepGradient shader = new SweepGradient(getWidth() / 2 - mRoundWidth / 2, getWidth() / 2 - mRoundWidth / 2, mGradientColorArray, null); mProgressPaint.setShader(shader); 
img_7aa4e07190511568a0ba5e6beab0f2b9.png
image.png

还是和钉钉的效果有点区别,钉钉的圆弧两边是浅色的,上面顶部的部分是深色的,我们稍作处理一下,代码如下:


img_d577e93e2020fcf75c7535f9acf16754.png
image.png
 /** * 处理渐变色 */ //渐变颜色数组 int[] mGradientColorArray = new int[]{ContextCompat.getColor(mContext, R.color.color_4fd8f5), ContextCompat.getColor(mContext, R.color.color_4fd8f5), ContextCompat.getColor(mContext, R.color.color_5e9fff), ContextCompat.getColor(mContext, R.color.color_4fd8f5)}; int count = mGradientColorArray.length; int[] colors = new int[count]; System.arraycopy(mGradientColorArray, 0, colors, 0, count); float[] positions = new float[count]; //由于此处绘制的是不完整的圆弧,故需要额外处理一下变化的比例值 float v = (360f / 270); positions[0] = 0.0f; positions[1] = 0.33f * v; positions[2] = 0.67f * v; positions[3] = 1.0f; SweepGradient shader = new SweepGradient(getWidth() / 2 - mRoundWidth / 2, getWidth() / 2 - mRoundWidth / 2, mGradientColorArray, positions); mProgressPaint.setShader(shader); 

渐变的效果搞定。


img_fba8ea14b6235ebb3a568b3ed5c5bf17.png
image.png

测试代码,在使用的地方调用startCountStep()即可,是不是敲极简单:

 //当前实际的步数 float mCurrentStep = 9709; sportStepView.startCountStep(mCurrentStep); 

View的完整代码:

package com.example.jojo.learn.customview; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.SweepGradient; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import android.util.AttributeSet; import android.util.TypedValue; import android.view.View; import android.view.animation.DecelerateInterpolator; import com.example.jojo.learn.R; /** * Created by JoJo on 2018/8/1. * wechat:18510829974 * description: 仿钉钉运动步数效果 */ public class SportStepView extends View { private Context mContext; //内圆环颜色 private int innerRoundColor; //外圆环颜色 private int outerRoundColor; //绘制背景圆环的画笔 private Paint mPaint; //绘制外面进度的圆环的画笔 private Paint mProgressPaint; //绘制外面进度的圆环的画笔 private Paint mTextPaint; //背景圆弧的绘制的宽度 private int mRoundWidth = 10; //进度圆环的宽度 private float mProgressRoundWidth = 15; //中间步数文字的大小 private int mTextSize = 40;//单位 sp //名次文字大小 private int mRandTextSize = 15; //圆环最大进度 private int mMaxStep = 10000; //圆环当前进度 private float mCurrentStep = 0; //排名与步数文字直接的间隔 private float textPadding = 80; //速度,值越大,变化速度越快 private float rate = 128; public SportStepView(Context context) { this(context, null); } public SportStepView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public SportStepView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); this.mContext = context; TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.SportStepView); innerRoundColor = typedArray.getColor(R.styleable.SportStepView_innerRoundColor, ContextCompat.getColor(mContext, R.color.color_e4e4e4)); outerRoundColor = typedArray.getColor(R.styleable.SportStepView_outerRoundColor, ContextCompat.getColor(mContext, R.color.color_4fd8f5)); init(); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //获取宽的模式 int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //获取宽的尺寸 int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); //对wrap_content这种模式进行处理 if (heightMode == MeasureSpec.AT_MOST) { heightSize = widthSize; } //以宽度为标准保存丈量结果 setMeasuredDimension(widthSize, heightSize); } private void init() { mPaint = new Paint(); mPaint.setAntiAlias(true); mPaint.setStyle(Paint.Style.STROKE); mPaint.setColor(innerRoundColor); mPaint.setStrokeCap(Paint.Cap.ROUND);// 圆形笔头 mPaint.setStrokeWidth(mRoundWidth); mProgressPaint = new Paint(); mProgressPaint.setAntiAlias(true);// 抗锯齿效果 mProgressPaint.setStyle(Paint.Style.STROKE); mProgressPaint.setColor(outerRoundColor); mProgressPaint.setStrokeCap(Paint.Cap.ROUND);// 圆形笔头 mProgressPaint.setStrokeWidth(mProgressRoundWidth); mTextPaint = new Paint(); mTextPaint.setAntiAlias(true);// 抗锯齿效果 mTextPaint.setStyle(Paint.Style.STROKE); mTextPaint.setColor(Color.BLACK); mTextPaint.setColor(ContextCompat.getColor(mContext, R.color.color_56a9ff)); mTextPaint.setTextSize(sp2px(mTextSize)); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); /** * 处理渐变色 */ //默认的渐变颜色数组: // int[] mGradientColorArray = new int[]{ContextCompat.getColor(mContext, R.color.color_5e9fff), ContextCompat.getColor(mContext, R.color.color_4fd8f5), ContextCompat.getColor(mContext, R.color.color_5e9fff)}; int[] mGradientColorArray = new int[]{ContextCompat.getColor(mContext, R.color.color_4fd8f5), ContextCompat.getColor(mContext, R.color.color_4fd8f5), ContextCompat.getColor(mContext, R.color.color_5e9fff), ContextCompat.getColor(mContext, R.color.color_4fd8f5)}; int count = mGradientColorArray.length; int[] colors = new int[count]; System.arraycopy(mGradientColorArray, 0, colors, 0, count); float[] positions = new float[count]; float v = (360f / 270); positions[0] = 0.0f; positions[1] = 0.33f * v; positions[2] = 0.67f * v; positions[3] = 1.0f; SweepGradient shader = new SweepGradient(getWidth() / 2 - mRoundWidth / 2, getWidth() / 2 - mRoundWidth / 2, mGradientColorArray, positions); mProgressPaint.setShader(shader); if (mRoundWidth < mProgressRoundWidth) { RectF oval = new RectF(0 + mProgressRoundWidth / 2, 0 + mProgressRoundWidth / 2, getWidth() - mProgressRoundWidth / 2, getWidth() - mProgressRoundWidth / 2); //绘制背景圆环 canvas.drawArc(oval, 135, 270, false, mPaint); //绘制进度圆环,绘制的角度最大不超过270° if (mCurrentStep * 1f / mMaxStep <= 1) { canvas.drawArc(oval, 0 + 135, mCurrentStep / mMaxStep * 270, false, mProgressPaint); } else { canvas.drawArc(oval, 0 + 135, 270, false, mProgressPaint); } } else { RectF oval = new RectF(0 + mRoundWidth / 2, 0 + mRoundWidth / 2, getWidth() - mRoundWidth / 2, getWidth() - mRoundWidth / 2); //绘制背景圆环 canvas.drawArc(oval, 135, 270, false, mPaint); //绘制进度圆环 if (mCurrentStep * 1f / mMaxStep <= 1) { canvas.drawArc(oval, 0 + 135, mCurrentStep / mMaxStep * 270, false, mProgressPaint); } else { canvas.drawArc(oval, 0 + 135, 270, false, mProgressPaint); } } //绘制中间的步数的文字 Rect textRect = new Rect(); String mShowText = (int) mCurrentStep + ""; mTextPaint.setTextSize( sp2px(mTextSize)); mTextPaint.getTextBounds(mShowText, 0, mShowText.length(), textRect); canvas.drawText(mShowText, getWidth() / 2 - textRect.width() / 2, getHeight() / 2 + textRect.height() / 2, mTextPaint); //绘制排名的文字,在步数文字的上方 String mRandText = "第4名"; mTextPaint.setTextSize( sp2px(mRandTextSize)); mTextPaint.getTextBounds(mRandText, 0, mRandText.length(), textRect); canvas.drawText(mRandText, getWidth() / 2 - textRect.width() / 2, getHeight() / 2 + textRect.height() / 2 - (textRect.height() + textPadding), mTextPaint); } public void setCurrentStep(float currentStep) { this.mCurrentStep = currentStep; //强制重绘,postInvalidate()可以在主线程也可以在分线程中执行 postInvalidate(); } public void setMaxProgress(int maxStep) { this.mMaxStep = maxStep; } public int getMaxtep() { return mMaxStep; } public float getCurrentStep() { return mCurrentStep; } /** * 将sp转换成px * * @param sp * @return */ private int sp2px(int sp) { return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, getResources().getDisplayMetrics()); } /** * 开始动态计步 * * @param currentStep */ public void startCountStep(final float currentStep) { //方法一:开一个分线程,动态改变进度的值,不断绘制达到进度变化的效果 // new Thread(new Runnable() { // @Override // public void run() { // setCurrentStep(0); // float changeProgress = currentStep; // for (float i = 0; i < changeProgress; i++) { // setCurrentStep(getCurrentStep() + rate); // SystemClock.sleep(20); //// invalidate();//invalidate()必须在主线程中执行,此处不能使用 //// postInvalidate();//强制重绘,postInvalidate()可以在主线程也可以在分线程中执行 // changeProgress = changeProgress - rate; // } // //由于上面的循环结束时,可能计算后最终无法到达mCurrentProgress的值,所以在循环结束后,将mCurrentProgress重新设置 // setCurrentStep(currentStep); // } // }).start(); /** * 方法二 */ ValueAnimator valueAnimator = ObjectAnimator.ofFloat(0, currentStep); valueAnimator.setDuration(1000); valueAnimator.setInterpolator(new DecelerateInterpolator()); valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { float currentStep = (float) animation.getAnimatedValue(); setCurrentStep((int) currentStep); } }); valueAnimator.start(); } } 

涉及到的自定义属性及color.xml

 <!--SportStepView--> <declare-styleable name="SportStepView"> <!--圆环半径--> <attr name="radius" format="dimension"></attr> <attr name="outerRoundColor" format="color"></attr> <attr name="innerRoundColor" format="color"></attr> </declare-styleable> 
 <color name="color_4fd8f5">#4fd8f5</color> <color name="color_5e9fff">#5e9fff</color> <color name="color_e4e4e4">#e4e4e4</color> 
原文链接:https://yq.aliyun.com/articles/638338
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章