我正在尝试在两个片段之间设置一个“共享元素” 过渡动画。但是,我想要的目的地不是单一视图,而是一个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
同一件事.. = /
答案 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)
}
}