Skip to content

Commit

Permalink
[BottomAppBar] Add a callback for whenever bottom view hides/unhides …
Browse files Browse the repository at this point in the history
…through scrolling

PiperOrigin-RevId: 450449206
  • Loading branch information
imhappi authored and afohrman committed May 24, 2022
1 parent 382ce9c commit 21f9b0f
Show file tree
Hide file tree
Showing 2 changed files with 104 additions and 5 deletions.
Expand Up @@ -16,6 +16,8 @@

package com.google.android.material.behavior;

import static androidx.annotation.RestrictTo.Scope.LIBRARY_GROUP;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.TimeInterpolator;
Expand All @@ -25,27 +27,64 @@
import android.view.ViewGroup;
import android.view.ViewPropertyAnimator;
import androidx.annotation.Dimension;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.RestrictTo;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.coordinatorlayout.widget.CoordinatorLayout.Behavior;
import androidx.core.view.ViewCompat;
import com.google.android.material.animation.AnimationUtils;
import java.util.LinkedHashSet;

/**
* The {@link Behavior} for a View within a {@link CoordinatorLayout} to hide the view off the
* bottom of the screen when scrolling down, and show it when scrolling up.
*/
public class HideBottomViewOnScrollBehavior<V extends View> extends CoordinatorLayout.Behavior<V> {

/**
* Interface definition for a listener to be notified when the bottom view scroll state
* changes.
*/
public interface OnScrollStateChangedListener {

/**
* Called when the bottom view changes its scrolled state.
*
* @param bottomView The bottom view.
* @param newState The new state. This will be one of {@link #STATE_SCROLLED_UP} or {@link
* #STATE_SCROLLED_DOWN}.
*/
void onStateChanged(@NonNull View bottomView, @ScrollState int newState);
}

@NonNull
private final LinkedHashSet<OnScrollStateChangedListener> onScrollStateChangedListeners =
new LinkedHashSet<>();

protected static final int ENTER_ANIMATION_DURATION = 225;
protected static final int EXIT_ANIMATION_DURATION = 175;

private static final int STATE_SCROLLED_DOWN = 1;
private static final int STATE_SCROLLED_UP = 2;
/** State of the bottom view when it's scrolled down. */
public static final int STATE_SCROLLED_DOWN = 1;
/**
* State of the bottom view when it's scrolled up.
*/
public static final int STATE_SCROLLED_UP = 2;

private int height = 0;
private int currentState = STATE_SCROLLED_UP;

/**
* Positions the scroll state can be set to.
*
* @hide
*/
@RestrictTo(LIBRARY_GROUP)
@IntDef({STATE_SCROLLED_DOWN, STATE_SCROLLED_UP})
public @interface ScrollState {}

@ScrollState private int currentState = STATE_SCROLLED_UP;
private int additionalHiddenOffsetY = 0;
@Nullable private ViewPropertyAnimator currentAnimator;

Expand Down Expand Up @@ -135,7 +174,7 @@ public void slideUp(@NonNull V child, boolean animate) {
currentAnimator.cancel();
child.clearAnimation();
}
currentState = STATE_SCROLLED_UP;
updateCurrentState(child, STATE_SCROLLED_UP);
int targetTranslationY = 0;
if (animate) {
animateChildTo(
Expand Down Expand Up @@ -176,7 +215,7 @@ public void slideDown(@NonNull V child, boolean animate) {
currentAnimator.cancel();
child.clearAnimation();
}
currentState = STATE_SCROLLED_DOWN;
updateCurrentState(child, STATE_SCROLLED_DOWN);
int targetTranslationY = height + additionalHiddenOffsetY;
if (animate) {
animateChildTo(
Expand All @@ -189,6 +228,13 @@ public void slideDown(@NonNull V child, boolean animate) {
}
}

private void updateCurrentState(@NonNull V child, @ScrollState int state) {
currentState = state;
for (OnScrollStateChangedListener listener : onScrollStateChangedListeners) {
listener.onStateChanged(child, currentState);
}
}

private void animateChildTo(
@NonNull V child, int targetY, long duration, TimeInterpolator interpolator) {
currentAnimator =
Expand All @@ -205,4 +251,27 @@ public void onAnimationEnd(Animator animation) {
}
});
}

/**
* Adds a listener to be notified of bottom view scroll state changes.
*
* @param listener The listener to notify when bottom view scroll state changes.
*/
public void addOnScrollStateChangedListener(@NonNull OnScrollStateChangedListener listener) {
onScrollStateChangedListeners.add(listener);
}

/**
* Removes a previously added listener.
*
* @param listener The listener to remove.
*/
public void removeOnScrollStateChangedListener(@NonNull OnScrollStateChangedListener listener) {
onScrollStateChangedListeners.remove(listener);
}

/** Remove all previously added {@link OnScrollStateChangedListener}s. */
public void clearOnScrollStateChangedListeners() {
onScrollStateChangedListeners.clear();
}
}
Expand Up @@ -58,6 +58,7 @@
import com.google.android.material.animation.AnimationUtils;
import com.google.android.material.animation.TransformationCallback;
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior;
import com.google.android.material.behavior.HideBottomViewOnScrollBehavior.OnScrollStateChangedListener;
import com.google.android.material.floatingactionbutton.ExtendedFloatingActionButton;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.google.android.material.floatingactionbutton.FloatingActionButton.OnVisibilityChangedListener;
Expand Down Expand Up @@ -541,6 +542,35 @@ public boolean isScrolledUp() {
return getBehavior().isScrolledUp();
}

/**
* Add a listener that will be called when the bottom app bar scroll state changes.
* See {@link OnScrollStateChangedListener}.
*
* <p>Components that add a listener should take care to remove it when finished via {@link
* #removeOnScrollStateChangedListener(OnScrollStateChangedListener)}.
*
* @param listener listener to add
*/
public void addOnScrollStateChangedListener(@NonNull OnScrollStateChangedListener listener) {
getBehavior().addOnScrollStateChangedListener(listener);
}

/**
* Remove a listener that was previously added via {@link
* #addOnScrollStateChangedListener(OnScrollStateChangedListener)}.
*
* @param listener listener to remove
*/
public void removeOnScrollStateChangedListener(
@NonNull OnScrollStateChangedListener listener) {
getBehavior().removeOnScrollStateChangedListener(listener);
}

/** Remove all previously added {@link OnScrollStateChangedListener}s. */
public void clearOnScrollStateChangedListeners() {
getBehavior().clearOnScrollStateChangedListeners();
}

@Override
public void setElevation(float elevation) {
materialShapeDrawable.setElevation(elevation);
Expand Down

0 comments on commit 21f9b0f

Please sign in to comment.