注册

Android 带你重新认知属性动画

前言


之前写过一篇关于属性动画简单使用的文章juejin.cn/post/714417…


虽然官方直接提供的属性动画只有4个效果:透明度、位移、旋转、缩放,然后用Set实现组合,用插值器加一些效果。但其实属性动画能做的超越你的想象,他能做到anything。你可以实现各种你所想象的效果,改图片形状、路径的动画、颜色的变化等(当然这得是矢量图)。而插值器,除了系统提供的那些插值器之外,你还能进行自定义实现你想要的运动效果。


实现的效果


我这里拿个形变的效果来举例。可以先看看实现的效果:


sp.gif


实现要点


要点主要有两点:(1)要去想象,到了这种程度包括更复杂的效果,没有人能教你的,只能靠自己凭借经验和想象力去规划怎么实现。 (2)要计算,一般做这种自定义的往往会涉及计算的成分,所以你要实现的效果越高端,需要计算的操作就越复杂。


思路


我做这个播放矢量图和暂停矢量图之间的形变,这个思路是这样的: 其实那个三角形是由两部分组成,左边是一个矩形(转90度的梯形),右边是一个三角形。然后把两个图形再分别变成长方形。具体计算方式是我把width分成4份,然后配合一个偏移量offset去进行调整(计算的部分没必要太纠结,都是要调整的)


步骤:



  1. 绘制圆底和两个图形
  2. 属性动画
  3. 页面退出后移除动画

1. 绘制圆底和两个图形


一共三个Paint


init {
paint = Paint()
paint2 = Paint()
paint3 = Paint()

paint?.color = context.resources.getColor(R.color.kylin_main_color)
paint?.isAntiAlias = true
paint2?.color = context.resources.getColor(R.color.kylin_white)
paint2?.style = Paint.Style.FILL
paint2?.isAntiAlias = true
paint3?.color = context.resources.getColor(R.color.kylin_white)
paint3?.isAntiAlias = true
}

绘制圆底就比较简单


paint?.let {
canvas?.drawCircle((width/2).toFloat(), (height/2).toFloat(), (width/2).toFloat(),
it
)
}

然后先看看我的一个参考距离的计算(有这个参考距离,才能让图形大小跟着宽高而定,而不是写死)


override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
super.onLayout(changed, left, top, right, bottom)
if (baseDim == 0f){
baseDim = (0.25 * width).toFloat()
}
}

另外两个图用路径实现


if (path1 == null || path2 == null){
path1 = Path()
path2 = Path()
// 设置初始状态
startToStopAnim(0f)
}
paint2?.let { canvas?.drawPath(path1!!, it) }
paint3?.let { canvas?.drawPath(path2!!, it) }

看具体的绘制实现


private fun startToStopAnim(currentValue : Float){
val offset : Int = (baseDim * 0.25 * (1-currentValue)).toInt()

path1?.reset()
path1?.fillType = Path.FillType.WINDING
path1?.moveTo(baseDim + offset, baseDim) // 点1不变
path1?.lineTo(2 * baseDim+ offset - baseDim/3*currentValue,
baseDim + (0.5 * baseDim).toInt() * (1-currentValue))
path1?.lineTo(2 * baseDim+ offset - baseDim/3*currentValue,
2 * baseDim +(0.5 * baseDim).toInt() + (0.5 * baseDim).toInt() * currentValue)
path1?.lineTo(baseDim+ offset, 3 * baseDim) // 点4不变
path1?.close()


path2?.reset()
path2?.fillType = Path.FillType.WINDING
if (currentValue <= 0f) {
path2?.moveTo(2 * baseDim + offset, baseDim + (0.5 * baseDim).toInt())
path2?.lineTo(3 * baseDim + offset, 2 * baseDim)
path2?.lineTo(2 * baseDim + offset, 2 * baseDim + (0.5 * baseDim).toInt())
}else {
path2?.moveTo(2 * baseDim+ offset + baseDim/3*currentValue,
baseDim + (0.5 * baseDim).toInt() * (1-currentValue))
path2?.lineTo(3 * baseDim + offset, baseDim + baseDim * (1-currentValue))
path2?.lineTo(3 * baseDim + offset, 2 * baseDim + baseDim * currentValue)
path2?.lineTo(2 * baseDim+ offset + baseDim/3*currentValue,
2 * baseDim +(0.5 * baseDim).toInt() + (0.5 * baseDim).toInt() * currentValue)
}
path2?.close()
}

这个计算的过程不好解释,加偏移量就是一个调整的过程,可以去掉偏移量offset看看效果就知道为什么要加了。path1代表左边的路径,左边的路径是4个点,path2是右边的路径,右边的路径会根据情况去决定是3个点还是4个点,默认情况是3个。


2、属性动画


fun startToStopChange(){
isRecordingStart = true
if (mValueAnimator1 == null) {
mValueAnimator1 = ValueAnimator.ofFloat(0f, 1f)
mValueAnimator1?.addUpdateListener {
val currentValue: Float = it.animatedValue as Float
startToStopAnim(currentValue)
postInvalidate()
}
mValueAnimator1?.interpolator = AccelerateInterpolator()
}
mValueAnimator1?.setDuration(500)?.start()
}

float类型0到1其实就是实现一个百分比的效果。变过去能实现后,变回来就也就很方便


fun stopToStartChange(){
isRecordingStart = false
if (mValueAnimator2 == null) {
mValueAnimator2 = ValueAnimator.ofFloat(1f, 0f)
mValueAnimator2?.addUpdateListener {
val currentValue: Float = it.animatedValue as Float
startToStopAnim(currentValue)
postInvalidate()
}
mValueAnimator2?.interpolator = AccelerateInterpolator()
}
mValueAnimator2?.setDuration(500)?.start()
}

3.移除动画


view移除后要移除动画


fun close(){
try {
if (mValueAnimator1?.isStarted == true){
mValueAnimator1?.cancel()
}
if (mValueAnimator2?.isStarted == true){
mValueAnimator2?.cancel()
}
}catch (e : Exception){
e.printStackTrace()
}finally {
mValueAnimator1 = null
mValueAnimator2 = null
}
}

然后还要注意,这个动画是耗时操作,所以要做防快速点击。


总结


从代码可以看出,其实并实现起来并不难,难的在于自己要有想象力,要能想出这样的一个过程,比较花费时间的可能就是一个调整的过程,其它也基本没什么技术难度。


我这个也只是简单做了个Demo来演示,你要问能不能实现其它效果,of course,你甚至可以先把三角形变成一个正方形,再变成两个长方形等等,你甚至可以用上贝塞尔来实现带曲线的效果。属性动画就是那么的强大,对于矢量图,它能实现几乎所有的你想要的效果,只有你想不到,没有它做不到。


作者:流浪汉kylin
链接:https://juejin.cn/post/7144190782205853732
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

0 个评论

要回复文章请先登录注册