Android自定义View和属性动画完美结合,创造出酷炫圆环动画,带标尺和进度

本文主要是介绍Android自定义View和属性动画完美结合,创造出酷炫圆环动画,带标尺和进度,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

转载请注明出处:http://blog.csdn.net/linglongxin24/article/details/53173288
本文出自【DylanAndroid的博客】


Android自定义View和属性动画完美结合,创造出酷炫圆环动画,带标尺和进度

无意中,在看了【Android自定义View实战】之仿QQ运动步数圆弧及动画,Dylan计步中的控件StepArcView 这个之后,突然发奇想,想做这么一个图。在原来的基础上增加一些东西,这样会更好一点。内容更丰富。

主要是在原来的基础上添加了如下功能

  • 1.进度圆环的颜色是渐变。
  • 2.添加一个进度标尺,类似与钟表表盘的样子,用来显示刻度。
  • 3.添加一个进度指示器,三角形的样子,用来显示进度。

    效果图如下

效果图

下面,就针对这三个变化来说明一下:

一.渐变的圆环颜色

        /*** 设置圆形渐变* 【第一个参数】:中心点x坐标* 【第二个参数】:中心点y坐标* 【第三个参数】:渐变的颜色数组* 【第四个参数】:渐变的颜色数组对应的相对位置*/paintCurrent.setShader(new SweepGradient(centerX, centerX, new int[]{getResources().getColor(R.color.start_color), getResources().getColor(R.color.end_color)}, null));

二.画三角形进度指示器

  • 1.拿到一个资源图片对象
        /*** 通过这个拿到一个资源图片对象*/bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.arrow);
  • 2.计算三角形移动轨迹的坐标

     /*** 为进度设置动画* ValueAnimator是整个属性动画机制当中最核心的一个类,属性动画的运行机制是通过不断地对值进行操作来实现的,* 而初始值和结束值之间的动画过渡就是由ValueAnimator这个类来负责计算的。* 它的内部使用一种时间循环的机制来计算值与值之间的动画过渡,* 我们只需要将初始值和结束值提供给ValueAnimator,并且告诉它动画所需运行的时长,* 那么ValueAnimator就会自动帮我们完成从初始值平滑地过渡到结束值这样的效果。** @param last* @param current*/@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {/**每次要绘制的圆弧角度**/currentAngleLength = (float) animation.getAnimatedValue();/**要绘制的三角形指示器的半径**/float radius=centerX - borderWidth-bitmap.getHeight();/**要绘制的三角形指示器的x坐标**/point.x = (float) (centerX +radius * Math.cos((startAngle + currentAngleLength) * Math.PI / 180));/**要绘制的三角形指示器的y坐标**/point.y = (float) (centerX + radius* Math.sin((startAngle + currentAngleLength) * Math.PI / 180));Log.d("stepView", point + "");/**要绘制的圆弧多绘制的部分减掉**/double subtractionScale = borderWidth/2/(centerX*2*Math.PI);double subtractionAngle=subtractionScale*angleLength;if(currentAngleLength>subtractionAngle){currentAngleLength-=subtractionAngle;}invalidate();}});progressAnimator.start();}
  • 3.调整旋转的角度后绘制三角形指示器

    /*** 5.画三角形** @param canvas*/private void drawBitmap(Canvas canvas) {// 定义矩阵对象Matrix matrix = new Matrix();// 参数为正则向右旋转matrix.postRotate(startAngle + currentAngleLength + 90);Bitmap dstbmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),matrix, true);Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);//这里不管怎么设置都不影响最终图像canvas.drawBitmap(dstbmp, point.x - dstbmp.getWidth() / 2, point.y - dstbmp.getHeight() / 2, mBitmapPaint);}

    三.绘制进度表尺的钟表仪表盘

     /*** 6. 画钟表线** @param canvas*/private void drawLine(Canvas canvas) {Paint mPaint = new Paint();mPaint.setStrokeWidth(5);mPaint.setColor(getResources().getColor(R.color.start_color));/**要绘制的表盘线的总数**/int count = 60;/**要绘制的表盘每个间隔线条之间的夹角**/int avgAngle = (360 / (count - 1));/**要绘制的表盘的最长的半径**/float radius = centerX - borderWidth - bitmap.getHeight() - 20;/**要绘制的表盘线条长度**/int lineLength = 25;/**起始点**/PointF point1 = new PointF();/**终止点**/PointF point2 = new PointF();for (int i = 0; i < count; i++) {int angle = avgAngle * i;/**起始点坐标**/point1.x = centerX + (float) Math.cos(angle * (Math.PI / 180)) * radius;point1.y = centerX + (float) Math.sin(angle * (Math.PI / 180)) * radius;/**终止点坐标**/point2.x = centerX + (float) Math.cos(angle * (Math.PI / 180)) * (radius - lineLength);point2.y = centerX + (float) Math.sin(angle * (Math.PI / 180)) * (radius - lineLength);/**画线**/canvas.drawLine(point1.x, point1.y, point2.x, point2.y, mPaint);}}
    

    四.完整代码

    package cn.bluemobi.dylan.stepcirclestaffview;/**
    * Created by yuandl on 2016-11-08.
    */import android.animation.ValueAnimator;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.BitmapFactory;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.LinearGradient;
    import android.graphics.Matrix;
    import android.graphics.Paint;
    import android.graphics.PointF;
    import android.graphics.RadialGradient;
    import android.graphics.Rect;
    import android.graphics.RectF;
    import android.graphics.Shader;
    import android.graphics.SweepGradient;
    import android.graphics.Typeface;
    import android.os.Build;
    import android.support.annotation.RequiresApi;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.View;import java.util.logging.Logger;/**
    * 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 = 90;/*** 终点对应的角度和起始点对应的角度的夹角*/private float angleLength = 360;/*** 所要绘制的当前步数的红色圆弧终点到起点的夹角*/private float currentAngleLength = 0;/*** 动画时长*/private int animationLength = 3000;private PointF point;private float centerX;private Bitmap bitmap;private int totalStepNum;private void init() {point = new PointF();/*** 通过这个拿到一个资源图片对象*/bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.arrow);}public StepArcView(Context context) {super(context);init();}public StepArcView(Context context, AttributeSet attrs) {super(context, attrs);init();}public StepArcView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init();}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);/**中心点的x坐标*/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);/**【第五步】绘制"步数"进度标尺的三角形*/drawBitmap(canvas);/**【第六步】绘制"步数"进度标尺类似于钟表线隔*/drawLine(canvas);}/*** 1.绘制总步数的黄色圆弧** @param canvas 画笔* @param rectF  参考的矩形*/private void drawArcYellow(Canvas canvas, RectF rectF) {Paint paint = new Paint();/** 默认画笔颜色,黄色 */paint.setColor(getResources().getColor(R.color.default_color));/** 结合处为圆弧*/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.SQUARE);//圆角弧度paintCurrent.setStyle(Paint.Style.STROKE);//设置填充样式paintCurrent.setAntiAlias(true);//抗锯齿功能paintCurrent.setStrokeWidth(borderWidth);//设置画笔宽度/*** 设置圆形渐变* 【第一个参数】:中心点x坐标* 【第二个参数】:中心点y坐标* 【第三个参数】:渐变的颜色数组* 【第四个参数】:渐变的颜色数组对应的相对位置*/paintCurrent.setShader(new SweepGradient(centerX, centerX, new int[]{getResources().getColor(R.color.start_color), getResources().getColor(R.color.end_color)}, null));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.center_text_color));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(13));vTextPaint.setTextAlign(Paint.Align.CENTER);vTextPaint.setAntiAlias(true);//抗锯齿功能vTextPaint.setColor(getResources().getColor(R.color.other_text_color));String stepString = "目标 "+totalStepNum;Rect bounds = new Rect();vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);canvas.drawText(stepString, centerX, getHeight() / 2 + bounds.height() + getFontHeight(numberTextSize), vTextPaint);canvas.save();stepString = "今天步数";bounds = new Rect();vTextPaint.getTextBounds(stepString, 0, stepString.length(), bounds);canvas.drawText(stepString, centerX, getHeight() / 2  - getFontHeight(numberTextSize), vTextPaint);}/*** 5.画三角形** @param canvas*/private void drawBitmap(Canvas canvas) {// 定义矩阵对象Matrix matrix = new Matrix();// 参数为正则向右旋转matrix.postRotate(startAngle + currentAngleLength + 90);Bitmap dstbmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(),matrix, true);Paint mBitmapPaint = new Paint(Paint.DITHER_FLAG);//这里不管怎么设置都不影响最终图像canvas.drawBitmap(dstbmp, point.x - dstbmp.getWidth() / 2, point.y - dstbmp.getHeight() / 2, mBitmapPaint);}/*** 6. 画钟表线** @param canvas*/private void drawLine(Canvas canvas) {Paint mPaint = new Paint();mPaint.setStrokeWidth(5);mPaint.setColor(getResources().getColor(R.color.start_color));/**要绘制的表盘线的总数**/int count = 60;/**要绘制的表盘每个间隔线条之间的夹角**/int avgAngle = (360 / (count - 1));/**要绘制的表盘的最长的半径**/float radius = centerX - borderWidth - bitmap.getHeight() - 20;/**要绘制的表盘线条长度**/int lineLength = 25;/**起始点**/PointF point1 = new PointF();/**终止点**/PointF point2 = new PointF();for (int i = 0; i < count; i++) {int angle = avgAngle * i;/**起始点坐标**/point1.x = centerX + (float) Math.cos(angle * (Math.PI / 180)) * radius;point1.y = centerX + (float) Math.sin(angle * (Math.PI / 180)) * radius;/**终止点坐标**/point2.x = centerX + (float) Math.cos(angle * (Math.PI / 180)) * (radius - lineLength);point2.y = centerX + (float) Math.sin(angle * (Math.PI / 180)) * (radius - lineLength);/**画线**/canvas.drawLine(point1.x, point1.y, point2.x, point2.y, mPaint);}}/*** 获取当前步数的数字的高度** @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 所走步数*/@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)public void setCurrentCount(int totalStepNum, int currentCounts) {this.totalStepNum = totalStepNum;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*/@RequiresApi(api = Build.VERSION_CODES.HONEYCOMB)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() {@Overridepublic void onAnimationUpdate(ValueAnimator animation) {/**每次要绘制的圆弧角度**/currentAngleLength = (float) animation.getAnimatedValue();/**要绘制的三角形指示器的半径**/float radius=centerX - borderWidth-bitmap.getHeight();/**要绘制的三角形指示器的x坐标**/point.x = (float) (centerX +radius * Math.cos((startAngle + currentAngleLength) * Math.PI / 180));/**要绘制的三角形指示器的y坐标**/point.y = (float) (centerX + radius* Math.sin((startAngle + currentAngleLength) * Math.PI / 180));Log.d("stepView", point + "");/**要绘制的圆弧多绘制的部分减掉**/double subtractionScale = borderWidth/2/(centerX*2*Math.PI);double subtractionAngle=subtractionScale*angleLength;if(currentAngleLength>subtractionAngle){currentAngleLength-=subtractionAngle;}invalidate();}});progressAnimator.start();}/*** 设置文本大小,防止步数特别大之后放不下,将字体大小动态设置** @param num*/public void setTextSize(int num) {String s = String.valueOf(num);int length = s.length();if (length <= 4) {numberTextSize = dipToPx(40);} else if (length > 4 && length <= 6) {numberTextSize = dipToPx(30);} else if (length > 6 && length <= 8) {numberTextSize = dipToPx(25);} else if (length > 8) {numberTextSize = dipToPx(20);}}}
    

    五.GitHub

这篇关于Android自定义View和属性动画完美结合,创造出酷炫圆环动画,带标尺和进度的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/410048

相关文章

Java如何通过反射机制获取数据类对象的属性及方法

《Java如何通过反射机制获取数据类对象的属性及方法》文章介绍了如何使用Java反射机制获取类对象的所有属性及其对应的get、set方法,以及如何通过反射机制实现类对象的实例化,感兴趣的朋友跟随小编一... 目录一、通过反射机制获取类对象的所有属性以及相应的get、set方法1.遍历类对象的所有属性2.获取

Python结合requests和Cheerio处理网页内容的操作步骤

《Python结合requests和Cheerio处理网页内容的操作步骤》Python因其简洁明了的语法和强大的库支持,成为了编写爬虫程序的首选语言之一,requests库是Python中用于发送HT... 目录一、前言二、环境搭建三、requests库的基本使用四、Cheerio库的基本使用五、结合req

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

基于Python实现PDF动画翻页效果的阅读器

《基于Python实现PDF动画翻页效果的阅读器》在这篇博客中,我们将深入分析一个基于wxPython实现的PDF阅读器程序,该程序支持加载PDF文件并显示页面内容,同时支持页面切换动画效果,文中有详... 目录全部代码代码结构初始化 UI 界面加载 PDF 文件显示 PDF 页面页面切换动画运行效果总结主

Android数据库Room的实际使用过程总结

《Android数据库Room的实际使用过程总结》这篇文章主要给大家介绍了关于Android数据库Room的实际使用过程,详细介绍了如何创建实体类、数据访问对象(DAO)和数据库抽象类,需要的朋友可以... 目录前言一、Room的基本使用1.项目配置2.创建实体类(Entity)3.创建数据访问对象(DAO

如何用Java结合经纬度位置计算目标点的日出日落时间详解

《如何用Java结合经纬度位置计算目标点的日出日落时间详解》这篇文章主详细讲解了如何基于目标点的经纬度计算日出日落时间,提供了在线API和Java库两种计算方法,并通过实际案例展示了其应用,需要的朋友... 目录前言一、应用示例1、天安门升旗时间2、湖南省日出日落信息二、Java日出日落计算1、在线API2

SpringBoot 自定义消息转换器使用详解

《SpringBoot自定义消息转换器使用详解》本文详细介绍了SpringBoot消息转换器的知识,并通过案例操作演示了如何进行自定义消息转换器的定制开发和使用,感兴趣的朋友一起看看吧... 目录一、前言二、SpringBoot 内容协商介绍2.1 什么是内容协商2.2 内容协商机制深入理解2.2.1 内容

vue如何监听对象或者数组某个属性的变化详解

《vue如何监听对象或者数组某个属性的变化详解》这篇文章主要给大家介绍了关于vue如何监听对象或者数组某个属性的变化,在Vue.js中可以通过watch监听属性变化并动态修改其他属性的值,watch通... 目录前言用watch监听深度监听使用计算属性watch和计算属性的区别在vue 3中使用watchE

Android WebView的加载超时处理方案

《AndroidWebView的加载超时处理方案》在Android开发中,WebView是一个常用的组件,用于在应用中嵌入网页,然而,当网络状况不佳或页面加载过慢时,用户可能会遇到加载超时的问题,本... 目录引言一、WebView加载超时的原因二、加载超时处理方案1. 使用Handler和Timer进行超

Qt QWidget实现图片旋转动画

《QtQWidget实现图片旋转动画》这篇文章主要为大家详细介绍了如何使用了Qt和QWidget实现图片旋转动画效果,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 一、效果展示二、源码分享本例程通过QGraphicsView实现svg格式图片旋转。.hpjavascript