I am trying to use a ConstraintLayout
in conjunction with views that slide up and down with animation.
These views are organized vertically, with a RecyclerView
at the top and two other views stacked under it:
<constraint layout container>
[ ]
[ recycler view ]
[ ]
[48dp height 1st view]
[48dp height 2nd view]
</constraint layout container>
The animation is very simple: When a button is tapped the 1st view moves from the bottom of the container to the position you can see above, when tapped again it moves down and stays overlapped on the 2nd view. When this happens the RecyclerView
at the top changes height since it is constrained to that 1st view, otherwise it would leave an empty space.
So far so good and the animation is working well, but the problem arises when the user taps the button too fast, leading to the following error:
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
After investigating what was going on I discovered that this problem is a consequence of the TransitionManager
that I am using to animate the changes. What I discovered was interesting, when the button was tapped to move the 1st view up (or down, just imagine the opposite steps) the manager was doing the following:
RecyclerView
(depends on whether or not you have a reversed layout or stack from end enabled)RecyclerView
RecyclerView
Even though I specified in all the places I could imagine that the RecyclerView
should not animate item changes, the TransitionManager
was overriding it and that meant there is a window when the same view that is being about to be removed from the RecyclerView
can be added again to the same parent, and that window is when the view is fading out by the manager.
Since the fade animation is not that fast, a user can very easily tap the button twice during that time, causing the manager to re-add the fading view, which is already in the RecyclerView
and thus crashing the app while throwing the above error.
Since this is an issue caused inside the RecyclerView
I would be perfectly fine with no animations inside it and for that reason I would like to know how can I specify in the TransitionManager.beginDelayedTransition
to only animate the direct child views of the ConstraintLayout
, this way it will not animate the views inside the RecyclerView
.
The related documentation does not shine anything over this, so I present here this question; How can I restrict the transition to direct child views?
In case it might be helpful I include here the snippet of the transition manager
final ConstraintSet constraintSet;
final ConstraintLayout constraintLayout;
constraintSet = new ConstraintSet();
constraintLayout = (ConstraintLayout) viewParent;
TransitionManager.beginDelayedTransition(constraintLayout);
constraintSet.clone(constraintLayout);
constraintSet.connect(startId, startSide, endId, endSide, margin);
constraintSet.applyTo(constraintLayout);
pskink posted the answer in the comments, but after asking twice for him to post as an answer for me to accept and never doing it, I'll post it instead and mark this as answered.
The TransitionManager
had a method excludeChildren
that works perfectly for what I was looking for. You can use by class types, children of a specific view and so on.
EDIT: Adding example requested
AutoTransition autoTransition = new AutoTransition();
autoTransition.excludeChildren(R.id.recyclerView, true);
TransitionManager.beginDelayedTransition(constraintLayout, autoTransition);
With constraintLayout
as the parent of the target view and R.id.recyclerView
being a child view of that parent that I don't want to be animated, in this specific example that is. You can also exclude all children that are of a specific class (EditText
for example) and so on.
I fixed it this way:
TransitionManager.beginDelayedTransition(
constraintLayout,
TransitionSet().apply {
ordering = TransitionSet.ORDERING_SEQUENTIAL
addTransition(ChangeBounds())
addTransition(Fade(Fade.IN))
}
)
it's pretty much the same code inside of AutoTransition
which is the default one if we don't pass a custom transition, but I removed the addTransition(Fade(Fade.OUT))
that seems to be the cause of the crash. And the animations look very similar to the original at least for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With