android自定义圆圈动画,自定义view实现动画数字圆圈

我们要实现的是如下的效果,

648267662b0dc32838bdc70899db9786.png

1.该view在设置属性之后时候会有数字和圆圈不断增长的效果

2.该view在按下和放开状态下显示不同的样式。

这种效果逻辑上并不复杂,底层灰色圆圈和蓝色扇形圆圈都是用canvas.drawArc()绘制出来的,中间的数字用drawtext绘制,数字不断增长的效果用了继承Animation的动画类;在按下和放开状态下显示不同的样式是重写了View 的setPressed()方法。

先贴出所有代码,再一一解释import com.jcodecraeer.util.MyUtils;

import android.content.Context;

import android.content.res.TypedArray;

import android.graphics.Canvas;

import android.graphics.Color;

import android.graphics.Paint;

import android.graphics.Path;

import android.graphics.Rect;

import android.graphics.RectF;

import android.graphics.Typeface;

import android.graphics.Paint.Align;

import android.graphics.Paint.Style;

import android.graphics.drawable.Drawable;

import android.util.AttributeSet;

import android.util.Log;

import android.view.View;

import android.view.animation.Animation;

import android.view.animation.Transformation;

public class CircleBar extends View {

private RectF mColorWheelRectangle = new RectF();

private Paint mDefaultWheelPaint;

private Paint mColorWheelPaint;

private Paint textPaint;

private float mColorWheelRadius;

private float circleStrokeWidth;

private float pressExtraStrokeWidth;

private String mText;

private int mCount;

private float mSweepAnglePer;

private float mSweepAngle;

private int mTextSize;

BarAnimation anim;

public CircleBar(Context context) {

super(context);

init(null, 0);

}

public CircleBar(Context context, AttributeSet attrs) {

super(context, attrs);

init(attrs, 0);

}

public CircleBar(Context context, AttributeSet attrs, int defStyle) {

super(context, attrs, defStyle);

init(attrs, defStyle);

}

private void init(AttributeSet attrs, int defStyle) {

circleStrokeWidth = MyUtils.dip2px(getContext(), 10);

pressExtraStrokeWidth = MyUtils.dip2px(getContext(), 2);

mTextSize = MyUtils.dip2px(getContext(), 40);

mColorWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mColorWheelPaint.setColor(0xFF29a6f6);

mColorWheelPaint.setStyle(Paint.Style.STROKE);

mColorWheelPaint.setStrokeWidth(circleStrokeWidth);

mDefaultWheelPaint = new Paint(Paint.ANTI_ALIAS_FLAG);

mDefaultWheelPaint.setColor(0xFFeeefef);

mDefaultWheelPaint.setStyle(Paint.Style.STROKE);

mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth);

textPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.LINEAR_TEXT_FLAG);

textPaint.setColor(0xFF333333);

textPaint.setStyle(Style.FILL_AND_STROKE);

textPaint.setTextAlign(Align.LEFT);

textPaint.setTextSize(mTextSize);

mText = "0";

mSweepAngle = 0;

anim = new BarAnimation();

anim.setDuration(2000);

}

@Override

protected void onDraw(Canvas canvas) {

canvas.drawArc(mColorWheelRectangle, -90, 360, false, mDefaultWheelPaint);

canvas.drawArc(mColorWheelRectangle, -90, mSweepAnglePer, false, mColorWheelPaint);

Rect bounds = new Rect();

String textstr=mCount+"";

textPaint.getTextBounds(textstr, 0, textstr.length(), bounds);

canvas.drawText(

textstr+"",

(mColorWheelRectangle.centerX())

- (textPaint.measureText(textstr) / 2),

mColorWheelRectangle.centerY() + bounds.height() / 2,

textPaint);

}

@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int height = getDefaultSize(getSuggestedMinimumHeight(),

heightMeasureSpec);

int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);

int min = Math.min(width, height);

setMeasuredDimension(min, min);

mColorWheelRadius = min - circleStrokeWidth -pressExtraStrokeWidth ;

mColorWheelRectangle.set(circleStrokeWidth+pressExtraStrokeWidth, circleStrokeWidth+pressExtraStrokeWidth,

mColorWheelRadius, mColorWheelRadius);

}

@Override

public void setPressed(boolean pressed) {

Log.i(TAG,"call setPressed ");

if (pressed) {

mColorWheelPaint.setColor(0xFF165da6);

textPaint.setColor(0xFF070707);

mColorWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);

mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);

textPaint.setTextSize(mTextSize-pressExtraStrokeWidth);

} else {

mColorWheelPaint.setColor(0xFF29a6f6);

textPaint.setColor(0xFF333333);

mColorWheelPaint.setStrokeWidth(circleStrokeWidth);

mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth);

textPaint.setTextSize(mTextSize);

}

super.setPressed(pressed);

this.invalidate();

}

public void startCustomAnimation(){

this.startAnimation(anim);

}

public void setText(String text){

mText = text;

this.startAnimation(anim);

}

public void setSweepAngle(float sweepAngle){

mSweepAngle = sweepAngle;

}

public class BarAnimation extends Animation {

/**

* Initializes expand collapse animation, has two types, collapse (1) and expand (0).

* @param view The view to animate

* @param type The type of animation: 0 will expand from gone and 0 size to visible and layout size defined in xml.

* 1 will collapse view and set to gone

*/

public BarAnimation() {

}

@Override

protected void applyTransformation(float interpolatedTime, Transformation t) {

super.applyTransformation(interpolatedTime, t);

if (interpolatedTime < 1.0f) {

mSweepAnglePer = interpolatedTime * mSweepAngle;

mCount = (int)(interpolatedTime * Float.parseFloat(mText));

} else {

mSweepAnglePer = mSweepAngle;

mCount = Integer.parseInt(mText);

}

postInvalidate();

}

}

}

属性变量及其说明

private RectF mColorWheelRectangle = new RectF();圆圈的矩形范围

private Paint mDefaultWheelPaint;  绘制底部灰色圆圈的画笔

private Paint mColorWheelPaint; 绘制蓝色扇形的画笔

private Paint textPaint; 中间文字的画笔

private float mColorWheelRadius; 圆圈普通状态下的半径

private float circleStrokeWidth; 圆圈的线条粗细

private float pressExtraStrokeWidth;按下状态下增加的圆圈线条增加的粗细

private String mText;中间文字内容

private int mCount; 为了达到数字增加效果而添加的变量,他和mText其实代表一个意思

private float mSweepAnglePer;  为了达到蓝色扇形增加效果而添加的变量,他和mSweepAngle其实代表一个意思

private float mSweepAngle; 扇形弧度

private int mTextSize;文字颜色

BarAnimation anim;动画类

构造方法调用之后,第一个调用的是init方法,在该方法中初始化了各种画笔的颜色,风格等,字体大小和线条粗细则使用了我自己定义的工具函数dip2px(),这样做的目的是在不同分辨率的手机上,相同数值的最终显示效果差别不大,比如字体大小mTextSize的初始化:mTextSize = MyUtils.dip2px(getContext(), 40);

还定义了动画对象以及动画持续时间:anim = new BarAnimation();

anim.setDuration(2000);

其中BarAnimation为自定义的动画类:public class BarAnimation extends Animation {

/**

* Initializes expand collapse animation, has two types, collapse (1) and expand (0).

* @param view The view to animate

* @param type The type of animation: 0 will expand from gone and 0 size to visible and layout size defined in xml.

* 1 will collapse view and set to gone

*/

public BarAnimation() {

}

@Override

protected void applyTransformation(float interpolatedTime, Transformation t) {

super.applyTransformation(interpolatedTime, t);

if (interpolatedTime < 1.0f) {

mSweepAnglePer = interpolatedTime * mSweepAngle;

mCount = (int)(interpolatedTime * Float.parseFloat(mText));

} else {

mSweepAnglePer = mSweepAngle;

mCount = Integer.parseInt(mText);

}

postInvalidate();

}

}

这个动画类利用了applyTransformation参数中的interpolatedTime参数(从0到1)的变化特点,实现了该View的某个属性随时间改变而改变。原理是在每次系统调用animation的applyTransformation()方法时,改变mSweepAnglePer,mCount的值,然后调用postInvalidate()不停的绘制view。if (interpolatedTime < 1.0f) {

mSweepAnglePer = interpolatedTime * mSweepAngle;

mCount = (int)(interpolatedTime * Float.parseFloat(mText));

}

mSweepAnglePer,mCount这两个属性只是动画过程中要用到的临时属性,mText和mSweepAngle才是动画结束之后表示扇形弧度和中间数值的真实值。

绘制方法

在onDraw方法中我们绘制了圆圈、扇形以及文字,但是绘制需要用到的一些坐标值是经过计算得出的,比如绘制扇形:canvas.drawArc(mColorWheelRectangle, -90, mSweepAnglePer, false, mColorWheelPaint);

mColorWheelRectangle是一个矩形,这个矩形的上下左右边界都是在onMeasure方法中根据控件所分配的大小得出来的。

具体计算方式在onMeasure的实现中:@Override

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {

int height = getDefaultSize(getSuggestedMinimumHeight(),

heightMeasureSpec);

int width = getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec);

int min = Math.min(width, height);

setMeasuredDimension(min, min);

mColorWheelRadius = min - circleStrokeWidth -pressExtraStrokeWidth ;

mColorWheelRectangle.set(circleStrokeWidth+pressExtraStrokeWidth, circleStrokeWidth+pressExtraStrokeWidth,

mColorWheelRadius, mColorWheelRadius);

}

从setMeasuredDimension(min, min)可以看出我们强制该View为正方形。上面说到的mColorWheelRectangle矩形区域比控件的实际边界要小,这样做的目的是在按下状态下状态下让圆圈的线条变大之后也并不会超出矩形区域。

按下松开view样式改变的实现

改变样式很简单,只需改变画笔的样式就可以了,关键是在什么地方改变。我们都知道设置背景成selector就能是按下松开状态下背景改变,但是直接设背景不满足这里的要求,因为这是个圆圈,如果设置背景那肯定不会紧贴着圆圈边缘,但是我们可以在不同状态下更改画笔然后重绘达到相同的效果。如何检测到按下与松开呢?

看了view的源码知道setPressed()方法可以满足我们的要求:

@Override

public void setPressed(boolean pressed) {

Log.i(TAG,"call setPressed ");

if (pressed) {

mColorWheelPaint.setColor(0xFF165da6);

textPaint.setColor(0xFF070707);

mColorWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);

mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth+pressExtraStrokeWidth);

textPaint.setTextSize(mTextSize-pressExtraStrokeWidth);

} else {

mColorWheelPaint.setColor(0xFF29a6f6);

textPaint.setColor(0xFF333333);

mColorWheelPaint.setStrokeWidth(circleStrokeWidth);

mDefaultWheelPaint.setStrokeWidth(circleStrokeWidth);

textPaint.setTextSize(mTextSize);

}

super.setPressed(pressed);

this.invalidate();

}

每次按下或者松开setPressed都会被调用,我们重写该方法,但要注意调用super.setPress()不然长按放开之后boolean pressed参数仍然为true,这样松开之后样式就保持按下的状态。具体原因还需要多阅读view的源码。

总结

其实这里最主要的是要有耐心了解canvas的一些方法,还有就是要根据自己的需求有针对性的分析view的源码。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/526260.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android+水滴粘性动画,Android控件实现水滴效果

看到ios版上QQ刷新效果像水滴&#xff0c;然后自己也想着去实现这样的效果&#xff0c;这篇文章暂时没有介绍下拉刷新的效果&#xff0c;只是单独用一个控件来实现这样的水滴效果。效果图如下&#xff1a;一、总体思路1、画两个圆形&#xff0c;其中一个就是上面的大圆&#xf…

第一台鸿蒙手机是,第一台预装鸿蒙OS的手机终于登场。

原标题&#xff1a;第一台预装鸿蒙OS的手机终于登场。千呼万唤始出来&#xff0c;之前大家期待了很久的鸿蒙OS它终于真正到来了。据工业和信息化部公布的消息&#xff0c;搭载鸿蒙OS的新款华为手机正式入网了&#xff0c;这也将是华为第一台预装鸿蒙OS的新款手机。但令人完全没…

html跳转网页为什么网页无法访问,朋友的网站被网址跳转,导致官网无法正常访问...

原标题&#xff1a;朋友的网站被网址跳转&#xff0c;导致官网无法正常访问昨天中午接到客户的一个电话&#xff0c;告知其某个网站打开之后直接跳转到其他的网站。客户的这个网站&#xff0c;不是我们做的&#xff0c;但是关系一直保持的不错&#xff0c;所以就顺带给他解决一…

2021中卫一中高考成绩查询,2021年宁夏高中排名及分数线 高考本科升学率排行榜...

2019年宁夏高中排名及分数线 高考本科升学率排行榜如何判断一所高中学校的优劣好坏&#xff0c;人们往往从这几个方面来看&#xff1a;高考/竞赛成绩、生源质量、师资力量、软硬件设施、管理水平等。高考成绩因为最直接&#xff0c;最广为关注&#xff0c;但是很多时候未免有失…

微观经济学如何计算机会成本,【微观经济学】机会成本

概念&#xff1a;机会成本机会成本是指你做了某项选择&#xff0c;而不得不因此失去的其他利益。比如你选择了A&#xff0c;就必须放弃B&#xff0c;B就是A的机会成本。对企业来说&#xff0c;最优方案的机会成本&#xff0c;就是次优方案可能带来的收益。机会成本是听起来很简…

计算机网络技术俄罗斯方块,《The Tetris Effect》:这本新书讲述了俄罗斯方块的传奇故事...

原标题&#xff1a;《The Tetris Effect》:这本新书讲述了俄罗斯方块的传奇故事在游戏发展史上&#xff0c;俄罗斯方块是个传奇。没有一款游戏像它一样影响深远&#xff0c;受到全球不同人的喜爱。在即将出版的《The Tetris Effect》(俄罗斯方块效应)中&#xff0c;科技记者 Da…

计算机主机响是什么原因,电脑主机发出不同的响声及解决措施

电脑在使用一段时间后&#xff0c;难免都会遇到些小问题。如很多朋友使用的台式电脑&#xff0c;在开机的时候&#xff0c;主机就会发出很大的响声。其实当主机发出不同响声时&#xff0c;就代表着不同的故障&#xff0c;我们可以根据相应的响声来找出故障的所在。今天U大侠小编…

计算机上的键有什么功能吗,电脑功能键都有什么用,电脑上功能键的用法_系统圣地...

电脑大家都使用了不少年了&#xff0c;但是对于键盘最上方的F1到F12的功能都是什么&#xff0c;相信不少小伙伴都一知半解吧&#xff0c;毕竟平时使用的比较少&#xff0c;但是在某些情况下&#xff0c;这些功能键能够有效的提升你的做事效率&#xff0c;所以了解一下还是不错的…

云测试软件详解,软件测试之登录测试详解

一、功能测试–登录功能性测试用例包括&#xff1a;1.什么都不输入&#xff0c;点击提交按钮&#xff0c;看提示信息。(非空检查)2.输入已注册的用户名和正确的密码&#xff0c;验证是否登录成功&#xff1b;3.输入已注册的用户名和不正确的密码&#xff0c;验证是否登录失败&a…

计算机启用时间 查找方式,电脑实用知识技巧 篇六:不需要第三方软件,这种方法查看系统启动时间...

电脑实用知识技巧 篇六&#xff1a;不需要第三方软件&#xff0c;这种方法查看系统启动时间2019-04-04 09:19:210点赞0收藏0评论上次我们说到&#xff1a;开机小助手&#xff0c;要让我们看到开机时间&#xff0c;必须添加自启动项目&#xff0c;这将拖慢我们的开机速度。有没有…

迷你世界显示未连接服务器成功,迷你世界登录未成功是什么意思 | 手游网游页游攻略大全...

发布时间&#xff1a;2017-08-29迷你世界两周年庆活动到来,那么两周年庆迷你世界有哪些活动呢?很多小伙伴都不了解吧,那么下面牛游戏小编就给大家仔细的介绍一下迷你世界两周年庆活动吧,希望能给大家带来帮助. 迷你世界两周年庆活动 世界守护计划--环保筑梦师 ...标签&#xf…

广播 消息 没有服务器,服务器节点消息广播

服务器节点消息广播 内容精选换一换添加节点时提示“添加节点失败&#xff0c;节点已存在”。待添加节点的服务器上已安装系统性能分析或者添加过节点。如果待添加节点的服务器上已安装系统性能分析&#xff0c;需要登录服务器卸载系统性能分析&#xff0c;详细步骤请参见卸载&…

微信收款音响s3服务器断开,微信收款音响s2和s3有什么区别

微信收款音响s2和s3的区别&#xff1a;1、S2是一款四角梯形形状的音箱&#xff0c;S3是一款圆角正方体形状的音箱。2、S2电池1200mAh&#xff0c;S3电池1800mAh。3、S3可以连接WiFi&#xff0c;S2都不可以。音响(Audio electronics)广义上是指一种利用电子回路设计进行音讯与电…

手机访问服务器中的数据库文件,手机连接服务器数据库文件在哪里

手机连接服务器数据库文件在哪里 内容精选换一换通过PostgreSQL客户端连接实例的方式有非SSL连接和SSL连接两种&#xff0c;其中SSL连接通过了加密功能&#xff0c;具有更高的安全性。绑定弹性公网IP并设置安全组规则。对目标实例绑定弹性公网IP。关于如何绑定弹性公网IP&#…

浅谈C语言字节对齐

首先&#xff0c;我们得知道为什么要进行内存对齐&#xff0c;它的意义何在&#xff1f;在这儿可以先看这样一张图。&#xff08;手绘请见谅&#xff01;&#xff01;&#xff01;&#xff09; 我们知道&#xff0c;在32位CPU下&#xff0c;一个读取周期可以读取四个字节。一个…

C语言中函数调用中的传值与传址

首先介绍一下函数中传值与传址的概念&#xff1a; 传值&#xff1a;传值&#xff0c;实际是把实参的值赋值给行参&#xff0c;相当于copy。那么对行参的修改&#xff0c;不会影响实参的值 。传址&#xff1a; 实际是传值的一种特殊方式&#xff0c;只是他传递的是地址&#xf…

C语言交换两个变量数值的几种方法

因为经常见到这类题目&#xff0c;就自己总结了以下几种办法 1. 创建中间变量 这是最快也是最简单的办法&#xff0c;例如&#xff1a; #include<stdio.h>int main() {int a10;int b20;int temp;printf("交换前a,b的值为:\n");printf("a%d\n",a);…

vim中自动添加文件的作者、时间信息、版本等

1、linux系统版本&#xff1a;ubuntu-10.10 2、打开&#xff1a;vim ~/.vimrc 在文件末尾添加如下内容&#xff0c;如图一 &#xff08;图一&#xff09; 3、新建文件后直接按“F4”可插入作者文件信息&#xff0c;如图二所示 &#xff08;图二&#xff09;

数组的下标越界与内存溢出

很相似的两个概念&#xff0c;一不小心就会混淆 首先&#xff0c;对两个名词做一个大概的解释&#xff1a; 下标越界 在引用数组元素时&#xff0c;使用的下标超过了该数组下标的应有范围&#xff0c;但应注意的是&#xff1a; C/C不对数组做边界检查。 可以重写数组的每一…

二分查找(折半查找)

二分查找(折半查找)&#xff1a; 从有序序列中找到给出的要查询的数字。 原理是&#xff1a;首先把一个有序序列中间位置的值与要查找的数比较&#xff0c;若相等则找到了有序序列中的此数&#xff1b;否则比较两者的大小&#xff0c;若前者大&#xff0c;则把有序序列的中间…