Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to specify TransitionManager.beginDelayedTransition to affect only direct child views

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:

  • Fade out the first/last view from the RecyclerView (depends on whether or not you have a reversed layout or stack from end enabled)
  • Remove that view from the RecyclerView
  • Decrease the height of the RecyclerView
  • Move the 1st view up

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);
like image 987
Shadow Avatar asked May 21 '18 22:05

Shadow


2 Answers

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.

like image 124
Shadow Avatar answered Oct 14 '22 00:10

Shadow


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.

like image 26
epool Avatar answered Oct 14 '22 02:10

epool