Skip to content

Commit

Permalink
[BottomAppBar] Update menu replacement animation to coordinate with f…
Browse files Browse the repository at this point in the history
…ab alignment animation.

When calling both setFabAlignmentMode and replaceMenu, the fab alignment animation and menu animation were not properly coordinating. replaceMenu causes a call to onLayout, which immediately updates the menu's translationX, causeing the menu to jump to its final position. This adds a condition to skip the menu position update if a menu animatior is running.

Additionally, the menu's contents were immediately being swapped making the animation look jumpy. This CL also as a method to update the fab alignment and menu resource at the same time so a coordinated animation of: fab scale out/slide start - > menu fade out -> menu replaced -> menu fade in -> fab scale/slide finish, can be run.

PiperOrigin-RevId: 316860005
  • Loading branch information
hunterstich authored and melaniegoetz committed Jun 18, 2020
1 parent ba9c2e6 commit d4a5702
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -112,8 +112,15 @@ public View onCreateDemoView(
ToggleButton attachToggle = view.findViewById(R.id.attach_toggle);
attachToggle.setChecked(fab.getVisibility() == View.VISIBLE);
centerButton.setOnClickListener(
v -> bar.setFabAlignmentMode(BottomAppBar.FAB_ALIGNMENT_MODE_CENTER));
endButton.setOnClickListener(v -> bar.setFabAlignmentMode(BottomAppBar.FAB_ALIGNMENT_MODE_END));
v -> {
bar.setFabAlignmentModeAndReplaceMenu(
BottomAppBar.FAB_ALIGNMENT_MODE_CENTER, R.menu.demo_primary);
});
endButton.setOnClickListener(
v -> {
bar.setFabAlignmentModeAndReplaceMenu(
BottomAppBar.FAB_ALIGNMENT_MODE_END, R.menu.demo_primary_alternate);
});
attachToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> {
if (isChecked) {
Expand All @@ -126,8 +133,15 @@ public View onCreateDemoView(
ToggleButton barScrollToggle = view.findViewById(R.id.bar_scroll_toggle);
barScrollToggle.setChecked(bar.getHideOnScroll());
barScrollToggle.setOnCheckedChangeListener(
(buttonView, isChecked) -> bar.setHideOnScroll(isChecked)
);
(buttonView, isChecked) -> bar.setHideOnScroll(isChecked));

ToggleButton fabAnimToggle = view.findViewById(R.id.fab_animation_mode_toggle);
fabAnimToggle.setOnCheckedChangeListener(
(buttonView, isChecked) ->
bar.setFabAnimationMode(
isChecked
? BottomAppBar.FAB_ANIMATION_MODE_SLIDE
: BottomAppBar.FAB_ANIMATION_MODE_SCALE));

setUpBottomAppBarShapeAppearance();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,14 +40,28 @@
android:paddingTop="16dp"
android:orientation="vertical">

<ToggleButton
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal">

<ToggleButton
android:id="@+id/bar_scroll_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:textOff="@string/cat_bottomappbar_scroll_hide"
android:textOn="@string/cat_bottomappbar_scroll_hide"/>

<ToggleButton
android:id="@+id/fab_animation_mode_toggle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textOff="@string/cat_bottomappbar_fab_anim_scale"
android:textOn="@string/cat_bottomappbar_fab_anim_slide"/>

</LinearLayout>

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,4 @@
android:icon="@drawable/ic_accelerator_24px"
android:title="@string/cat_bottomappbar_accelerator"
app:showAsAction="ifRoom"/>
<item
android:icon="@drawable/ic_dashboard_24px"
android:title="@string/cat_bottomappbar_dashboard"
app:showAsAction="ifRoom"/>
</menu>
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright 2020 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:icon="@drawable/ic_accelerator_24px"
android:title="@string/cat_bottomappbar_accelerator"
app:showAsAction="ifRoom"/>
<item
android:icon="@drawable/ic_3d_rotation_24px"
android:title="@string/cat_bottomappbar_3d"
app:showAsAction="ifRoom"/>
<item
android:icon="@drawable/ic_dashboard_24px"
android:title="@string/cat_bottomappbar_dashboard"
app:showAsAction="ifRoom"/>
</menu>
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus
<string name="cat_bottomappbar_fab_hide">Hide Fab</string>
<string name="cat_bottomappbar_fab_show">Show Fab</string>
<string name="cat_bottomappbar_scroll_hide">Hide on scroll</string>
<string name="cat_bottomappbar_fab_anim_scale">Scale fab</string>
<string name="cat_bottomappbar_fab_anim_slide">Slide fab</string>
<string name="cat_bottomappbar_fab_circle">Circle</string>
<string name="cat_bottomappbar_fab_diamond">Diamond</string>
<string name="cat_bottomappbar_end">End</string>
Expand Down
64 changes: 54 additions & 10 deletions lib/java/com/google/android/material/bottomappbar/BottomAppBar.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,10 +100,8 @@
* @attr ref com.google.android.material.R.styleable#BottomAppBar_fabAlignmentMode
* @attr ref com.google.android.material.R.styleable#BottomAppBar_fabAnimationMode
* @attr ref com.google.android.material.R.styleable#BottomAppBar_fabCradleMargin
* @attr ref
* com.google.android.material.R.styleable#BottomAppBar_fabCradleRoundedCornerRadius
* @attr ref
* com.google.android.material.R.styleable#BottomAppBar_fabCradleVerticalOffset
* @attr ref com.google.android.material.R.styleable#BottomAppBar_fabCradleRoundedCornerRadius
* @attr ref com.google.android.material.R.styleable#BottomAppBar_fabCradleVerticalOffset
* @attr ref com.google.android.material.R.styleable#BottomAppBar_hideOnScroll
* @attr ref com.google.android.material.R.styleable#BottomAppBar_paddingBottomSystemWindowInsets
*/
Expand Down Expand Up @@ -151,11 +149,23 @@ public class BottomAppBar extends Toolbar implements AttachedBehavior {

/** Keeps track of the number of currently running animations. */
private int animatingModeChangeCounter = 0;

private ArrayList<AnimationListener> animationListeners;

/**
* Track whether or not a new menu will be inflated along with a FAB alignment change. The
* inflation of the resource is deferred until an appropriate time during the FAB alignment/menu
* animation before being set and clearing this pending resource.
*/
private static final int NO_MENU_RES_ID = 0;

@MenuRes private int pendingMenuResId = NO_MENU_RES_ID;
private boolean menuAnimatingWithFabAlignmentMode = false;

/** Callback to be invoked when the BottomAppBar is animating. */
interface AnimationListener {
void onAnimationStart(BottomAppBar bar);

void onAnimationEnd(BottomAppBar bar);
}

Expand All @@ -182,7 +192,13 @@ interface AnimationListener {
new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
maybeAnimateMenuView(fabAlignmentMode, fabAttached);
// If the FAB has begun to animate as a result of the FAB alignment mode changing, the FAB
// alignment animation will handle coordination of menu animation and this should be
// skipped. If the FAB has begun to animate as a result of hiding/showing the FAB, the
// menu animation should be cancelled and restarted.
if (!menuAnimatingWithFabAlignmentMode) {
maybeAnimateMenuView(fabAlignmentMode, fabAttached);
}
}
};

Expand Down Expand Up @@ -323,15 +339,34 @@ public int getFabAlignmentMode() {
}

/**
* Sets the current fabAlignmentMode. An animated transition between current and desired modes
* will be played.
* Sets the current {@code fabAlignmentMode}. An animated transition between current and desired
* modes will be played.
*
* @param fabAlignmentMode the desired fabAlignmentMode, either {@link #FAB_ALIGNMENT_MODE_CENTER}
* or {@link #FAB_ALIGNMENT_MODE_END}.
*/
public void setFabAlignmentMode(@FabAlignmentMode int fabAlignmentMode) {
maybeAnimateModeChange(fabAlignmentMode);
setFabAlignmentModeAndReplaceMenu(fabAlignmentMode, NO_MENU_RES_ID);
}

/**
* Sets the current {@code fabAlignmentMode} and replaces the {@code BottomAppBar}'s menu
* resource. An animated transition between the current and desired mode will be played in
* coordination with a menu resource swap animation.
*
* @param fabAlignmentMode the desired fabAlignmentMode, either {@link #FAB_ALIGNMENT_MODE_CENTER}
* or {@link #FAB_ALIGNMENT_MODE_END}.
* @param newMenu the menu resource of a new menu to be inflated and swapped during the animation.
* Passing 0 for newMenu will not clear the menu but will skip all menu manipulation. If you'd
* like to animate the FAB's alignment and clear the menu at the same time, use {@code
* getMenu().clear()} and {@link #setFabAlignmentMode(int)}.
*/
public void setFabAlignmentModeAndReplaceMenu(
@FabAlignmentMode int fabAlignmentMode, @MenuRes int newMenu) {
this.pendingMenuResId = newMenu;
this.menuAnimatingWithFabAlignmentMode = true;
maybeAnimateMenuView(fabAlignmentMode, fabAttached);
maybeAnimateModeChange(fabAlignmentMode);
this.fabAlignmentMode = fabAlignmentMode;
}

Expand Down Expand Up @@ -541,6 +576,7 @@ public void onAnimationStart(Animator animation) {
@Override
public void onAnimationEnd(Animator animation) {
dispatchAnimationEnd();
modeAnimator = null;
}
});
modeAnimator.start();
Expand Down Expand Up @@ -617,6 +653,7 @@ private void createFabTranslationXAnimation(

private void maybeAnimateMenuView(@FabAlignmentMode int targetMode, boolean newFabAttached) {
if (!ViewCompat.isLaidOut(this)) {
menuAnimatingWithFabAlignmentMode = false;
return;
}

Expand Down Expand Up @@ -647,6 +684,7 @@ public void onAnimationStart(Animator animation) {
@Override
public void onAnimationEnd(Animator animation) {
dispatchAnimationEnd();
menuAnimatingWithFabAlignmentMode = false;
menuAnimator = null;
}
});
Expand Down Expand Up @@ -689,6 +727,10 @@ public void onAnimationCancel(Animator animation) {
public void onAnimationEnd(Animator animation) {
if (!cancelled) {
translateActionMenuView(actionMenuView, targetMode, targetAttached);
if (pendingMenuResId != NO_MENU_RES_ID) {
replaceMenu(pendingMenuResId);
pendingMenuResId = NO_MENU_RES_ID;
}
}
}
});
Expand Down Expand Up @@ -839,7 +881,9 @@ private void setCutoutState() {

private void setActionMenuViewPosition() {
ActionMenuView actionMenuView = getActionMenuView();
if (actionMenuView != null) {
// If the menu is null there is no need to translate it. If the menu is currently being
// animated, the menuAnimator will take care of re-positioning the menu if necessary.
if (actionMenuView != null && menuAnimator == null) {
actionMenuView.setAlpha(1.0f);
if (!isFabVisibleOrWillBeShown()) {
translateActionMenuView(actionMenuView, FAB_ALIGNMENT_MODE_CENTER, false);
Expand Down Expand Up @@ -976,7 +1020,7 @@ public void onLayoutChange(
fabLayoutParams.bottomMargin = child.getBottomInset() + minBottomMargin;
fabLayoutParams.leftMargin = child.getLeftInset();
fabLayoutParams.rightMargin = child.getRightInset();
boolean isRtl = ViewUtils.isLayoutRtl(fab);
boolean isRtl = ViewUtils.isLayoutRtl(fab);
if (isRtl) {
fabLayoutParams.leftMargin += child.fabOffsetEndMode;
} else {
Expand Down

0 comments on commit d4a5702

Please sign in to comment.