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:
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.
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.
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;
}
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:
And here is the root of the problem - navigation button is removed from the toolbar but it doesn't event know about this.
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.
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