博主声明:

转载请在开头附加本文链接及作者信息,并标记为转载。本文由博主 威威喵 原创,请多支持与指教。

本文首发于此   博主:威威喵  |  博客主页:https://blog.csdn.net/smile_running

今天在打开 QQ 的时候,我本人一般都是不怎么去看消息的,除了各大群里面的消息刷屏,也很少聊天。其实,我个人不太喜欢在网络上聊天,除了有什么事情要说,那倒是情有可原。如果就闲聊、扯淡的话,那等到有时间,几个人坐下来喝喝茶,聊聊天不是更好嘛。虽然我高中也喜欢聊天,到大学了,就真的很少和别人在网络上聊天了。也许是年龄越来越大了,融入不了小年轻的世界啦,哈哈,我 TM 才 21 岁啊,老了老了。。。

好了,瞎扯淡的话就到这里。也许是我今天走了挺多路程的,不知咋地了,QQ 运动忽然对我说 “今天状态爆表”,我赶紧点进去看了一下,不知不觉今天都走了 2W 多步了,不过就是脚有点酸。我又点进去看了一下,咦,这个步数的动画效果很不错啊。之前虽然也看过了,但都没在意,今天突然来兴趣了,总想试试如何实现这个效果。那么,说干就干吧。

首先呢,QQ 步数动画是这样一个效果,它的外圈是一条弧线,中间是步数的值,随着这个值的增加,那么弧线的弧度也会随之递增,直接来看我实现的效果图吧,更加形象:

好了,就是这样的一个效果,在很多 app 里面都很常见的,好像支付宝的钱钱动画也是类似的效果,一个值会在变化,直到值为最终结果为止。那么我们接下来看看如何实现这个效果吧。

首先呢,这肯定是一个自定义 View,我们需要获取有它的一个步数值、最大步数值、弧线、弧线的宽度、不同的颜色值等这几个属性,那我们就可以将它定义为一个 attrs.xml 文件,在 View 标签中用属性来设置值。第一步,在 res — value 下新建一个 attrs.xml 文件,其中代码如下:

<?xml version="1.0" encoding="utf-8"?>
<resources><declare-styleable name="QQStepsView"><!-- 步数文本的颜色 --><attr name="step_text_color" format="color" /><!-- 步数文本字体的大小 --><attr name="step_text_size" format="dimension" /><!-- 步数文本字体的大小 --><attr name="step_text_spacing" format="float" /><!-- 步数的最大值 --><attr name="step_max_value" format="float" /><!-- 弧的颜色 --><attr name="step_arc_color" format="color" /><!-- 进度的颜色 --><attr name="step_progress_color" format="color" /><!-- 弧的宽度 --><attr name="step_arc_width" format="dimension" /><!-- 弧开始的角度 --><attr name="arc_start_angle" format="float" /><!-- 弧开始的角度 --><attr name="arc_sweep_angle" format="float" /></declare-styleable>
</resources>

上面代码我都标了属性作用,也就不要我再解释了吧。然后要在哪里获取这些自定义属性呢?肯定是在我们的自定义 View 里面,我们继承 View 时都会默认添加三个构造函数,就是在构造函数里面去获取这些值的。

    public QQStepsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initAttrs(context, attrs);init();}private void initAttrs(Context context, AttributeSet attrs) {if (attrs != null) {TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QQStepsView);mArcColor = array.getColor(R.styleable.QQStepsView_step_arc_color, Color.LTGRAY);mStepsColor = array.getColor(R.styleable.QQStepsView_step_progress_color, Color.RED);mTextColor = array.getColor(R.styleable.QQStepsView_step_text_color, Color.RED);mStartAngle = array.getFloat(R.styleable.QQStepsView_arc_start_angle, -225f);mSweepAngle = array.getFloat(R.styleable.QQStepsView_arc_start_angle, 270f);mStepsWidth = array.getFloat(R.styleable.QQStepsView_step_arc_width, dp2px(10f));mTextSize = array.getDimension(R.styleable.QQStepsView_step_text_size, dp2px(28f));mTextSpacing = array.getFloat(R.styleable.QQStepsView_step_text_spacing, dp2px(0.02f));mMaxSteps = array.getFloat(R.styleable.QQStepsView_step_max_value, 20000f);array.recycle();}}

下面的话,就要去测量 View 的宽和高了。根据这种情况,我们 View 的宽和高要一致才行,不然的话圆弧会变成椭圆的弧,就不好看了,代码如下:

    @SuppressLint("DrawAllocation")@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST || MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {mWidth = mHeight = 200;} else {mWidth = MeasureSpec.getSize(widthMeasureSpec);mHeight = MeasureSpec.getSize(heightMeasureSpec);if (mWidth != mHeight) {mHeight = mWidth = Math.min(mWidth, mHeight);}}mCenterX = mWidth / 2;mCenterY = mHeight / 2;float paintSize = mArcPaint.getStrokeWidth() / 2;mRect = new RectF(paintSize, paintSize, mWidth - paintSize, mHeight - paintSize);}

最后,就是绘制图像了。一个是弧线的绘制,一个是步数文本的绘制,这部分没啥好解释的,就是计算一下坐标而已,代码如下:

    private void drawSteps(Canvas canvas) {// 绘制空的弧canvas.drawArc(mRect, mStartAngle, mSweepAngle, false, mArcPaint);//绘制步数的弧mStepScale = mSteps / mMaxSteps;canvas.drawArc(mRect, mStartAngle, mStepScale * mSweepAngle, false, mStepsPaint);}private void drawStepsText(Canvas canvas) {String steps = String.valueOf(mSteps);mBounds = new Rect();mStepsTextPaint.getTextBounds(steps, 0, steps.length(), mBounds);Paint.FontMetrics metrics = mStepsTextPaint.getFontMetrics();canvas.drawText(steps, mCenterX - mBounds.width() / 2, mCenterY - (metrics.top + metrics.bottom) / 2, mStepsTextPaint);}

这样的话,这 QQ 步数效果所需的关键性代码都写完了,那我直接放出所以代码:

package nd.no.xww.qqmessagedragview;import android.annotation.SuppressLint;
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.Typeface;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;/*** @author xww* @desciption : QQ 步数动画效果* @date 2018/7/26* @time 16:48* @威威喵*/
public class QQStepsView extends View {private Paint mArcPaint;private Paint mStepsPaint;private Paint mStepsTextPaint;private float mStepsWidth;private float mStartAngle;private float mSweepAngle;private int mTextColor;private int mArcColor;private int mStepsColor;private float mTextSize;private float mTextSpacing;private int mSteps;private float mMaxSteps;private float mStepScale;private RectF mRect;private Rect mBounds;private float mWidth;private float mHeight;private float mCenterX;private float mCenterY;private void init() {initPaint();}private void initAttrs(Context context, AttributeSet attrs) {if (attrs != null) {TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.QQStepsView);mArcColor = array.getColor(R.styleable.QQStepsView_step_arc_color, Color.LTGRAY);mStepsColor = array.getColor(R.styleable.QQStepsView_step_progress_color, Color.RED);mTextColor = array.getColor(R.styleable.QQStepsView_step_text_color, Color.RED);mStartAngle = array.getFloat(R.styleable.QQStepsView_arc_start_angle, -225f);mSweepAngle = array.getFloat(R.styleable.QQStepsView_arc_start_angle, 270f);mStepsWidth = array.getDimension(R.styleable.QQStepsView_step_arc_width, dp2px(10f));mTextSize = array.getDimension(R.styleable.QQStepsView_step_text_size, dp2px(28f));mTextSpacing = array.getFloat(R.styleable.QQStepsView_step_text_spacing, dp2px(0.02f));mMaxSteps = array.getFloat(R.styleable.QQStepsView_step_max_value, 20000f);array.recycle();}}private void initPaint() {mArcPaint = getPaint(mArcColor);mStepsPaint = getPaint(mStepsColor);mStepsTextPaint = getPaint(mTextColor);mStepsTextPaint.setStyle(Paint.Style.FILL);mStepsTextPaint.setTextSize(mTextSize);mStepsTextPaint.setLetterSpacing(mTextSpacing);mStepsTextPaint.setTypeface(Typeface.MONOSPACE);}private Paint getPaint(int color) {Paint paint = new Paint();paint.setDither(true);paint.setStyle(Paint.Style.STROKE);paint.setStrokeWidth(mStepsWidth);paint.setAntiAlias(true);paint.setColor(color);//设置弧度两端为圆滑paint.setStrokeCap(Paint.Cap.ROUND);return paint;}public QQStepsView(Context context) {this(context, null);}public QQStepsView(Context context, @Nullable AttributeSet attrs) {this(context, attrs, 0);}public QQStepsView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);initAttrs(context, attrs);init();}@SuppressLint("DrawAllocation")@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);if (MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST || MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.AT_MOST) {mWidth = mHeight = 200;} else {mWidth = MeasureSpec.getSize(widthMeasureSpec);mHeight = MeasureSpec.getSize(heightMeasureSpec);if (mWidth != mHeight) {mHeight = mWidth = Math.min(mWidth, mHeight);}}mCenterX = mWidth / 2;mCenterY = mHeight / 2;float paintSize = mArcPaint.getStrokeWidth() / 2;mRect = new RectF(paintSize, paintSize, mWidth - paintSize, mHeight - paintSize);}@Overrideprotected void onDraw(Canvas canvas) {drawSteps(canvas);drawStepsText(canvas);}private void drawSteps(Canvas canvas) {// 绘制空的弧canvas.drawArc(mRect, mStartAngle, mSweepAngle, false, mArcPaint);//绘制步数的弧mStepScale = mSteps / mMaxSteps;canvas.drawArc(mRect, mStartAngle, mStepScale * mSweepAngle, false, mStepsPaint);}private void drawStepsText(Canvas canvas) {String steps = String.valueOf(mSteps);mBounds = new Rect();mStepsTextPaint.getTextBounds(steps, 0, steps.length(), mBounds);Paint.FontMetrics metrics = mStepsTextPaint.getFontMetrics();canvas.drawText(steps, mCenterX - mBounds.width() / 2, mCenterY - (metrics.top + metrics.bottom) / 2, mStepsTextPaint);}public void setSteps(int steps) {this.mSteps = steps;invalidate();}public void setMaxSteps(float maxSteps) {this.mMaxSteps = maxSteps;}private float dp2px(float dp) {return dp * getResources().getDisplayMetrics().density;}
}

上面就是完整代码,那使用起来也非常简单,比如你可以这样:

    <nd.no.xww.qqmessagedragview.QQStepsViewandroid:id="@+id/qqstepsview"android:layout_width="200dp"app:step_arc_color="#D15FEE"app:step_max_value="40000"app:step_progress_color="#FF83FA"app:step_text_color="#B23AEE"app:step_text_size="25sp"android:layout_height="200dp"android:layout_gravity="center" />

这里要记得添加一下 app 的一个命名空间,否则是出现不了我们自定义的属性的。这个命名空间的话,在上一层 View 下打入 app 就会跑出来啦。最后,也是至关重要的一步,动画效果的设定,上面仅仅是一个静态的 View,要想让它动起来,还需要加入以下代码方可呈现动画:

        qqStepsView = findViewById(R.id.qqstepsview);ValueAnimator valueAnimator = ObjectAnimator.ofInt(0, 26666);valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {qqStepsView.setSteps((int) animation.getAnimatedValue());}});valueAnimator.setDuration(2000);valueAnimator.setInterpolator(new AccelerateInterpolator());valueAnimator.start();

解释一下上面的代码,在 MainActivity 中绑定一下 id 拿到实例,然后给它设置一个值动画,给这个动画添加一个值的监听,它会在上面设置的 Duration(2S)内从 0 变化到 26666 的值,然后我们设置一个步数就好了,那么 View 就会一直跟随着它的变化直到动画结束。

切换一下颜色和字体大小等,都非常的简单,像使用 TextView 一样,看看效果

好了,这样就可以完成一个效果很不错的动画了,所以你在学会了这种值变化动画后,对于那些支付宝金额、QQ步数等一些数值变化的动画还不是手到擒来。

自定义 View 之仿 QQ 步数变动动画效果相关推荐

  1. Android自定义View之仿QQ运动步数进度效果

    文章目录 前言 先看效果图 ![在这里插入图片描述](https://img-blog.csdnimg.cn/6e4ddec17933496ea4830fa08d8ffbe5.png?x-oss-pr ...

  2. 自定义View之仿QQ运动步数进度效果

    前言 今天接着上一篇来写关于自定义View方面的东西,我是近期在学习整理这方面的知识点,所以把相关的笔记都放到这个Android自定义View的专栏里了,方便自己下次忘记的时候能回来翻翻,今天的内容是 ...

  3. android 自定义view实现仿QQ运动步数进度效果

    最近公司在策划一个新的项目,原型还没出来,再说这公司人都要走没了,估计又要找工作了,所以必须要学习,争取每个写个关于自定义view方面的,这样几个月积累下来,也能学习到东西,今天就带来简单的效果,就是 ...

  4. Android自定义View实现仿QQ实现运动步数效果

    效果图: 1.attrs.xml中 <declare-styleable name="QQStepView"><attr name="outerColo ...

  5. Android自定义View之仿QQ侧滑菜单实现

    最近,由于正在做的一个应用中要用到侧滑菜单,所以通过查资料看视频,学习了一下自定义View,实现一个类似于QQ的侧滑菜单,顺便还将其封装为自定义组件,可以实现类似QQ的侧滑菜单和抽屉式侧滑菜单两种菜单 ...

  6. Android自定义View分享——仿网易云音乐留声机效果

    写在前面 这是笔者自学习自定义View以来,分享的第五篇效果,之前分享过一篇动态时钟效果的自定义View,如果有兴趣的可以看看: Android自定义View分享--一个时钟 之前的博客笔者一般都会说 ...

  7. android幻灯片效果自定义,Android自定义View实现仿网易音乐唱片播放效果

    本文实例为大家分享了Android实现仿网易音乐唱片播放效果的具体代码,供大家参考,具体内容如下 效果图: 在values中创建attrs.xml文件 //中间图片的半径 //图片 //唱片旋转的速度 ...

  8. <android>音乐频谱显示效果 音乐播放动画 自定义view Visualizer 对接MediaPlayer 声音频率 动画效果

    最近写了一个音乐频谱显示效果的自定义view,通过Visualizer 函数对接了MediaPlayer的声源byte数据的回调,全部封装到了view的里面,外部只需要设置一个MediaPlayer即 ...

  9. 04.自定义View(SlidingView仿QQ侧滑)

    感谢红橙Darren博主 布局文件中 <?xml version="1.0" encoding="utf-8"?> <LinearLayout ...

最新文章

  1. Matplotlib-动态更新图表
  2. 冷却水的循环方式有哪几种_一种清洁环保高效的方法处理工业循环冷却水
  3. centos solr 部署到 tomcat 上
  4. spring @Order注解
  5. 熟悉java环境实验报告_2018-2019-2 20175324实验二面向对象程序设计《Java开发环境的熟悉》实验报告...
  6. JavaScript --- 取得鼠标事件的坐标
  7. 【es】ElasticSearch 插件开发
  8. 人脸对齐(四)--CLM算法及概率图模型改进
  9. ANSYS模态分析详细步骤记录
  10. android开发apk捆绑,Android 下 APK 捆绑器的实现
  11. 【常用办公软件】万彩办公大师教程丨全能图片转换工具
  12. 无线键鼠接收器配对怎么就那么难?简直就是浪费
  13. windows linux 共存,Windows与Linux共存
  14. ElasticSearch Java 客户端连接ElasticSearch
  15. r7 6700g核显相当于什么显卡 锐龙r76700g性能怎么样
  16. python scapy2.3 在windows上的安装
  17. 关于笔记本连接显示器检测不到的问题(NoVideoInput)
  18. VM虚拟机 .vmdk文件,拆分多个文件,合并单个文件
  19. 基本概念 - 二维关系表
  20. 攻防世界crypto进阶区--告诉你个秘密

热门文章

  1. CodeForces 997C Sky Full of Stars
  2. 关于集成墙板商家营销的十个禁忌,你都懂吗?
  3. 鼎捷软件MOM解决方案助力汽车零部件企业实现全制程品质追溯
  4. 微信小程序使用canvas画不规则图形
  5. 关于Cisco Packet Tracer中PAT配置
  6. 游戏角色——龙皇后制作曝光,大师级作品,值得膜拜
  7. ECharts插件的基本使用
  8. CAD打印PDF后页边距太大怎么办?CAD打印PDF设置技巧
  9. 微型计算机接口2018年4月,全国2018年4月自考微型计算机及接口技术04732试题和答案(最后)...
  10. python中open()方法总结