Skip to content

ObjectAnimator 更改视图位置

如果要改变某个View的位置,直接修改坐标会有一种刷的一下突然改变位置的感觉。 为了让它看起来更自然一些,我们可以加入动画,位置移动看起来更顺滑。

我们可以用ObjectAnimator来实现动画效果。配合上时间插值器,能控制动画的加减速。

接下来用一些例子来说明ObjectAnimator的用法。本文使用Kotlin来演示。

动画移动示例

mTv是被我们操作的View。同时它显示了当前它的x,ytranslationX/Y

private val mUpdateListener = ValueAnimator.AnimatorUpdateListener {
    mTv.text = "RustFisher\n(${mTv.x.toInt()},${mTv.y.toInt()}); (${mTv.translationX.toInt()},${mTv.translationY.toInt()})"
}
这个mUpdateListener不用也行。这里只是为了显示坐标。

TRANSLATION_X

这个小例子是为了演示TRANSLATION_X修改的动画。水平方向的动画,时常1秒。

val ani = ObjectAnimator.ofFloat(mTv, View.TRANSLATION_X, 0f, 200f, 640f).apply {
    duration = 1000
}
ani.addUpdateListener(mUpdateListener)
ani.start()

使用静态方法ObjectAnimator.ofFloat获得ObjectAnimator对象。

ofFloat(T target, Property<T, Float> property, float... values)

  • target 是要移动的对象,我们传入一个View
  • property 是要更改的属性
  • float... values 是目的数值

注意这里都是用float。values里我们传入了3个数字。开始到结束时从0变化到640。 动画将会分成2个部分,前500毫秒是0到200;后500毫秒是200到640。

apply里,动画时间duration设置为1000毫秒。

动画效果

可以看到,最后停止的位置并不是我们指定的640,而是差那么1像素。

实际上,如果不用监听器,可以写成这样

ObjectAnimator.ofFloat(mTv, View.TRANSLATION_X, 0f, 200f, 640f).apply {
    duration = 1000
    start()
}

TRANSLATION_Y

同理,我们也可以改变View.TRANSLATION_Y

val ani = ObjectAnimator.ofFloat(mTv, View.TRANSLATION_Y, 100f).apply {
    duration = 1000
}
ani.addUpdateListener(mUpdateListener)
ani.start()
values里只传入了100f。表示从当前值变化到100f。这个是目标值。

X,Y

我们也可以直接改变View的X和Y

val ani = ObjectAnimator.ofFloat(mTv, View.X, 500f).apply {
    duration = 800
}
ani.addUpdateListener(mUpdateListener)
ani.start()
// 或者
val ani = ObjectAnimator.ofFloat(mTv, View.Y, 700f).apply {
    duration = 700
}
ani.addUpdateListener(mUpdateListener)
ani.start()

上下左右

可以以View当前位置为出发点,计算出目标点。实现一个控制上下左右移动的效果。

例如控制向上,让Y减少100。

val ani = ObjectAnimator.ofFloat(mTv, View.Y, mTv.y - 100).apply {
    duration = 200
}
ani.addUpdateListener(mUpdateListener)
ani.start()

沿指定路径移动

除了水平/垂直移动,我们还可以指定一个路径给动画。 要用到Path类。

圆弧

指定一个圆弧路径。Path().apply{ }里指定圆弧的参数。

val path = Path().apply {
    arcTo(0f, 0f, 960f, 1200f, 270f, -180f, true)
}
val ani = ObjectAnimator.ofFloat(mTv, View.X, View.Y, path).apply {
    duration = 2000
}
ani.addUpdateListener(mUpdateListener)
ani.start()

折线

同样是利用Path,我们自己绘制一个折线路线。

val path = Path().apply {
    moveTo(0f, 0f)
    lineTo(800f, 400f)
    lineTo(0f, 600f)
    lineTo(800f, 800f)
    lineTo(0f, 1000f)
}
val ani = ObjectAnimator.ofFloat(mTv, View.X, View.Y, path).apply {
    duration = 3000
}
ani.addUpdateListener(mUpdateListener)
ani.start()
这是个长达3秒的动画。从(0,0)出发,最后抵达(0,1000)。视觉上就是走了一个折线。 创建Path时,先moveTo(0f, 0f)回到(0,0),然后用lineTo把路径绘制出来。

参考

作者: RustFisher
联系: rf.cs@foxmail.com
博客: rustfisher.com | RustFisher cnblog
示例: AndroidTutorial Gitee, Tutorial Github
链接: https://www.an.rustfisher.com/android/ui/view/animation/object-animator-intro/
一家之言,仅当抛砖引玉。如有错漏,还请指出。