一、简单属性动画
1.1 通过 xml 实现动画
基本的属性介绍:
在res/animator文件夹下,创建animator_translation.xml文件。XML文件有四个标签可用,要注意到propertyValuesHolder标签的Android 版本适配。
set
标签对应代码的AnimatorSet
,只有一个属性可以设置:android:ordering
,取值:同时播放together
、顺序播放sequentially
。animator
标签对应代码的ValueAnimator
,可以设置如下属性:
android:duration
:动画时长android:valueType
:属性类型,intType
、floatType
、colorType
、pathType
android:valueFrom
:属性初始值android:valueTo
:属性结束值android:repeatCount
:重复次数android:repeatMode
:重复模式android:interpolator
:插值器,可看下一节默认插值器。android:startOffset
:延迟,对应startOffset()
延迟多少毫秒执行
调用:
val alpha = AnimatorInflater.loadAnimator(this, R.animator.alpha_test)
alpha.setTarget(tv_property_anim)
alpha.start()
注意属性名不能写错,全部属性名如下:
const val ALPHA = "alpha"
const val SCALE_X = "scaleX"
const val SCALE_Y = "scaleY"
const val ROTATION = "rotation"
const val ROTATION_X = "rotationX"
const val ROTATION_Y = "rotationY"
const val TRANSLATION_X = "translationX"
const val TRANSLATION_Y = "translationY"
示例:
- 渐变
```java
<?xml version=”1.0” encoding=”utf-8”?>
- 渐变
```java
<?xml version=”1.0” encoding=”utf-8”?>
- 2. 旋转
```java
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="rotation"
android:valueFrom="0"
android:valueTo="180"
android:duration="2000"
android:valueType="floatType">
</objectAnimator>
- 平移
```java
<?xml version=”1.0” encoding=”utf-8”?>
- 平移
```java
<?xml version=”1.0” encoding=”utf-8”?>
- 4. 缩放
```java
<?xml version="1.0" encoding="utf-8"?>
<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="2"
android:duration="2000"
android:valueType="floatType"
>
</objectAnimator>
动画集合
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
>
<objectAnimator android:duration="3000"
android:propertyName="alpha"
android:valueFrom="0.2"
android:valueTo="1"/>
<objectAnimator android:duration="3000"
android:propertyName="scaleX"
android:valueFrom="1"
android:valueTo="2"/>
</set>
1.2 通过代码实现
顾名思义,通过控制对象的属性,来实现动画效果。官方定义:定义一个随着时间 (注:停个顿)更改任何对象属性的动画,无论其是否绘制到屏幕上。
可以控制对象什么属性呢?什么属性都可以,理论是通过set和get某个属性来达到动画效果。例如常用下面一些属性来实现View对象的一些动画效果。
位移:translationX、translationY、translationZ
透明度:alpha,透明度全透明到不透明:0f->1f
旋转:rotation,旋转一圈:0f->360f
缩放:水平缩放scaleX,垂直缩放scaleY
简单的效果图:
- 1. 位移动画
先看一下布局代码的实现:
<LinearLayout
android:id="@+id/llAddAccount"
android:layout_width="wrap_content"
android:layout_height="35dp"
android:layout_alignParentRight="true"
android:layout_marginTop="100dp"
android:layout_marginRight="-70dp"//将现有视图藏在屏幕的右边
android:background="@drawable/bg_10_10_fff">
<ImageView
android:id="@+id/ivMakeNote"
android:layout_width="35dp"
android:layout_height="30dp"
android:layout_gravity="center_vertical"
android:paddingLeft="2dp"
android:paddingTop="2dp"
android:paddingBottom="2dp"
android:src="@mipmap/ic_account_add" />
<TextView
android:id="@+id/tvAddAccount"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center_vertical"
android:paddingRight="12dp"
android:text="添加账户"
android:textColor="@color/colorPrimary"
android:textSize="14sp" />
</LinearLayout>
上面只是简单实现了布局,下面看看属性动画代码的实现:
llAddAccount.setOnClickListener {
val objectAnimation =ObjectAnimator.ofFloat(llAddAccount, "translationX", 0f, -70f)
objectAnimation.start()
}
到这里,我们才真正看到属性动画的影子。通过ObjectAnimator的工厂方法ofFloat我们得到一个ObjectAnimator对象,并通过该对象的start()方法,开启动画效果。
ofFloat()方法的第一个参数为要实现动画效果的View,例如这里整体效果的LinearLayout;第二个参数为属性名,也就是前面所说的:translationX,translationY,alpha,rotation,scaleX,scaleY等,这里要实现的是水平平移效果,所以我们采用了translationX;第三参数为可变长参数,第一个值为动画开始的位置,第二个值为结束值得位置,如果数组大于3位数,那么前者将是后者的起始位置。
注意事项:如果可变长参数只有一个值,那么ObjectAnimator的工厂方法会将值作为动画结束值,此时属性必须拥有初始化值和getXXX方法。
translationX和translationY这里涉及到的位移都是相对自身位置而言。例如 View在点A(x,y)要移动到点B(x1,y1),那么ofFloat()方法的可变长参数,第一个值应该0f,第二个值应该x1-x。
XML布局实现:
在res/animator文件夹下,创建animator_translation.xml文件,内容如下:
在代码上调用:
llAddAccount.setOnClickListener {
val objectAnimation =AnimatorInflater.loadAnimator(this,R.animator.animator_translation)
objectAnimation.setTarget(llAddAccount)
objectAnimation.start()
}
- 2、透明属性动画
透明度属性动画比较简单,即控制View的可见度实现视觉差动画效果。这里展示效果是从不透明到透明,再到不透明。
代码如下:
tvText.setOnClickListener {
val objectAnimation =ObjectAnimator.ofFloat(tvText, "alpha", 1f,0f,1f)
objectAnimation.duration=3000
objectAnimation.start()
}
ofFloat()方法将属性名换成了透明度alpha,并且可变长参数增加到了3个。给ObjectAnimator对象的duration属性设置了动画展示时间3秒,默认情况下300毫秒。
- 3、缩放属性动画
缩放可以通过控制scaleX和scaleY分别在X轴和Y轴上进行缩放,如下图在X轴中进行两次两倍缩放。
代码如下:
tvText.setOnClickListener {
val objectAnimation =ObjectAnimator.ofFloat(tvText, "scaleX", 1f,2f)
objectAnimation.duration=3000
objectAnimation.repeatCount=2
objectAnimation.repeatMode=ValueAnimator.REVERSE
objectAnimation.start()
}
ofFloat()方法传入参数属性为scaleX和scaleY时,动态参数表示缩放的倍数。设置ObjectAnimator对象的repeatCount属性来控制动画执行的次数,设置为ValueAnimator.INFINITE表示无限循环播放动画;通过repeatMode属性设置动画重复执行的效果,取值为:ValueAnimator.RESTART和ValueAnimator.REVERSE。
ValueAnimator.RESTART效果:(即每次都重头开始)
ValueAnimator.REVERSE效果:(即和上一次效果反着来)
- 4、旋转属性动画
旋转动画也比较简单,将一个View进行顺时针或逆时针旋转。
代码如下:
tvText.setOnClickListener {
val objectAnimation =
ObjectAnimator.ofFloat(tvText, "rotation", 0f,180f,0f)
objectAnimation.duration=3000
objectAnimation.start()
}
ofFloat()方法的可变长参数,如果后者的值大于前者,那么顺时针旋转,小于前者,则逆时针旋转。
- 5、AnimatorSet
如果想要一个动画结束后播放另外一个动画,或者同时播放,可以通过AnimatorSet来编排。
val aAnimator=ObjectAnimator.ofInt(1)
val bAnimator=ObjectAnimator.ofInt(1)
val cAnimator=ObjectAnimator.ofInt(1)
val dAnimator=ObjectAnimator.ofInt(1)
AnimatorSet().apply {
play(aAnimator).before(bAnimator)//a 在b之前播放
play(bAnimator).with(cAnimator)//b和c同时播放动画效果
play(dAnimator).after(cAnimator)//d 在c播放结束之后播放
start()
}
或者
AnimatorSet().apply {
playSequentially(aAnimator,bAnimator,cAnimator,dAnimator) //顺序播放
start()
}
AnimatorSet().apply {
playTogether(animator,bAnimator,cAnimator,dAnimator) //同时播放
start()
}
另有:
AnimatorSet ().apply {
play(aAnimator).after(1000) //1秒后播放a动画
start()
}
二、设置旋转或是平移中心
只能通过要设置动画的控件来设置,无论是 xml 还是代码实现动画都是这样,示例:
val rotateZ = AnimatorInflater.loadAnimator(this, R.animator.rorate_x)
//设置中心
tv_property_anim.pivotX = 0f
tv_property_anim.pivotY = 0f
tv_property_anim.invalidate()
rotateZ.setTarget(tv_property_anim)
rotateZ.start()
三、实现一个控件多个动画
- 使用 ValueAnimator
使用 ValueAnimator 改变控件的宽高:val valueAnimator = ValueAnimator.ofFloat(0f, 500f)
valueAnimator.setTarget(tv_property_anim)
valueAnimator.duration = 1000
valueAnimator.start()
valueAnimator.addUpdateListener {
tv_property_anim.translationY = it.animatedValue as Float
}
private fun testValueAnimatorAnim() {
val width2 = tv_property_anim.layoutParams.width
val width = tv_property_anim.width
LogUtils.e("$width----- $width2")
val valueAnimator = ValueAnimator.ofInt(width, width * 2)
valueAnimator.duration = 2000
valueAnimator.addUpdateListener {
//当前值
LogUtils.e(it.animatedValue)
tv_property_anim.layoutParams.width = it.animatedValue as Int
//刷新视图,即重新绘制,从而实现动画效果
tv_property_anim.requestLayout()
}
valueAnimator.start()
}
- 使用 ValueAnimator
- ObjectAnimator
val anim = ObjectAnimator.ofFloat(
tv_property_anim,
"AnimatorUtil.SCALE_X",
1f, 0f
).setDuration(2000)
anim.start()
anim.addUpdateListener {
val fl = it.animatedValue as Float
tv_property_anim.alpha = fl
tv_property_anim.scaleX = fl
tv_property_anim.scaleY = fl
}
- ObjectAnimator
- PropertyValuesHolder
btn_property_propertyValuesHolder.setOnClickListener {
val alpha = PropertyValuesHolder.ofFloat(AnimatorUtil.ALPHA, 1f, 0f, 1f)
val scalex = PropertyValuesHolder.ofFloat(AnimatorUtil.SCALE_X, 1f, 0f, 1f)
val scaley = PropertyValuesHolder.ofFloat(AnimatorUtil.SCALE_Y, 1f, 0f, 1f)
ObjectAnimator.ofPropertyValuesHolder(tv_property_anim,alpha,scalex,scaley).setDuration(2000).start()
}
- PropertyValuesHolder
- 使用 animtorset
val set = AnimatorSet()
val rotation = ObjectAnimator.ofFloat(tv_property_anim,AnimatorUtil.ROTATION,0f,90f)
val rotationX = ObjectAnimator.ofFloat(tv_property_anim,AnimatorUtil.ROTATION_X,0f,40f)
set.play(rotation).with(rotationX)
set.duration = 2000
set.start()
四、参考
Android属性动画,看完这篇够用了吧
Android中的View动画和属性动画
Android 属性动画:这是一篇全面 & 详细的 属性动画 总结&攻略
Android 属性动画(Property Animation) 完全解析
Android属性动画完全解析
- 使用 animtorset