前一段时间让实现一个类似弹幕的效果,从右到左飘过去,时间八秒,嗯,很好做,一个动画就ok了
布局代码:
文件代码:
val width = windowManager.defaultDisplay.width//获取屏幕的宽
val dp100 = R.dimen.dp100.toDimen()
btStart.setOnClickListener {
//播放一个平移代码
bt.startAnimator(AnimatorUtil.translationX, 8000, width.toFloat() + dp100, 0F)//这个工具类放在最后面
}
效果图:
现在改了需求,需要把弹幕的视图改成跟屏幕一样宽,然后动画时间还是8秒,但是需要在中间停留五秒
现在拆分需求
调整宽度,布局代码,调整宽度
然后把layout_marginLeft设置为负的屏幕的宽度
val layoutParams = LinearLayout.LayoutParams(width, R.dimen.dp40.toDimen().toInt())
layoutParams.leftMargin = -width
bt.layoutParams = layoutParams
然后直接改动画,把动画拆成三步,第一步从右走到中间,第二部停留五秒,第三部从中间走到左边
btStart.setOnClickListener {
bt.startAnimator(AnimatorUtil.translationX, 1500, width.toFloat() * 2, width.toFloat()) {
if (it == AnimatorUtil.END) {
btStart.postDelayed({
//从右往左进到中间动画结束后,
//在中间停五秒
//然后再播从中间往左走到外面的动画
bt.startAnimator(AnimatorUtil.translationX, 1500, width.toFloat(), 0F)
}, 5000)
}
}
}
效果图:
虽然上面的动画确实做到了效果,但是安全性不高,并且嵌套也很深,能不能优化一下呢?
我们可以使用动画差值器来进行优化,并且可以把上面实现的两个动画缩减到一个动画,而且中间不用做定时操作,提升了安全性,也减少了嵌套
我们可以实现动画差值器的接口(TimeInterpolator)来实现上面的效果,接口源码:
public interface TimeInterpolator {
float getInterpolation(float input);
}
该接口只有一个方法,输入的是动画执行的百分比,返回值也是动画执行的百分比,所以可以重写该方法来动态的调整进度实现变更的需求效果
代码如下:
btStart.setOnClickListener {
bt.startAnimator(AnimatorUtil.translationX, 8000, width.toFloat() * 2, 0F)
.setInterpolator {//添加一个动画差值器,这个是kt风格的代码,java也类似
return@setInterpolator when {
it <= 0.1875F -> //0.1875是1.5秒相对于8秒的比例值
//因为需要在0.1875(18.75%)走完0.5(50%),所以需要做一下数学计算
it / 0.1875F * 0.5F
it >= 0.8125F ->//0.8125是6.5秒相对于8秒的比例值
(it - 0.8125F) / 0.1875F * 0.5F + 0.5F
else -> 0.5F
}
}
}
效果和上面一样,而且使用动画差值器基本可以不修改原有动画代码,并且可以实现很多的效果,当然,取决于自身的数学水平..
附上动画工具类的代码:
import android.animation.Animator
import android.animation.ObjectAnimator
/**
* creator: lt 2019/8/9--18:46 [email protected]
* effect : 属性动画工具类
* warning:
*/
/**
* 动画回调的状态
*/
typealias AnimatorState = Int
/**
* 动画常用的属性和状态
*/
object AnimatorUtil {
/**开始动画*/
const val START: AnimatorState = 0
/**动画结束*/
const val END: AnimatorState = 1
/**取消了动画*/
const val CANCEL: AnimatorState = 2
/**重复动画*/
const val REPEAT: AnimatorState = 3
/**透明动画*/
const val alpha = "alpha"
/**x轴旋转*/
const val rotationX = "rotationX"
/**y轴旋转*/
const val rotationY = "rotationY"
/**缩放x轴*/
const val scaleX = "scaleX"
/**缩放y轴*/
const val scaleY = "scaleY"
/**位移x轴*/
const val translationX = "translationX"
/**位移y轴*/
const val translationY = "translationY"
}
/**
* 开始一个属性动画
*/
fun Any.startAnimator(funName: String,
time: Long,
vararg floats: Float,
listener: ((AnimatorState) -> Unit)? = null): ObjectAnimator {
val objectAnimator = ObjectAnimator.ofFloat(this, funName, *floats)
.setDuration(time)
if (listener != null) {
objectAnimator.addListener(object : Animator.AnimatorListener {
override fun onAnimationRepeat(animation: Animator?) {
listener.invoke(AnimatorUtil.REPEAT)
}
override fun onAnimationEnd(animation: Animator?) {
listener.invoke(AnimatorUtil.END)
}
override fun onAnimationCancel(animation: Animator?) {
listener.invoke(AnimatorUtil.CANCEL)
}
override fun onAnimationStart(animation: Animator?) {
listener.invoke(AnimatorUtil.START)
}
})
}
objectAnimator.start()
return objectAnimator
}