Reputation: 2408
I'm trying to set up a "shared element" transition animation among two fragments. However, the destination I want is not a single view, but a FrameLayout
with two overlapped elements that share size (an arrow and a rotating map) and must move and shrink at the same time.
My target layout looks like this:
<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>
I want to treat all this as a single thing.
Before transitions I was doing this animation on container_arrow
using scale and translation properties, and it worked fine.
However, when I use a transition the size animation only affects the outer FrameLayout
, but not its children. The inner arrow moves, but doesn't start small and grows, it start big and stays big. If I target the arrow instead, it works.
Looking at ChangeBounds
transition code it seems it uses setFrame()
to directly adjust the bounds of the target element. That doesn't propagate to its children.
I would need the translation+shrink animation to affect two elements, but transition names must be unique. Is there any way to achieve what I want?
EDIT:
I'm already trying to set the FrameLayout as a group by calling:
ViewCompat.setTransitionName(arrowContainer, "animatedArrow")
ViewGroupCompat.setTransitionGroup(arrowContainer, true) // <-- this
Same thing.. =/
Upvotes: 0
Views: 1376
Reputation: 2408
I ended up creating my own Transition
subclass which is similar to ChangeBounds
but uses translation and scale view properties to move the target instead of adjusting bounds. A delta for translation is calculated and it's animated to 0, and an initial scale is also calculated and animated to 1.
Here's the code:
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)
}
}
Upvotes: 1
Reputation: 200120
This is precisely what the ViewGroupCompat.setTransitionGroup()
API (for API 14+ devices when using AndroidX Transition) or android:transitionGroup="true"
XML attribute (for API 21+ devices) is for - by setting that flag to true, that entire ViewGroup is used as a single item when it comes to shared element transitions.
Note that you must also set a transition name on the same element you set as a transition group (using ViewCompat.setTransitionName()
/ android:transitionName
depending on whether you want to support back to API 14 or only API 21+).
Upvotes: 1