安卓动画系列之五, 属性动画PropertyAnimation(下) - 通过官方例子深入了解
来源:互联网 发布:算法英语 编辑:程序博客网 时间:2024/04/30 13:29
这里继续之前写的上篇属性动画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(); } @Override public 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坐标偏移25f ValueAnimator 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等等都是构成小球压扁效果的动画,宽度增加至50 ValueAnimator squashAnim2 = ObjectAnimator.ofFloat(newBall, "width", newBall.getWidth(), newBall.getWidth() + 50); squashAnim2.setDuration(duration/4); squashAnim2.setRepeatCount(1); //因为压扁完还要恢复原状,这里必须是逆向返回起始值,使用ValueAnimator.REVERSE squashAnim2.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() { @Override public void onAnimationEnd(Animator animation) { balls.remove(((ObjectAnimator)animation).getTarget()); } }); // Sequence the two animations to play one after the other AnimatorSet animatorSet = new AnimatorSet(); //这里规定消失动画必须在整个下落回弹结束后才开始 animatorSet.play(bouncer).before(fadeAnim); // Start the animation //启动动画 animatorSet.start(); //拦截触摸事件,返回true. return true; } private ShapeHolder addBall(float x, float y) { //创建椭圆Shape OvalShape 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; } @Override protected 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(); } } }
上面官方的例子没有用到自定义属性计算器Evaluator,于是为了和上面的效果对比明显,就写了一个奇葩的.
/** * 自定义属性计算器 */ public class MyEvaluator implements TypeEvaluator { @Override public Object evaluate(float fraction, Object startValue, Object endValue) { fraction = (Float)startValue - (Float)endValue/10; return fraction; } }
然后在第一个动画中去设置它:
bounceAnim.setEvaluator(new MyEvaluator());
这样看看效果,是不是点击屏幕的时候,小球不再是直接从按下的地方开始下落,而是从按下的Y轴坐标更往上一些的地方下落.
值得注意的是,如果要使用自定义的对象去调用属性动画,必须在属性动画所用到的对象中设置相应属性的getter和setter方法才行.不然是没有效果的.
最后,希望能给你提供一点点帮助.感谢你阅读本文.
0 0
- 安卓动画系列之五, 属性动画PropertyAnimation(下) - 通过官方例子深入了解
- 安卓动画系列之四, 属性动画PropertyAnimation(上)之初步印象
- 动画之属性动画--PropertyAnimation
- QML动画之PropertyAnimation(属性动画)
- Android属性动画(PropertyAnimation)(下)
- Android属性动画(PropertyAnimation)(下)
- 属性动画PropertyAnimation 小试牛刀
- 【Android】PropertyAnimation属性动画
- PropertyAnimation属性动画
- Android属性动画PropertyAnimation系列三之LayoutTransition(布局容器动画)
- Android属性动画PropertyAnimation系列三之LayoutTransition(布局容器动画)
- 安卓属性动画系列
- QML动画 之 PropertyAnimation
- Android动画之属性动画(PropertyAnimation)详解(一)
- Android 属性动画(PropertyAnimation)详解
- Android属性动画(PropertyAnimation)
- 安卓动画之属性动画
- Android动画3-属性动画(PropertyAnimation)
- java.util.concurrent
- Lua学习之数据结构
- ASP开发入门+实战电子书共50本
- INSTALL_FAILED_INSUFFICIENT_STORAGE 的解决方法
- Android开发免豆资料(教程+工具+源码)下载地址汇总
- 安卓动画系列之五, 属性动画PropertyAnimation(下) - 通过官方例子深入了解
- VBA笔记
- hihocoder: 二分·二分查找之k小数
- centos7 ifconifg没有ip
- 一步一步学习ASP.NET 5 (三)- 认识新的Web结构
- 蓝桥杯 循环小数 模拟除法 解题报告
- 【c语言】从标准输入读取几行输入。每行输入都要打印到标准输出上,前面加上行号
- Linux7.0 NFS网络文件系统
- 作业2.2---信号报告