安卓动画系列之五, 属性动画PropertyAnimation(下) - 通过官方例子深入了解

本文主要是介绍安卓动画系列之五, 属性动画PropertyAnimation(下) - 通过官方例子深入了解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

这里继续之前写的上篇属性动画PropertyAnimation(上)之初步印象 来写下篇,了解一下自定义的对象如何调用实现属性动画,还有AnimatorSet的一些灵活用法.本来也尝试像之前那样写demo去讲,但发现android官方在这方面已经提供了非常好的例子,于是就拿官方的这个小球下落回弹来作为例子,深入的了解属性动画的用法吧. 代码中的注释我已经非常详细,所以不再另外写出来了.过一遍代码,相信我们自己以后也能灵活运用属性动画了.


先上效果图:



代码:

ShapeHolder:

package com.test.android.objectanimationmain;import android.graphics.Paint;
import android.graphics.RadialGradient;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.Shape;/*** ShapeHolder作为属性动画的对象,必须设置setter和getter方法才能使动画生效.* 这个类本质上也是通过ShapeDrawable来创建可视化的实体对象.* 里面都是主要的getter和setter方法*/
public class ShapeHolder {private float x = 0, y = 0;private ShapeDrawable shape;private int color;private RadialGradient gradient;private float alpha = 1f;private Paint paint;public void setPaint(Paint value) {paint = value;}public Paint getPaint() {return paint;}public void setX(float value) {x = value;}public float getX() {return x;}public void setY(float value) {y = value;}public float getY() {return y;}public void setShape(ShapeDrawable value) {shape = value;}public ShapeDrawable getShape() {return shape;}public int getColor() {return color;}public void setColor(int value) {shape.getPaint().setColor(value);color = value;}public void setGradient(RadialGradient value) {gradient = value;}public RadialGradient getGradient() {return gradient;}public void setAlpha(float alpha) {this.alpha = alpha;shape.setAlpha((int)((alpha * 255f) + .5f));}public float getWidth() {return shape.getShape().getWidth();}public void setWidth(float width) {Shape s = shape.getShape();s.resize(width, s.getHeight());}public float getHeight() {return shape.getShape().getHeight();}public void setHeight(float height) {Shape s = shape.getShape();s.resize(s.getWidth(), height);}public ShapeHolder(ShapeDrawable s) {shape = s;}}


主类方法:

 public class MyAnimationView extends View {private static final int RED = 0xffFF8080;private static final int BLUE = 0xff8080FF;private static final int CYAN = 0xff80ffff;private static final int GREEN = 0xff80ff80;public final ArrayList<ShapeHolder> balls = new ArrayList<ShapeHolder>();AnimatorSet animation = null;public MyAnimationView(Context context) {super(context);// Animate background color// Note that setting the background color will automatically invalidate the// view, so that the animated color, and the bouncing balls, get redisplayed on// every frame of the animation.//设置背景颜色的动画ValueAnimator colorAnim = ObjectAnimator.ofInt(this, "backgroundColor", RED, BLUE);colorAnim.setDuration(3000);//设置属性值计算器colorAnim.setEvaluator(new ArgbEvaluator());colorAnim.setRepeatCount(ValueAnimator.INFINITE);colorAnim.setRepeatMode(ValueAnimator.REVERSE);colorAnim.start();}@Overridepublic boolean onTouchEvent(MotionEvent event) {//如果触摸事件不是按下或者移动事件,则不拦截if (event.getAction() != MotionEvent.ACTION_DOWN &&event.getAction() != MotionEvent.ACTION_MOVE) {return false;}ShapeHolder newBall = addBall(event.getX(), event.getY());// Bouncing animation with squash and stretch//获取触摸的事件X,Y坐标float startY = newBall.getY();//小球最后下落点的Y坐标,由屏幕高度减去小球高度得到float endY = getHeight() - 50f;//获取shapeholder的高度float h = (float)getHeight();float eventY = event.getY();int duration = (int)(500 * ((h - eventY)/h));//这里设置了"y","x"这些属性,但是系统针对newBall这对象是没有setX(), setY()的方法支持的.//所以我们在newBall所继承的自定义的ShapeHolder了中,必须自己去实现setX() setY()//和getX() , getY()方法,这样才能是属性动画在使用过程中可以根据时间不断的getter和setter.//这里就是我们所使用自定义的对象去调用属性动画的关键地方.//换言之,如果ShapeHolder中将x,y换成locationX,locationY,只要类中实现类似setLocationY(),getLocationY()方法就行了,//下面这句照样可以换成ValueAnimator bounceAnim = ObjectAnimator.ofFloat(newBall, "locationY", startY, endY);ValueAnimator bounceAnim = ObjectAnimator.ofFloat(newBall, "y", startY, endY);bounceAnim.setDuration(duration);//设置插值器,由慢到快.这样小球(也就是一个圆)在下落的过程中是从慢到快的,看起来是受了地心引力的影响...更真实一些bounceAnim.setInterpolator(new AccelerateInterpolator());//这里创建的squashAnim1属性动画,是实现小球掉落在底部的Y坐标时的压扁效果.X坐标偏移25fValueAnimator squashAnim1 = ObjectAnimator.ofFloat(newBall, "x", newBall.getX(),newBall.getX() - 25f);//这里设置的动画时间只有bounceAnim动画时间的1/4,因为压扁的过程是迅速的,所以要时间值小很多才合理squashAnim1.setDuration(duration/4);//设置重复次数为1次squashAnim1.setRepeatCount(1);//设置重复模式是逆向返回起点squashAnim1.setRepeatMode(ValueAnimator.REVERSE);//设置插值器,一直减速squashAnim1.setInterpolator(new DecelerateInterpolator());//这里一连串的squashAnim1,squashAnim2等等都是构成小球压扁效果的动画,宽度增加至50ValueAnimator squashAnim2 = ObjectAnimator.ofFloat(newBall, "width", newBall.getWidth(),newBall.getWidth() + 50);squashAnim2.setDuration(duration/4);squashAnim2.setRepeatCount(1);//因为压扁完还要恢复原状,这里必须是逆向返回起始值,使用ValueAnimator.REVERSEsquashAnim2.setRepeatMode(ValueAnimator.REVERSE);squashAnim2.setInterpolator(new DecelerateInterpolator());//依旧是构成压扁效果的动画,小球压扁后Y轴上移25f,也就是半个小球的高度,看起来是压扁到恢复原状的效果了.ValueAnimator stretchAnim1 = ObjectAnimator.ofFloat(newBall, "y", endY,endY + 25f);stretchAnim1.setDuration(duration/4);stretchAnim1.setRepeatCount(1);stretchAnim1.setInterpolator(new DecelerateInterpolator());stretchAnim1.setRepeatMode(ValueAnimator.REVERSE);//依旧是构成压扁效果的动画,小球高度减小25.ValueAnimator stretchAnim2 = ObjectAnimator.ofFloat(newBall, "height",newBall.getHeight(), newBall.getHeight() - 25);stretchAnim2.setDuration(duration/4);stretchAnim2.setRepeatCount(1);stretchAnim2.setInterpolator(new DecelerateInterpolator());stretchAnim2.setRepeatMode(ValueAnimator.REVERSE);//小球回弹动画,弹回原点.ValueAnimator bounceBackAnim = ObjectAnimator.ofFloat(newBall, "y", endY,startY);bounceBackAnim.setDuration(duration);bounceBackAnim.setInterpolator(new DecelerateInterpolator());// Sequence the down/squash&stretch/up animations//这里是AnimatorSet的灵活用法,上篇中只讲了AnimatorSet的playSequentially()和playTogether()方法//AnimatorSet作为一个动画容器,这里规定了bounceAnim在squashAnim1动画前面播放,//然后squashAnim1播放的同时又播放squashAnim2,stretchAnim1,stretchAnim2这三个动画//最后规定bounceBackAnim这个小球弹起动画必须放在stretchAnim2这个动画后面去播放,也就是放最后.//这些规则,保证了小球从下落到底部,然后在底部产生压扁效果,然后恢复原状,再弹回原点的整个过程.AnimatorSet bouncer = new AnimatorSet();bouncer.play(bounceAnim).before(squashAnim1);bouncer.play(squashAnim1).with(squashAnim2);bouncer.play(squashAnim1).with(stretchAnim1);bouncer.play(squashAnim1).with(stretchAnim2);bouncer.play(bounceBackAnim).after(stretchAnim2);// Fading animation - remove the ball when the animation is done//这里动画定义透明度从可见到完全不可见,就是小球弹回原点后消失了的效果.ValueAnimator fadeAnim = ObjectAnimator.ofFloat(newBall, "alpha", 1f, 0f);//消失的时间很短,所以这个值不要设置过长.fadeAnim.setDuration(250);//添加监听接口,当动消失的动画结束,就将小球(ShapeHolder的子类)从队列中删去.fadeAnim.addListener(new AnimatorListenerAdapter() {@Overridepublic void onAnimationEnd(Animator animation) {balls.remove(((ObjectAnimator)animation).getTarget());}});// Sequence the two animations to play one after the otherAnimatorSet animatorSet = new AnimatorSet();//这里规定消失动画必须在整个下落回弹结束后才开始animatorSet.play(bouncer).before(fadeAnim);// Start the animation//启动动画animatorSet.start();//拦截触摸事件,返回true.return true;}private ShapeHolder addBall(float x, float y) {//创建椭圆ShapeOvalShape circle = new OvalShape();//X和Y都是50f,则代表这个椭圆circle是一个半径为25f的圆circle.resize(50f, 50f);//上篇分析过ShapeDrawable的源码,可知下面是使用OvalShape去创建shapedrawable对象ShapeDrawable drawable = new ShapeDrawable(circle);ShapeHolder shapeHolder = new ShapeHolder(drawable);shapeHolder.setX(x - 25f);shapeHolder.setY(y - 25f);//下面是通过随机的方法去生成红绿蓝三个值.,从而组合成ARGB的值,设置为该圆的颜色int red = (int)(Math.random() * 255);int green = (int)(Math.random() * 255);int blue = (int)(Math.random() * 255);int color = 0xff000000 | red << 16 | green << 8 | blue;//每个ShapeDrawable对象都有自己的paint,直接getPaint()就能获取了Paint paint = drawable.getPaint(); //new Paint(Paint.ANTI_ALIAS_FLAG);int darkColor = 0xff000000 | red/4 << 16 | green/4 << 8 | blue/4;//给上面生成的ARGB值,添加圆的中心到边缘颜色从深到浅的渐变效果RadialGradient gradient = new RadialGradient(37.5f, 12.5f,50f, color, darkColor, Shader.TileMode.CLAMP);paint.setShader(gradient);//到了这一步,圆的颜色效果已经确定了shapeHolder.setPaint(paint);balls.add(shapeHolder);return shapeHolder;}@Overrideprotected void onDraw(Canvas canvas) {for (int i = 0; i < balls.size(); ++i) {//画出小球ShapeHolder shapeHolder = balls.get(i);canvas.save();canvas.translate(shapeHolder.getX(), shapeHolder.getY());shapeHolder.getShape().draw(canvas);canvas.restore();}}}


在自己新建的Activity的view中直接container.addView(new MyAnimationView(getActivity())); 就能看到效果了.


上面官方的例子没有用到自定义属性计算器Evaluator,于是为了和上面的效果对比明显,就写了一个奇葩的.

 /*** 自定义属性计算器*/public class MyEvaluator implements TypeEvaluator{@Overridepublic Object evaluate(float fraction, Object startValue, Object endValue) {fraction = (Float)startValue - (Float)endValue/10;return fraction;}}

然后在第一个动画中去设置它:

bounceAnim.setEvaluator(new MyEvaluator());

这样看看效果,是不是点击屏幕的时候,小球不再是直接从按下的地方开始下落,而是从按下的Y轴坐标更往上一些的地方下落.



值得注意的是,如果要使用自定义的对象去调用属性动画,必须在属性动画所用到的对象中设置相应属性的getter和setter方法才行.不然是没有效果的.


以上demo所用到的方法已经是比较复杂的属性动画的进阶用法,相关说明也在注释写上,所有复杂的动画都不是一两个动画就一蹴而就的,复杂的东西都由较为简单的东西去组合而成.就像代码中AnimatorSet的动画集制定了各种播放规则,组合了5,6个动画才实现一个小球从下落到底部,然后压扁恢复原状,再弹起,最后消失,这样的过程.


最后,希望能给你提供一点点帮助.感谢你阅读本文.


这篇关于安卓动画系列之五, 属性动画PropertyAnimation(下) - 通过官方例子深入了解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



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

相关文章

Spring Security 从入门到进阶系列教程

Spring Security 入门系列 《保护 Web 应用的安全》 《Spring-Security-入门(一):登录与退出》 《Spring-Security-入门(二):基于数据库验证》 《Spring-Security-入门(三):密码加密》 《Spring-Security-入门(四):自定义-Filter》 《Spring-Security-入门(五):在 Sprin

关于数据埋点,你需要了解这些基本知识

产品汪每天都在和数据打交道,你知道数据来自哪里吗? 移动app端内的用户行为数据大多来自埋点,了解一些埋点知识,能和数据分析师、技术侃大山,参与到前期的数据采集,更重要是让最终的埋点数据能为我所用,否则可怜巴巴等上几个月是常有的事。   埋点类型 根据埋点方式,可以区分为: 手动埋点半自动埋点全自动埋点 秉承“任何事物都有两面性”的道理:自动程度高的,能解决通用统计,便于统一化管理,但个性化定

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

活用c4d官方开发文档查询代码

当你问AI助手比如豆包,如何用python禁止掉xpresso标签时候,它会提示到 这时候要用到两个东西。https://developers.maxon.net/论坛搜索和开发文档 比如这里我就在官方找到正确的id描述 然后我就把参数标签换过来

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

科研绘图系列:R语言扩展物种堆积图(Extended Stacked Barplot)

介绍 R语言的扩展物种堆积图是一种数据可视化工具,它不仅展示了物种的堆积结果,还整合了不同样本分组之间的差异性分析结果。这种图形表示方法能够直观地比较不同物种在各个分组中的显著性差异,为研究者提供了一种有效的数据解读方式。 加载R包 knitr::opts_chunk$set(warning = F, message = F)library(tidyverse)library(phyl

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

滚雪球学Java(87):Java事务处理:JDBC的ACID属性与实战技巧!真有两下子!

咦咦咦,各位小可爱,我是你们的好伙伴——bug菌,今天又来给大家普及Java SE啦,别躲起来啊,听我讲干货还不快点赞,赞多了我就有动力讲得更嗨啦!所以呀,养成先点赞后阅读的好习惯,别被干货淹没了哦~ 🏆本文收录于「滚雪球学Java」专栏,专业攻坚指数级提升,助你一臂之力,带你早日登顶🚀,欢迎大家关注&&收藏!持续更新中,up!up!up!! 环境说明:Windows 10

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝