自定义view⑤属性动画和硬件加速

366 阅读5分钟

属性动画

属性动画 ViewPropertyAnimator

//1. 属性动画ViewPropertyAnimator
view.animate()
    .translationX(200.dp)//横向移动
    .translationY(100.dp)//纵向移动
    .alpha(0.5f)//透明度
    .scaleX(2f)//横向放大
    .scaleY(2f)//纵向放大
    .rotation(90f)//旋转90度
    .setStartDelay(1000)//延迟一秒执行
26 ViewPropertyAnimator中的其他方法

属性动画 ObjectAnimator

使用方式:

  • 如果是自定义控件,需要添加 setter / getter 方法;
  • 用 ObjectAnimator.ofXXX() 创建 ObjectAnimator 对象;
  • 用 start() 方法执行动画。
  1. 圆放大效果 ofFloat

        val animator = ObjectAnimator.ofFloat(circleView, "radius", 150.dp)
        animator.startDelay = 1000
        animator.start()
    
    27
  2. 组合动画AnimatorSet

        val bottomFlipAnimator = ObjectAnimator.ofFloat(cameraView, "bottomFlip", 60f)
        bottomFlipAnimator.startDelay = 1000
        bottomFlipAnimator.duration = 1000
    
        val flipRotationAnimator = ObjectAnimator.ofFloat(cameraView, "flipRotation", 270f)
        flipRotationAnimator.startDelay = 200
        flipRotationAnimator.duration = 1000
    
        val topFlipAnimator = ObjectAnimator.ofFloat(cameraView, "topFlip", -60f)
        topFlipAnimator.startDelay = 200
        topFlipAnimator.duration = 1000
    
        val animatorSet = AnimatorSet()
        animatorSet.playSequentially(bottomFlipAnimator,flipRotationAnimator,topFlipAnimator)
        animatorSet.start()
    
    28
  3. PropertyValuesHolder

    • 效果同上
    val bottomFlipHolder = PropertyValuesHolder.ofFloat("bottomFlip", 60f)
    val flipRotationHolder = PropertyValuesHolder.ofFloat("flipRotation", 270f)
    val topFlipHolder = PropertyValuesHolder.ofFloat("topFlip", - 60f)
    val holderAnimator = ObjectAnimator.ofPropertyValuesHolder(cameraView, bottomFlipHolder,  flipRotationHolder,  topFlipHolder)
    holderAnimator.startDelay = 1000
    holderAnimator.duration = 2000
    holderAnimator.start()
    
  4. PropertyValuesHolder 配合Keyframe 细化动画

    • 下面是一个用关键帧做的回弹动画
        val length = 200.dp
        val keyframe1 = Keyframe.ofFloat(0f, 0f)
        val keyframe2 = Keyframe.ofFloat(0.2f, 1.5f * length)
        val keyframe3 = Keyframe.ofFloat(0.8f, 0.6f * length)
        val keyframe4 = Keyframe.ofFloat(1f, 1f * length)
        val keyframeHolder = PropertyValuesHolder.ofKeyframe("translationX", keyframe1, keyframe2, keyframe3, keyframe4)
        val animator = ObjectAnimator.ofPropertyValuesHolder(cameraView, keyframeHolder)
        animator.startDelay = 1000
        animator.duration = 2000
        animator.start()
    
    29
  5. Interpolator 插值器

    • Interpolator 其实就是速度设置器。你在参数里填入不同的 Interpolator ,动画就会以不同的速度模型来执行
        val animator = ObjectAnimator.ofFloat(cameraView,"translationX", 360.dp)
        animator.interpolator = AccelerateDecelerateInterpolator()//先加速再减速
    //    animator.interpolator = AccelerateInterpolator()//一直加速
    //    animator.interpolator = LinearInterpolator()//匀速
        animator.startDelay = 1000
        animator.duration = 1000
        animator.start()
    
  6. TypeEvaluator 估值器

    • 圆点移动效果
        val animator = ObjectAnimator.ofObject(
            pointView, "point", pointFEvaluator(), PointF(100.dp, 200.dp)
        )
        animator.startDelay = 1000
        animator.duration = 1000
        animator.start()
        
    //自定义估值器
    class pointFEvaluator:TypeEvaluator<PointF> {
        override fun evaluate(fraction: Float, startValue: PointF, endValue: PointF): PointF {
          val startX= startValue.x
          val endX = endValue.x
          val currentX = startX + (endX-startX)*fraction
          val startY= startValue.y
          val endY = endValue.y
          val currentY = startY + (endY-startY)*fraction
          return PointF(currentX,currentY)
        }
    }
    
    30
    • 文字滚动效果,模拟城市地区滚动效果
    //TypeEvaluator 文字滚动效果
    val animator = ObjectAnimator.ofObject(proviceView,"province",ProvinceEvaluator(),"澳门特别行政区")
    animator.startDelay = 1000
    animator.duration = 10000
    animator.start()
    
    class ProvinceView(context: Context?, attrs: AttributeSet?) : View(context, attrs) {
    ...
      private val paint = Paint(Paint.ANTI_ALIAS_FLAG).apply {
        textSize = 40.dp
        textAlign = Paint.Align.CENTER
      }
      var province = "北京市"
        set(value) {
        field = value
        invalidate()
      }
      ...
    }
    
    31
  7. Listeners 设置监听器

和 View 的点击、⻓按监听器一样,Animator 也可以使用 setXxxListener() addXxxListener() 来设置监听器。

  • 给动画设置监听器,可以在关键时刻得到反馈,从而及时做出合适的操作,例如在动画的属性更新时同步更新其他数据,或者在动画结束后回收资源等。
  • 设置监听器的方法, ViewPropertyAnimator 和 ObjectAnimator 略微不一样: ViewPropertyAnimator 用的是 setListener() 和 setUpdateListener() 方法,可以设置一个监听器,要移除监听器时通过 set[Update]Listener(null) 填 null 值来移除;而 ObjectAnimator 则是用 addListener() 和 addUpdateListener() 来添加一个或多个监听器,移除监听器则是通过 remove[Update]Listener() 来指定移除对象。
  • 另外,由于 ObjectAnimator 支持使用 pause() 方法暂停,所以它还多了一个 addPauseListener() / removePauseListener() 的支持;而 ViewPropertyAnimator 则独有 withStartAction() 和 withEndAction() 方法,可以设置一次性的动画开始或结束的监听。
  • ViewPropertyAnimator.setListener() / ObjectAnimator.addListener()
    • onAnimationStart(Animator animation) 开始执行时
    • onAnimationEnd(Animator animation) 动画结束时
    • onAnimationCancel
      • 当动画被通过 cancel() 方法取消时,这个方法被调用。
      • 需要说明一下的是,就算动画被取消,onAnimationEnd() 也会被调用。所以当动画被取消时,如果设置了 AnimatorListener,那么 onAnimationCancel() 和 onAnimationEnd() 都会被调用。onAnimationCancel() 会先于 onAnimationEnd() 被调用。
    • onAnimationRepeat(Animator animation)
      • 当动画通过 setRepeatMode() / setRepeatCount() 或 repeat() 方法重复执行时,这个方法被调用。
      • 由于 ViewPropertyAnimator 不支持重复,所以这个方法对 ViewPropertyAnimator 相当于无效。
  • ViewPropertyAnimator.setUpdateListener() / ObjectAnimator.addUpdateListener()
    • 和上面的两个方法一样,这两个方法虽然名称和可设置的监听器数量不一样,但本质其实都一样的,它们的参数都是 AnimatorUpdateListener。它只有一个回调方法:onAnimationUpdate(ValueAnimator animation)。
  • ObjectAnimator.addPauseListener()
  • ViewPropertyAnimator.withStartAction/EndAction()
    • 这两个方法是 ViewPropertyAnimator 的独有方法。它们和 set/addListener() 中回调的 onAnimationStart() / onAnimationEnd() 相比起来的不同主要有两点:
      • withStartAction() / withEndAction() 是一次性的,在动画执行结束后就自动弃掉了,就算之后再重用 ViewPropertyAnimator 来做别的动画,用它们设置的回调也不会再被调用。而 set/addListener() 所设置的 AnimatorListener 是持续有效的,当动画重复执行时,回调总会被调用。
      • withEndAction() 设置的回调只有在动画正常结束时才会被调用,而在动画被取消时不会被执行。这点和 AnimatorListener.onAnimationEnd() 的行为是不一致的。

ValueAnimator

这是最基本的 Animator,它不和具体的某个对象联动,而是直接对两个数值进行渐 变计算。使用很少。

硬件加速

  • 使用 CPU 绘制到 Bitmap,然后把 Bitmap 贴到屏幕,就是软件绘制;
  • 使用 CPU 把绘制内容转换成 GPU 操作,交给 GPU,由 GPU 负责真正的绘制, 就叫硬件绘制;
  • 使用 GPU 绘制就叫做硬件加速

如何加速

  • GPU 分摊了工作
  • GPU 绘制简单图形(例如方形、圆形、直线)在硬件设计上具有先天优势,会 更快
  • 流程得到优化(重绘流程涉及的内容更少)

缺陷

兼容性。由于使用 GPU 的绘制(暂时)无法完成某些绘制,因此对于一些特定的 API,需要关闭硬件加速来转回到使用 CPU 进行绘制。
如图,官方文档上关于硬件加速无法绘制的api列表

离屏缓冲

  • 离屏缓冲是什么:单独的一个绘制 View(或 View 的一部分)的区域
  • setLayerType() 和 saveLayer()
    • setLayerType() 是对整个 View,不能针对 onDraw() 里面的某一具体过程
      • 这个方法常用来关闭硬件加速,但它的定位和定义都不只是一个「硬件 加速开关」。它的作用是为绘制设置一个离屏缓冲,让后面的绘制都单 独写在这个离屏缓冲内。如果参数填写 LAYER_TYPE_SOFTWARE ,会把离屏缓冲设置为一个 Bitmap ,即使用软件绘制来进行缓冲,这样 就导致在设置离屏缓冲的同时,将硬件加速关闭了。但需要知道,这个 方法被用来关闭硬件加速,只是因为 Android 并没有提供一个便捷的方 法在 View 级别简单地开关硬件加速而已。
    • saveLayer() 是针对 Canvas 的,所以在 onDraw() 里可以使用 saveLayer() 来圈出具体哪部分绘制要用离屏缓冲
      • 然而......最新的文档表示这个方法太重了,能不用就别用,尽量用 setLayerType() 代替