首页 > 解决方案 > 在不破坏布局约束的情况下实现动画

问题描述

我需要构建自己的动画库/助手,只需通过更新动画的进度百分比就可以将视图从 A 点移动到 B 点。这必须用 Kotlin 编写,但如果需要,我可以用 java 翻译它。

我做了什么

class Animator(
        private val animations: List<Animation>
) {
    constructor(view: View,
                startPoint: Point2D = Point2D(view.x, view.y),
                endPoint: Point2D) : this(listOf(Animation(view, startPoint, endPoint)))

    private val transitionState: TransitionState = TransitionState(0)
    private val animationQueue: Handler = Handler()

    /**
     * Apply a progression in the animation from [startPoint] to [endPoint] or conversely depending
     * on the transition state.
     * When the [TransitionState.progress] reach 0 the position of the [view] to animate is at [startPoint]. When it
     * reach 100 the view will be at the [endPoint]
     *
     * @param percent   an Integer that must be between 0 and 100 because it's percentage. If the
     *                  given percent is below 0 it
     *                  will override it to 0. Same process when it's over 100.
     */
    fun progressTo(percent: Int) {
        for (anim in animations) {
            val finalPercent = if (percent > 100) 100 else if (percent < 0) 0 else percent
            if (Math.abs(transitionState.progress - finalPercent) < 10) {
                animationQueue.post {
                    anim.view.x = (Vector2D(anim.startPoint, anim.endPoint) % finalPercent).endPoint.x
                    anim.view.y = (Vector2D(anim.startPoint, anim.endPoint) % finalPercent).endPoint.y
                }
            } else {
                anim.view.animate().x((Vector2D(anim.startPoint, anim.endPoint) % finalPercent).endPoint.x)
                anim.view.animate().y((Vector2D(anim.startPoint, anim.endPoint) % finalPercent).endPoint.y)
            }
            transitionState.progress = finalPercent
        }
    }

    /**
     * Finish the animation to the endPoint or startPoint depending on the [TransitionState.progress]
     */
    fun finishAnimation() {
        if (transitionState.progress < 50) {
            progressTo(0)
        } else if (transitionState.progress >= 50) {
            progressTo(100)
        }
    }
}

data class TransitionState(
        var progress: Int,
        var isOnTransaction: Boolean = progress != 0 && progress != 100
)

data class Vector2D(
        val startPoint: Point2D,
        val endPoint: Point2D
) {
    operator fun rem(percent: Int): Vector2D {
        val finalPercent = if (percent > 100) 100 else if (percent < 0) 0 else percent
        return Vector2D(startPoint, Point2D(
                startPoint.x + ((endPoint.x - startPoint.x) * finalPercent / 100),
                startPoint.y + ((endPoint.y - startPoint.y) * finalPercent / 100)
        ))
    }
}

data class Animation(
        val view: View,
        val startPoint: Point2D = Point2D(view.x, view.y),
        val endPoint: Point2D
)

data class Point2D(
        val x: Float,
        val y: Float
)

如您所见,我正在使用属性 View.setX 和 View.setY 来移动视图。它很好地使用正确的动画和运动正确移动视图。

问题
当我使用 X 和 Y 值时,视图的布局父级的约束(LinearLayout、RelativeLayout 或 ConstraintLayout)没有更新或遵守。例如,约束布局中有一个视图(第一个捕获中的红色视图),它被约束到父级的左、右和下,如此布局编辑器捕获中所示
当我更改视图的 y 值时,底部约束不会更新(它不再适合父底部)。这是动画之前和动画之后的真实应用程序的捕获。
使用View.layout()View.invalidate()不会改变任何东西,如果有一种方法可以重绘和恢复整个布局,我不确定应用程序是否会继续运行......

编辑 我更正了一段代码(在 for 循环中),使动画更加流畅。

问题
是否可以在不破坏其约束的情况下更改视图的位置?

我很确定我误解了 android ui 定位/绘图中的某些内容,因此即使您无法回答我的问题,也请随时发表您对此的理解。提前谢谢你。

标签: androidanimationkotlin

解决方案


推荐阅读