Android开发(12)——属性动画

2021-03-27  本文已影响0人  让时间走12138

本节内容

1.菜单效果实战

2.旋转动画

3.缩放平移透明度动画

4.ViewPropertyAnimator

5.属性动画实现菜单效果

一、菜单效果实战(补间动画实战)
1.做一个简单的菜单,点击一个按钮,然后从按钮下方弹出一些控件。
image.png
  • 如上图,点击一下红色按钮,然后就会弹出黑色的这些控件,再点击一下,它们又会收回去。这些控件出来的时候,会带有一些旋转和移动的动画。(按道理,这些黑色按钮应该是在一条竖线上的,但是这里没有,可能是因为我出了点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
上一篇下一篇

猜你喜欢

热点阅读