欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Android自定义控件入门与实战之属性动画

程序员文章站 2022-05-02 22:14:35
...

第三章:属性动画

在前面提过,Android动画分为两类:

视图动画(view animation)和属性动画(property animation)

其中视图动画包括补间动画和逐帧动画
属性动画包括:ValueAnimator和ObjectAnimator

1.视图动画和属性动画的区别
除了引入时间和所在包名不同以外,就是动画类的命名不同,视图动画的命名都是xxxAnimation 而propertyview命名都是XXXXAnimator
2.为什么引入属性动画?
答:逐帧动画主要用来实现动画的,而补间动画才能实现控件的淡入淡出移动旋转等效果;
属性动画是为了弥补视图动画的不足而出现的,主要是由于视图动画只能针对派生自view的控件实例 有效果,而属性动画则不同,属性动画理论上可以为任意的控件添加动画效果,补间动画虽然能够对控件做动画,但是并没有改变内部的属性值。

3.1.2ValueAnimator的简单使用
ValueAnimator是针对值的而不是针对控件本身的,ValueAnimator不会对控件进行任何操作 我们可以给他设定从某个值运动到某个值,通过监听这些值的渐变过程来自己操作控件
1.初步使用Value Animator
要使用Value Animator有两步操作
第一步:创建Value Animator实例,比如:

Value Animator animator=Value Animator.ofInt(0,400);
animator.setDuration(1000);
animator.start();

在这里我们用了Value Animator.ofInt()函数创建了一个值从0到400的动画,
持续时间为1s,然后让动画开始,animator没有和任何控件相关联
所以我们需要监听valueAnimator的动画过程来自己对控件进行操作

总而言之,value animator只负责指定值区间进行动画运算
我们需要对运算过程进行监听,然后自己对控件执行动画操作

2.valueanimator的使用实例
给一个textview加上监听器,点击时显示helloworld,然后新建一个valueanimator对象,
设置值的改变范围为0-400,持续时间为1000ms。然后添加监听器,改变textview的layout。

对于layout函数需要做一点说明:
textview.layout(l,t,r,b);四个参数是如何确定textview的位置的呢?
l 和t 就可以确定textview的位置,再通过r和b来确定textview的宽高

3.1.3常用函数
1.ofInt()和ofFloat()函数 他们的参数都是可变长参数,所以我们可以传入任何数量的值,
传进去列表的值就表示 动画时的变化范围,比如说ofInt(2,90,45)就表示数字从2变化到90后再变成45,我们传进去的 值越多,动画就越复杂。
常用函数汇总
1.setDuration();//设置动画持续时间
2.getAnimatedValue()//在运动时当前运动点的值
3.start()//开始动画
4.setRepeatCount()//设置重复的次数
5.setRepeatMode();//设置循环模式
6.cancel();//取消动画
3.添加与移除监听器
1.添加监听器 ValueAnimator中有两种监听器
监听器一:监听动画过程中值的实时变化//AnimatorUpdateListener();
监听器二:监听动画变化时的四个状态:开始,结束,取消,重复//AnimatorListener();

2.移除监听器 移除addUpdateListener():
void removeUpdateListener()
void removeAllUpdateListener();

3.移除AnimationListener
void removeListener():移除指定的监听器
void removeAllListener();移除所有的监听器

4.其他不常用的函数
void setStartDelay():延长多久开始,单位是毫秒
ValueAnimator clone()//完全克隆一个value animator实例包括对他设置的全部实例

3.1.4弹跳加载效果
其效果是用三张图片来实现的,一张照片从上向上运动,到达顶部后,另外一张图片也从同样的 位置出发向顶部运动,三张图片轮流切换,形成动画效果。
步骤:首先要动态的改变image的高度,通过valueAnimator来提供一个0-100-0的值 在创建时,首先要确定image的起始高度,可以在onLayout中确定,在addUpdateListerner()中 动态的改变高度setTop(Top-动态改变的值),再设置一个插值器,然后如果要动态改变图片 需要在repeatListener中添加代码。当动画再次重复的时候改变ImageView的背景图片,setImageDrawable()改变背景即可

3.2自定义插值器与Evaluator
视图动画中,仅允许我们通过setinterpolator()函数来设置插值器,但是属性动画不仅可以设置插值器还可以设置Evaluator。
3.2.1自定义插值器
怎么自定义插值器?
只需要实现一下Interpolator接口,然后处理一下返回的input就行了,你可以设计一下怎么返回input
3.2.2Evaluator
了解一下我们是如何得到当前进度的:
1.ofInt(0,400)//设定数值区间
2.插值器//返回当前数值进度
3.Evaluator//依据数值进度计算当前值
4.监听器返回

什么是Evaluator?
Evaluator其实就是一个转换器。他能把小数进度转换成对应的数值位置。
2.各种Evaluator
每种返回类型的动画有自己对应的Evaluator,必须要确定返回值是同一类型才可以通用,
对于ofInt()的Evaluator的类是IntEvaluator,同样的对应于ofFloat的Evaluator的类是FloatEvaluator()

IntEvaluator是怎么实现的?
intEvaluator继承自TypeEvaluator:
在intEvaluator中只有一个函数evaluate(float fraction ,Integer startValue,Integer endValue)

解释: fraction:参数就是插值器中返回的值,表示当前动画的数值进度 start和end分别是传进来的两个参数

下面我们来简单的实现一下Evaluator

新建一个类实现TypeEvaluator,然后处理starvalue,endvalue和fraction之间的关系,返回当前
的实际进度

流程如下: ofInt(100,400)——》插值器(返回当前的数值进度)——》Evaluator(根据数值进度计算当前值也就是实际进度) ——》监听器返回(在addUpdateListener()中返回)

在插值器中我们可以自定义插值器的返回的数据进度来改变返回的数值的位置

结论:既可以通过重写插值器改变改变数值位置,也可以通过改变Evaluator中的数值进度所对应的 具体数值位置。

下面我们来想一个问题:

如何实现倒序播放动画?
比方说ofInt(100,400),如果我们不做改变 那么返回的值就是从100到400,通过我们的处理可以是返回的值从400到100,我们可以通过改变改变插值器返回的inpu参数,将之设置为1-input,这样就简单设置了倒序,也可以通过自定义Evaluator实现

总而言之:如何通过改变Evaluator和自定义插值器来实现不同的效果就是ValueAnimation的主要内容

5.关于ArgbEvaluator
1.使用ArgbEvaluator
ArgbEvaluator主要功能是实现颜色值过渡转换的

简单说说ArgbEvaluator的使用方法:
通过ofInt()方法来实现从某个颜色值到某个颜色值的过渡
这里注意我们需要使用ValueAnimation.ofInt()方法来实现颜色的过渡 ,因为我们知道颜色值是int类型的,如果用ofFloat会弹出强转错误,然后必须要指定 ARGB四种都需要,其实呢,使用并没有什么难点,就是和移动位置一样

简单分析ArgbEvaluator的实现原理
简单的说就是通过位操作符将16进制的ARGB色值的每一部分单独提取出来进行处理 再根据fracation计算出每种A,R,G,B当前的值,通过|运算再将ARGB合成色值。



3.3ValueAnimator进阶——ofObject

概述:由于ofInt和ofFloat只能传入int或者float的值,如果我们需要操作其他类型的变量就不能用这两种方法。
所以我们要使用ofObject,这个函数可以传入任意类型的变量,这个函数有两个参数,
第一个参数:自定义的Evaluator
第二个参数:属于Object类型的任意参数。

为什么要强制传入自定义的Evaluator呢?
由于系统并不知道我们要将数值进度转换出来的值具体是什么
所以必须要我们使用自定义的Evaluator来告诉系统如何实现

下面我们尝试用ofObject函数来实现TextView中的字从A变成Z
我们要怎么做呢?首先我们需要传入什么呢?
我们需要传入的是char类型的’A‘和’Z‘,
然后自定义Evaluator来告诉系统的数值进度以及返回值,这里的返回值是char类型
首先将object类型强转为char类型的即可,这就是简单的使用




3.4ObjectAnimator

ValueAnimator的缺点:只能对动画中的数值进行计算,如果想要对某个控件执行操作,就需要监听 ValueAnimator的动画过程,相比于补间动画要繁琐的多,为了让动画能够直接与对应的控件相关联 开发人员再ValueAnimator的基础上派生了一个类ObjectAnimator,由于ObjectAnimator是派生自ValueAnimator ,所以ValueAnimator中的函数在ObjectAnimator中总是可以使用的

ObjectAnimator需要重写几个函数,比如ofInt()和ofFloat()

下面用ObjectAnimator重写ofFloat实现一个动画

>>>>三行代码搞定一个控件由可见到不可见再变为可见<<<<

ObjectAnimator animator=ObjectAnimator.ofFloat(Imageview,“alpha” ,1.0f,0.0f,1.0f);
animator.setDuration(5000);
animator.start();

下面来分析一下该构造方法的三种参数
第一个参数是用于绑定控件的,指定对哪一个控件起作用
第二个参数是用于指定这个动画要操作这个控件的那些属性
第三个参数是可变长参数,是指这个属性值如何变化

从代码中可以看出,我们只需要指定第二个参数的属性,即可以实现动画效果
那么,让我们思考一下,系统怎么知道第二个参数值是什么呢?
其实系统是通过传递进来的参数找到相对应的set方法,从而改变他们的值

系统中已经存在可以直接使用的四种方法,其实就是之前说的四种标签。
alpha,translate,rotation,scale,有的还针对xy两个轴单独做了set方法,比如setScaleX()

总结:要使用Objectanimator来构造动画,在要操作的控件中必须要存在对应属性的set函数,
而且参数类型必须于构造所使用的ofFloat或者ofInt一致
set函数的命名必须要采用骆驼拼写法,即set后的首个字母大写其余字母小写

还是简单的看看上述几个set函数
setRotationX(float x):表示围绕着X轴旋转,x表示旋转度数
setRotationY(float y):表示围绕着Y轴旋转,y表示旋转度数

setTranslateX():表示在X轴平移的距离
setTranslateY():表示在Y轴平移的距离
其他的不说了,都一样,下面我们重点介绍Objectanimator的原理

3.4.2ObjectAnimator动画原理
再复习一下Valueanimator的动画流程:
ofInt(0,400)——》插值器(返回当前数值进度,如0.2)——》Evaluator(根据数值进度计算实际进度) ——》监听器返回(在AnimatorUpdateListener()中返回

再来看看ObjectAnimator的动画流程
ofFloat(tv,“scaleY”,0,3,1)——》插值器(返回当前数值进度)——》Evaluator——》调用set函数(根据属性拼接set函数并反射调用,将当前值作为参数传入)

几点注意事项:
1.set的方法名必须要符合规则,就是他会将属性的第一字母大写,比方说属性是length,那么他会找到setLength()方法,你在写属性的时候可以写length也可以写Length,他都会找到setLength()方法

2.如何确定函数的参数类型呢?
由于插值器和Evaluator都有两种类型,一种是float一种是int,所以我们再写set方法时,最好也能写两种参数的构造方法,避免报错

3.4.3自定义ObjectAnimator属性
ObjectAnimator属性中有三个构造函数
ofFloat,ofInt,ofObject和Valueanimator的构造方法差不多,只是多了属性名

总结:如何对自定义的控件使用ObjectAnimator.ofObject方法来改变我们想要改变的属性呢?
第一步:自定义控件中需要添加setXXX(属性)的方法,这样我们调用ofObject传入属性时,就会反射到该setXXX方法

第二步:如何给object方法传入自定义对象,比如Point(0,0);
我们先创建一个该对象的构造方法,然后通过将对象传入ofObject方法中,然后重写Evaluator的object方法

将startvalue和endvalue强制转换成point对象,获得传进来的point对象的x,y的值,并通过对fraction和startvalue以及endvalue来返回一个point对象,这个对象携带当前的xy值进度
当然这样我们必须要传入自定义的Evaluator,否则系统不知道如何处理传进来的point对象。

重点:通过反射机制来调用相应的set函数

3.4.4何时需要实现对应属性的get函数
当且仅当动画只有一个过渡值时,系统才会调用对应属性的get方法来得到动画的初始值,当不存在get函数时则会取动画参数类型的默认初始值,当无法取得默认类型的初始值时,会直接崩溃。

3.5组合动画——AnimationSet
3.5.1.playSequentially()和playTogether()函数
playSequentially():所有动画依次播放
playTogether():所有动画同时播放

1.playSequentially声明如下
void playSequentially(Animator items)
void playSequentially(List)
第一个构造函数是常用的,他的参数是可变长参数,也就是说我们可以传入任意多的animator对象
传入的animator会依次播放

第二个构造函数传入一个Animator列表,原理一样

总结:playTogether()和playSequentially()函数在开始动画时,只是把每个控件的动画**,至于每个控件自身的动画是否延时,是否无限循环,只于控件自身的动画设定有关,与这两个函数无关,他们只负责再某个时间**动画

4.如何实现组合动画的无限循环播放
主要就是给每个单独的动画设置无限循环播放,animatorset只关注每个动画的播放顺序,其他的还是由动画本身来控制的

AnimatorSet.Builder()

1.概述
虽然之前说的playSeqentially()和playTogether()能够实现播放必须的控制,但是不是非常的* ,比如说我们想要实现先播放C动画,再同时播放A,B动画,再不设置延时的前提下, 上述两个是无法实现的

AnimatorSet.Builder builder=animatorSet.play(xxx)

2.Animator.Builder的函数
AnimatorSet.Builder builder对象是通过animatorset.play函数生成的对象
//play();表示要播放某个动画
//with();//和前面的动画一起执行
//before(xxx);在xxx动画之前执行
//after(xxx):在xxx动画之后执行
//after(long delay):延迟几秒再执行,所有的动画都是以play为基准的
play表示当前所播放的动画

3.5.3AnimatorSet监听器
与ValueAnimator和ObjectAnimator的监听器一样,这里不再重复 不同的是onAnimatorRepeat()函数,由于Animatorset并没有设置重复的函数,故这个函数 并不会执行

AnimatorSet设置相关函数比如
setDraution()和setInterpolator()会覆盖objectAnimator的设置

animatorSet.setTarget()函数的作用是将所有的动画效果统一绑定到传入的控件上。

3.5.5路径动画

让我们先来思考一下这种动画怎么实现呢?
既然名叫路径动画可能和路径有关!
这里有一个关于路径动画的示例————自定义弹出式菜单
[自定义弹出式菜单](https://blog.csdn.net/weixin_43927892/article/details/100549167)

3.6Animator动画的XML实现

在xml中与Animator对应的有三个标签
animator:对应ValueAnimator
objectAnimator:对应的ObjectAnimator
set:对应AnimatorSet

下面具体看看每个标签的作用:

animator的所有字段以及作用
android:duration:动画播放时长
android:valueFrom:初始动画值
android:valueTo:动画结束值
android:startoffset:动画**延时,对应代码中startDelay()函数
android:repeatCount:动画重复次数
android:repeatMode:动画重复类型
android:valueType:表示参数类型,取值intType或者FloatType
注意:如果android:valueFrom和Android:value To的值设置为color类型,则不需要设置这个参数
android:interpolator:设置插值器

在代码中如何引入xml资源?
ValueaAnimator valueanimator=(ValueAnimator)AnimatorInflater.loadAnimator(XXXActivity.this,R.animator.animator)

3.6.2objectAnimator标签
1.概述
下面是完整的objectAnimator标签所有的字段以及取值范围
android:propertyName=“指定某个属性”
android:duration=“int”
android:valueFrom=“float|int|color”
android:valueTo=""

关于属性动画的个人总结! 首先,Android为什么要推出属性动画呢? 答:属性动画和视图动画相比,视图动画是针对view来操作的,而属性动画则不同,他不是针对控件本身的,他只在乎数值的变化
第二,属性动画的分类以及每种分别的适应场景使用方法? 属性动画分为:ValueAnimator和ObjectAnimator。 ValueAnimator有主要的三种构造方法,ofInt(),ofFloat(),ofObject(); ofInt():适用于传参时用的int方法 ofFloat():适用于传参时用的Float方法 ofObject():适用于自定义传参类型,比如一个点之类的,但需要先构造,提供getset方法,并告诉 Evaluator如何去实现变化,这个是需要我们去定义Evaluator的.

Valueanimator的常用方法
代码创建:
ValueAnimator animator=ValueAnimator.ofInt();
xml文件创建:
ValueAnimator animator=AnimatorInflater.loadanimator(xxx,R.animtor.xxx)

setDruation():设置延时
addUpdateListener():这个方法呢主要就是我们用来获取当前进度的方法,我们可以根据返回的值来动态的改变
一些view的位置
addListener():设置监听,主要监听四个状态开始结束重复取消
setDelay()设置延时,这个有时候可能排上大用场
setEvaluator():自定义这个时间段内数值的变化。
setInterpolator:设置插值器,可以自定义
start():开始
cencle():取消

简单的总结一下ValueAnimator的使用流程与原理 使用流程:通过ValueAnimator.ofInt()或者ofFloat()又或者ofObject()来构造一个ValueAnimator对象,拿到对象以后我们通过设置他的相关属性:比如持续时间,插值器,Evaluator,添加监听,设置延迟,等等,在监听中改变想要改变的属性。

注意事项:需要注意的就是你选择不同的构造方法,一定要让参数类型相同,比如你自定义插值器和Evaluator,那就需要注意这些返回的参数,参数不一致就会出现强转错误,导致程序崩溃。

ObjectAnimator和ValueAnimator的对比
为什么有了ValueAnimator还需要ObjectAnimator? ValueAnimator虽然功能比较强大,但是唯一的缺点就是我们需要自己去手动的去控制动画去控件的关系,而objectanimator支持我们直接去绑定控件。下面再具体说说ObjectAnimator的使用。

ObjectAnimator的使用方法
代码创建:
ObjectAnimator animator=ObjectAnimator.ofInt();
xml文件创建:
ObjectAnimator animator=AnimatorInflater.loadanimator(xxx,R.animtor.xxx)

常用的构造方法也有三种ofInt,ofFloat,ofObject()
不同的是我们需要传递的参数不同,我们需要传递porpertyName,Evaluator(ofObject),以及起始值和target(目标控件)
如果我们需要自定一种porpertyName,只需要提供他的set方法。get方法仅仅在你只传递了一个起始值的时候会被调用,相当于提供一个默认的初始值

ObjectAnimator的相关方法和ValueAnimator相似。
如果要说ObjectAnimator的强大之处:就是支持我们自定义属性去进行改变,以及使用更简单,一行代码可以解决一种变换

下面再对AnimatorSet进行简单的总结
代码创建:
AnimatorSet支持我们同时操作若干个动画,提供的两种方法:
playSequentially():按照传递进来的顺序依次播放动画
playTogether():同时播放所有的动画
他其实是一个动画集合,他的一些属性和动画本身相同,比如设置持续时间。但是需要注意,如果给set设置了属性,会覆盖
动画本身的该属性。

AnimatorSet.Builder():该对象创建
AnimatorSet.Bulider set=animatorset.play(xxx),
通过set可以对动画设置具体的播放时序,非常*,animatorset只支持两种播放,同时或者依次序
但是builder可以*的决定动画播放时序

大概内容就是这些,属性动画入门结束!

该博客是Android自定义控件入门与实战的学习笔记,参考资料为启舰大神的Android自定义控件入门与实战书籍,下一篇,属性动画进阶!

相关标签: 属性动画