如何将ViewGroup用作过渡动画的共享元素?

时间:2020-08-09 21:41:19

标签: android android-animation android-transitions shared-element-transition

我正在尝试在两个片段之间设置一个“共享元素” 过渡动画。但是,我想要的目的地不是单一视图,而是一个FrameLayout,其中两个重叠的元素共享大小(箭头和旋转的地图),并且必须同时移动和缩小。

我的目标布局如下:

    <FrameLayout
        android:id="@+id/container_arrow"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <androidx.fragment.app.FragmentContainerView
            android:id="@+id/map_container"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />

        <ar.com.lichtmaier.antenas.ArrowView
            android:id="@+id/arrow"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            />

    </FrameLayout>

我想将所有这些都视为一件事情。

在过渡之前,我使用缩放和平移属性在container_arrow上制作了此动画,并且效果很好。

但是,当我使用过渡效果时,尺寸动画只会影响外部FrameLayout,而不会影响其子元素。内箭头会移动,但不会从小处开始并逐渐增长,而是从大处开始并保持大状态。如果我改用箭头为目标,它将起作用。

看一下ChangeBounds过渡代码,看来它是使用setFrame()直接调整目标元素的边界的。那不会传播到它的孩子。

我需要平移+缩小动画来影响两个元素,但是过渡名称必须唯一。有什么方法可以实现我想要的吗?

编辑:

我已经在尝试通过调用以下命令将FrameLayout设置为一个组:

    ViewCompat.setTransitionName(arrowContainer, "animatedArrow")
    ViewGroupCompat.setTransitionGroup(arrowContainer, true) // <-- this

同一件事.. = /

2 个答案:

答案 0 :(得分:1)

这正是ViewGroupCompat.setTransitionGroup() API(适用于使用AndroidX Transition的API 14+设备)或android:transitionGroup="true" XML属性(适用于API 21+设备)的含义-通过将该标志设置为true,涉及共享元素过渡时,整个ViewGroup都用作单个项目。

请注意,还必须在设置为过渡组的同一元素上设置过渡名称(使用ViewCompat.setTransitionName() / android:transitionName,具体取决于您是想支持API 14还是仅支持API 21 +)。

答案 1 :(得分:0)

我最终创建了自己的Transition子类,该子类与ChangeBounds类似,但是使用平移和缩放视图属性移动目标而不是调整边界。计算翻译的增量并将其动画化为0,还计算初始比例并将其动画化为1。

这是代码:

class MoveWithScaleAndTranslation : Transition() {

    override fun captureStartValues(transitionValues: TransitionValues) {
        captureValues(transitionValues)
    }

    override fun captureEndValues(transitionValues: TransitionValues) {
        captureValues(transitionValues)
    }

    override fun getTransitionProperties() = properties

    private fun captureValues(transitionValues: TransitionValues) {
        val view = transitionValues.view
        val values = transitionValues.values

        val screenLocation = IntArray(2)
        view.getLocationOnScreen(screenLocation)
        values[PROPNAME_POSX] = screenLocation[0]
        values[PROPNAME_POSY] = screenLocation[1]

        values[PROPNAME_WIDTH] = view.width
        values[PROPNAME_HEIGHT] = view.height
    }

    override fun createAnimator(sceneRoot: ViewGroup, startValues: TransitionValues?, endValues: TransitionValues?): Animator? {
        if(startValues == null || endValues == null)
            return null

        val leftDelta = ((startValues.values[PROPNAME_POSX] as Int) - (endValues.values[PROPNAME_POSX] as Int)).toFloat()
        val topDelta = ((startValues.values[PROPNAME_POSY] as Int) - (endValues.values[PROPNAME_POSY] as Int)).toFloat()

        val scaleWidth = (startValues.values[PROPNAME_WIDTH] as Int).toFloat() / (endValues.values[PROPNAME_WIDTH] as Int).toFloat()
        val scaleHeight = (startValues.values[PROPNAME_HEIGHT] as Int).toFloat() / (endValues.values[PROPNAME_HEIGHT] as Int).toFloat()

        val view = endValues.view
        val anim = ObjectAnimator.ofPropertyValuesHolder(view,
                PropertyValuesHolder.ofFloat("scaleX", scaleWidth, 1f),
                PropertyValuesHolder.ofFloat("scaleY", scaleHeight, 1f),
                PropertyValuesHolder.ofFloat("translationX", leftDelta, 0f),
                PropertyValuesHolder.ofFloat("translationY", topDelta, 0f)
        )
        anim.doOnStart {
            view.pivotX = 0f
            view.pivotY = 0f
        }
        return anim
    }

    companion object {
        private const val PROPNAME_POSX = "movewithscaleandtranslation:posX"
        private const val PROPNAME_POSY = "movewithscaleandtranslation:posY"
        private const val PROPNAME_WIDTH = "movewithscaleandtranslation:width"
        private const val PROPNAME_HEIGHT = "movewithscaleandtranslation:height"
        val properties = arrayOf(PROPNAME_POSX, PROPNAME_POSY, PROPNAME_WIDTH, PROPNAME_HEIGHT)
    }
}