Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Toolbar animateLayoutChanges strange behavior

Tags:

android

I set animateLayoutChanges=true in my code, to animate the options item I have in my fragments.

When I open the PreferenceFragment I enable the up navigation icon, then I disable it when I close it, but it behaves in a strange way, leaving a sort of padding to the left of the title, where the NavigationIcon was.

Here's a gif showing what's happening:

img

Do you guy's have any idea on why is this happening? I searched far and wide on the internet, but found nothing.

Is there any workaround to animate these items in the same way? Thanks.

like image 695
azazello Avatar asked Oct 17 '22 03:10

azazello


1 Answers

Remark: I know this thread in more than a year long but a lot of people still stumble on this problem.

It took me good few hours to finally dig into this problem and solution is actually relatively easy.

The Scenario

When you call ActionBar.setDisplayHomeAsUpEnabled() or Toolbar.setNavigationIcon(), Toolbar will dynamically add or remove navigation view depending on specified value

public void setNavigationIcon(@Nullable Drawable icon) {
    if (icon != null) {
        ensureNavButtonView();
        if (!isChildOrHidden(mNavButtonView)) {
            addSystemView(mNavButtonView, true);
        }
    } else if (mNavButtonView != null && isChildOrHidden(mNavButtonView)) {
        removeView(mNavButtonView);
        mHiddenViews.remove(mNavButtonView);
    }
    if (mNavButtonView != null) {
        mNavButtonView.setImageDrawable(icon);
    }
}

And in onLayout it will check whether navigation button is present and lay it out.

protected void onLayout(boolean changed, int l, int t, int r, int b) {

    // more code before

    if (shouldLayout(mNavButtonView)) {
        if (isRtl) {
            right = layoutChildRight(mNavButtonView, right, collapsingMargins,
                    alignmentHeight);
        } else {
            left = layoutChildLeft(mNavButtonView, left, collapsingMargins,
                    alignmentHeight);
        }
    }

    // more code after. also mTitleTextView is laid out here
}

Toolbar also increments left/right position after laying out each view. These values used as an offset for each subsequent view.

To check whether view should be laid out it uses helper function:

private boolean shouldLayout(View view) {
    return view != null && view.getParent() == this && view.getVisibility() != GONE;
}

The Problem

When setting layoutTransition through the code or animateLayoutChanges in the xml we change behaviour a little bit.

Lets start with what happening when we remove child from parent (ViewGroup code):

private void removeFromArray(int index) {
    final View[] children = mChildren;
    if (!(mTransitioningViews != null && mTransitioningViews.contains(children[index]))) {
        children[index].mParent = null;
    }

    // more code after
}

Did you mention that child's parent is not nulled when we have transition set? This is important!

Also please recall what shouldLayout from above does - it checks for only three cases:

  1. view is not null
  2. view's parent is toolbar
  3. view is not GONE

And here is the root of the problem - navigation button is removed from the toolbar but it doesn't event know about this.

The Solution

In order to fix this problem we need to force toolbar into thinking that view shouldn't be laid out. Ideally it would be nice to override shouldLayout method but we don't have such luxury... So the only way we can accomplish this is by changing visibility on the navigation button.

class FixedToolbar(context: Context, attrs: AttributeSet?) : MaterialToolbar(context, attrs) {
    companion object {
        private val navButtonViewField = Toolbar::class.java.getDeclaredField("mNavButtonView")
                .also { it.isAccessible = true }
    }

    override fun setNavigationIcon(icon: Drawable?) {
        super.setNavigationIcon(icon)

        (navButtonViewField.get(this) as? View)?.isGone = (icon == null)
    }
}

Now toolbar will know that navigation button must not be laid out and all animations will go as intended.

like image 159
MatrixDev Avatar answered Oct 20 '22 23:10

MatrixDev