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应用中以模糊效果设置背景图片

我试图使列表视图上的背景图像模糊,但是我尝试按照教程进行操作,但它不起作用.任何人都请指教,谢谢.主要活动public class IngredientCategoryMain extends Activity {ListView list;String[] title;CategoryImageAdapter adapter;Overrideprotected void onCreate(Bundle save…

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

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

android 标题栏进度圈使用方法,Android 自定义标题栏 显示网页加载进度的方法实例...

这阵子在做Lephone的适配&#xff0c;测试组提交一个bug&#xff1a;标题栏的文字较长时没有显示完全&#xff0c;其实这并不能算个bug&#xff0c;并且这个问题在以前其他机器也没有出现&#xff0c;只是说在Lephone的这个平台上显示得不怎么美观&#xff0c;因为联想将原生的…

android7.0uri,整理: 解决Android7.0以上文件报FileUriExposedException问题

最近项目Android编译版本改为targetSdkVersion26, 之前隐藏的版本问题暴露出来, 尤其是文件第三方打开及Apk更新问题,这里记录下7.0以后文件相关问题问题描述:安装apk的代码一般写法如下&#xff0c;网上随处可以搜到:public static void installApk(Context context, File fil…

android+使用bmob冲突,bmob开发android遇到的问题

昨天尝试使用bmob&#xff0c;但是在使用文件对象时候遇到问题&#xff0c;但是他们的工作人员周末没上班&#xff0c;问题没解决&#xff0c;昨晚一晚没睡好&#xff0c;之后大清早的来求帮助了。源码如下&#xff1a;String path Environment.getExternalStorageDirectory()…

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

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

linux sed 正则转义,Linux运维云升笔记 (一)正则表达式以及文档编辑器sed

正则表达式概述使用单个字符来描述、匹配一系列符合某个语句规则的字符串&#xff0c;由普通字符与特殊字符组成&#xff0c;正则表达式广泛使用在脚本编程、文本编辑器中。正则表达式简写为regex、regexp、RE。再大多数语言当中正则表达式都被包括在两个正斜杠当中“/”正则表…

html 自动切换tab栏,html 实现tab切换的示例代码

tab切换在项目中也算是常用技术&#xff0c;一般实现tab切换都用js或者jq实现&#xff0c;今天介绍两种只用css实现tab切换方法&#xff1a;方法一&#xff1a;原理&#xff1a;通过label标签的关联属性和input的单选类型实现相应div的显示1.创建一个类名为wrap的div当作容器2.…

html匹配属性正则表达式,正则表达式匹配html标签的属性值

html">正则表达式是做文本解析工作必不可少的技能。如Web服务器日志分析&#xff0c;网页前端开发等。很多高级文本编辑器都支持正则表达式的一个子集&#xff0c;熟练掌握正则表达式&#xff0c;经常能够使你的一些工作事半功倍。例如统计代码行数&#xff0c;只需一个…

同一个html页面中两个area,HTML中的map和area标签

1. 标签介绍&#xff1a;(1)map标签&#xff1a; 该标签是指图片的映射&#xff0c;也就是说一张可以点击的图片的映射&#xff1b;属性介绍&#xff1a;<1> id: 中的 usemap 属性可引用 中的 id 或 name 属性(取决于浏览器)<2>name:同上说明&#xff1a;不同的浏览…

html 响应式布局 九宫格,两种方法实现响应式九宫格布局

html布局以及基础样式代码如下响应式九宫格html, body { color:#222; margin:0; padding: 0; text-decoration: none; }ul { list-style: none outside none; margin:0; padding: 0; }body { background-color:#eee; }ul li:nth-child(8n1) {background-color:#36A1DB}ul li:nt…

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

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

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

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

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

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

如何注释python中html,Python在HTML中提取带注释的代码,python,html,被

假设被注释代码段如下&#xff1a;html""""""如果直接对此代码段使用pyquery转换并提取from pyquery import PyQuery as pqresponse pq(html)("div.forum_content")print(response)会报错&#xff1a;lxml.etree.ParserError: Docume…

基于蓝墨云平台的计算机教学,基于蓝墨云班课的中职计算机“分层教学”模式探究...

崔月娇一、教学现状概述一方面&#xff0c;我校是面向全国招生&#xff0c;部分欠发达地区的学生由于资源配置导致计算机基础相当薄弱&#xff0c;而来自发达地区的学生早早地接触了计算机&#xff0c;部分计算机基础操作已相当熟练&#xff0c;学生的计算机水平参差不齐&#…

计算机专业和机电专业的区别,12届计算机专业和机电专业.doc

12届计算机专业和机电专业12届计算机1班、12届机电1、2、3、4班《计算应用基础》月考试题学号&#xff1a;_________ 姓名&#xff1a;_________ 班级&#xff1a;_____________考试说明&#xff1a;1、考试时间90分钟2、总分&#xff1a;100分3、应用的班级有12计算机1班、12届…

计算机基础知识上机操作excer,《计算机应用基础》Excel上机操作练习题.doc

《计算机应用基础》Excel上机操作练习题.doc《计算机应用基础》Excel上机操作练习题第1题 建立一工作簿&#xff0c;并作如下操作1&#xff0e; 在Sheet工作表中输入如下内容&#xff1a;在A1单元格中输入&#xff1a;中华人民共和国以数字字符的形式在B1单元格…

angularjs html 缓存,如何删除使用AngularJS的HTML中的浏览器缓存?

如何删除HTML浏览器的缓存&#xff0c;它使用AngularJS&#xff1f;我在我的index.html中使用了以下内容。我也尝试在我的app.js中使用$templateCache&#xff0c;但没用。如何删除使用AngularJS的HTML中的浏览器缓存&#xff1f;app.config([$routeProvider, function($routeP…

dede列表页if判断输出html,首页、列表页调用文章body内容的两种方法

随着源码的开放性&#xff0c;很多SEOER对页面的要求也越来越复杂多样性&#xff0c;很多时候&#xff0c;织梦系统的原有功能并不能满足seoer的页面布置要求&#xff0c;这就需要继续开发页面&#xff0c;做更多的功能调用。今天徐金华SEO给大家讲的是关于首页、列表页调用文章…