Android开发(12)——属性动画
2021-03-27 本文已影响0人
让时间走12138
本节内容
1.菜单效果实战
2.旋转动画
3.缩放平移透明度动画
4.ViewPropertyAnimator
5.属性动画实现菜单效果
image.png一、菜单效果实战(补间动画实战)
1.做一个简单的菜单,点击一个按钮,然后从按钮下方弹出一些控件。
如上图,点击一下红色按钮,然后就会弹出黑色的这些控件,再点击一下,它们又会收回去。这些控件出来的时候,会带有一些旋转和移动的动画。(按道理,这些黑色按钮应该是在一条竖线上的,但是这里没有,可能是因为我出了点bug)
2.红色的按钮在最上方,黑色的那些按钮都在红色按钮的后面,所以一开始只有红色按钮显示出来。
3.最开始配置一下activity_main.xml,先拖动这些图片到drawable中,然后给每个按钮都设置一个id,然后用约束布局,布局一下。注意:这个时候要把所有的黑色按钮都放在红色按钮后面。最外面只有一个红色按钮。
4.按钮的旋转动画是一样的,但是移动动画不一样。.因为旋转动画都是一样的,所以旋转动画直接用xml布局。创建一个anim包,然后新建一个resourcefile
<rotate xmlns:android="http://schemas.android.com/apk/res/android"
android:fromDegrees="0"
android:toDegrees="360"
android:pivotX="50%"
android:pivotY="50%"
/>
5.在MainActivity里面写一个函数,实现加载旋转动画。
private fun loadRotateAnim() = AnimationUtils.loadAnimation(this,R.anim.rotate_anim)
6.用代码实现加载移动动画的方法。
private fun loadTranslateAnim(index:Int,isOpen:Boolean):TranslateAnimation{
var startY:Float = 0f
var endY :Float = 0f
if(isOpen){
endY = ((index+1)*(menu1.height+7)).toFloat()
}else{
startY = ((index+1)*(menu1.height+7)).toFloat()
}
return TranslateAnimation(0f,0f,startY, endY)
}
isOpen:用来记录菜单现在是打开的还是闭合的。
index是从0开始的,所以我们设置移动的距离为(index+1)*(menu1.height+7),也就是黑色按钮的高度+7,然后乘以相应的倍数,这样的排列就比较美观。
7.因为六个黑色按钮要同时实现动画效果,所以要把这几个按钮用数组存储一下
private val menus : Array<ImageView> by lazy {
arrayOf(menu1,menu2,menu3,menu4,menu5,menu6)
}
8.给红色按钮添加点击事件
menuBtn.setOnClickListener{
for((i,item) in menus.withIndex()) {
//旋转动画
//移动动画
//AnimationSet 包裹这两个动画
AnimationSet(true).apply {
addAnimation(loadRotateAnim())
addAnimation(loadTranslateAnim(i,isOpen))
duration=1000
fillAfter= true
interpolator = BounceInterpolator()
item.startAnimation(this)
}
}
isOpen = !isOpen
}
interpolator = BounceInterpolator(),设置一个弹簧效果,看起来比较美观。interpolator:插值器。动画从起始到结束有一个过程,默认是匀速的,但是这个插值器就让它这个过程有了一些特殊的节奏。插值器有很多种类型,这个弹簧效果只是其中一种。
9.设置一个变量来记录菜单是打开还是收回的。(前面是最终的代码,所以提前出现了isOpen)
private var isOpen:Boolean = true
结合前面loadTranslateAnim里的代码食用,如果菜单是打开的,那么endY为(index+1)(menu1.height+7),否则satrtY=(index+1)(menu1.height+7)。
10.这个demo有一个bug,当弹出菜单序列的时候,我们点击黑色的按钮,但是并没有任何效果。因为补间动画只是 视觉上的动画效果,并没有真正改变控件的对应属性,所以这个按钮并不会真正被点击。例如我们添加以下代码:
menu1.setOnClickListener{
Log.v("swl","相机按钮被点击了")
}
当我们点击黑色按钮的时候,并不会出现“相机按钮被点击了”的文字提示。按钮的实体还是在红色按钮后面,它的y坐标并没有真正被改变。
想要解决这个问题,还是需要用上属性动画,它可以真正更改视图对应的属性。
二、旋转动画
1.先在xml中随便布局一下,添加一个矩形和一个按钮,分别给它们添加一下id,然后给按钮设置一下点击事件。
2.对于属性动画来说,不管是旋转,平移,缩放等都是ObjectAnimator来进行管理的。
ObjectAnimator.ofFloat(view,"rotationX",0f,360f).apply {
duration = 1000
addListener(object :Animator.AnimatorListener{
override fun onAnimationRepeat(animation: Animator?) {
}
override fun onAnimationEnd(animation: Animator?) {
}
override fun onAnimationCancel(animation: Animator?) {
}
override fun onAnimationStart(animation: Animator?) {
}
})
addPauseListener(object :Animator.AnimatorPauseListener{
override fun onAnimationPause(animation: Animator?) {
}
override fun onAnimationResume(animation: Animator?) {
}
})
addUpdateListener (object :ValueAnimator.AnimatorUpdateListener{
override fun onAnimationUpdate(animation: ValueAnimator?) {
}
})
start()
}
第一个参数view,为动画的对象。第二个参数“rotationX”表示绕着水平中心线旋转,后面是旋转的起始度数和结束度数。
addListener:动画的监听器,有一些必须实现的方法。可以监听动画的开始、结束、取消和重复。
addPauseListener:暂停动画的监听器。可以监听动画暂停了,动画又重新开始了。
addUpdateListener:获取数据的更新范围。
三、缩放平移透明度动画
1.缩放动画
val scaleX= ObjectAnimator.ofFloat(view,"scaleX",0.1f,1.2f,1.0f,1.5f).apply {
duration =1000
}
val scaleY= ObjectAnimator.ofFloat(view,"scaleY",0.1f,1.2f,1.0f,1.5f).apply {
duration =1000
}
如果想让宽和高同时动,那么可以使用两个函数。 playSequentially(scaleX,scaleY)顺序播放, playTogether(scaleX,scaleY)同时播放。
AnimatorSet().apply {
//顺序播放
playSequentially(scaleX,scaleY)
//同时播放
playTogether(scaleX,scaleY)
start()
}
还有一个方法可以让宽和高的动画同时播放。
val holderX=
PropertyValuesHolder.ofFloat("scaleX",0.1f,1.2f,1.0f,1.5f)
val holderY =
PropertyValuesHolder.ofFloat("scaleY",0.1f,1.2f,1.0f,1.5f)
ObjectAnimator.ofPropertyValuesHolder(view,holderX,holderY).apply {
duration = 1000
start()
}
2.透明度动画
ObjectAnimator.ofFloat(view,"alpha",0.5f,1.0f,0f).apply {
duration =1000
start()
}
透明度从0.5f变为1.0f,再变为0f。中间值可以设很多,它都会经历。
3.平移动画
ObjectAnimator.ofFloat(view,"translationX",300f,200f,250f).apply {
duration = 1000
start()
先向右平移300f,再移动到200f,最后移动到250f,都是相对于控件最右侧的距离
如果想让x和y方向上都有移动,那么可以使用PropertyValuesHolder
val tx = PropertyValuesHolder.ofFloat("translationX",300f,200f,250f)
val ty = PropertyValuesHolder.ofFloat("translationY",300f,200f,250f)
ObjectAnimator.ofPropertyValuesHolder(view,tx,ty).apply {
duration =1000
start()
}
四、ViewPropertyAnimator
1.如果是对view的属性来进行动画 可以使用简洁版的ObjectAnimator -
ValuePropertyAnimator
旋转360度
view.animate().rotation(360f)
透明度,淡出到0f。从1f到0f。
view.animate().alpha(0f)
移动,每次向右移动100f。在之前的基础上移动。
view.animate().translationXBy(100f)
让x和y都移动
view.animate().translationXBy(100f)
.translationYBy(100f).apply {
duration =1000
}
缩放到1.5倍
view.animate().scaleX(1.5f)
.scaleY(1.5f)
多个动画同时进行
view.animate()
.scaleX(1.5f)
.scaleY(1.5f)
.alpha(0.5f)
.rotation(360f)
.alpha(1.0f).setDuration(2000)
2.ViewPropertyAnimator和ObjectAnimator的比较
ViewPropertyAnimator :使用方便 值单一 同时进行
ObjectAnimator :多个值 控制顺序或同时进行
3.ValueAnimator:动画过程中每个阶段的具体数据是用ValueAnimator来管理的,一般是用来自定义控件的。
ValueAnimator.ofFloat(0f,100f).apply {
duration =1000
addUpdateListener (object :ValueAnimator.AnimatorUpdateListener{
override fun onAnimationUpdate(animation: ValueAnimator?) {
Log.v("swl","${animation?.animatedValue}")
}
})
start()
}
我们需要的数据是0-100,那么我们点击按钮之后,它每隔一段时间就会产生一个新的值给我们,是一个中间的数据。
五、属性动画实现菜单效果
1.首先图片控件和判断菜单是否打开的变量和之前一样
private var isOpen:Boolean = true
private val menus :Array<ImageView> by lazy{
arrayOf(menu1,menu2,menu3,menu4,menu5,menu6)
}
2.给按钮设置点击事件。
menuBtn.setOnClickListener {
for((i,item)in menus.withIndex()){
//ObjectAnimator 多个值 1.5, 0.8 0.0
//ViewPropertyAnimator 一个值
//旋转 360
//移动 100
val ty = if(isOpen) (i+1)*(menu6.height+70).toFloat() else 0f
item.animate()
.rotation(360f)
.translationY(ty)
.interpolator = BounceInterpolator()
}
isOpen = !isOpen
}
因为只有纵向上的移动,所以用translationY()方法。
3.给黑色按钮设置点击事件
menu1.setOnClickListener{
Log.v("swl","相机打开了")
}
image.png
这个时候,再点击黑色按钮,底下就会打印相应的数据了。