[java] yusuf

Viewer

  1. // Important add your package here or don't delete package line from your created class.
  2. // Make sure the java file name & this class name, matches.
  3.  
  4. import static androidx.annotation.RestrictTo.Scope.GROUP_ID;
  5.  
  6. import android.content.Context;
  7. import android.content.res.TypedArray;
  8. import android.os.Parcel;
  9. import android.os.Parcelable;
  10. import android.util.AttributeSet;
  11. import android.util.Log;
  12. import android.util.TypedValue;
  13. import android.view.MotionEvent;
  14. import android.view.VelocityTracker;
  15. import android.view.View;
  16. import android.view.ViewConfiguration;
  17. import android.view.ViewGroup;
  18. import android.view.ViewParent;
  19.  
  20. import androidx.annotation.IntDef;
  21. import androidx.annotation.NonNull;
  22. import androidx.annotation.RestrictTo;
  23. import androidx.annotation.VisibleForTesting;
  24. import androidx.coordinatorlayout.widget.CoordinatorLayout;
  25. import androidx.core.os.ParcelableCompat;
  26. import androidx.core.os.ParcelableCompatCreatorCallbacks;
  27. import androidx.core.view.NestedScrollingChild;
  28. import androidx.core.view.ViewCompat;
  29. import androidx.customview.view.AbsSavedState;
  30. import androidx.customview.widget.ViewDragHelper;
  31. import androidx.viewpager.widget.ViewPager;
  32. import androidx.viewpager2.widget.ViewPager2;
  33.  
  34. import com.realgear.extensions.R;
  35.  
  36. import java.lang.annotation.Retention;
  37. import java.lang.annotation.RetentionPolicy;
  38. import java.lang.ref.WeakReference;
  39. import java.util.ArrayList;
  40. import java.util.List;
  41.  
  42. public class CustomBottomSheetBehavior<extends View> extends CoordinatorLayout.Behavior<V> {
  43.     public final String TAG = this.getClass().getSimpleName();
  44.  
  45.     /**
  46.      * Callback for monitoring events about bottom sheets.
  47.      */
  48.     public abstract static class BottomSheetCallback {
  49.  
  50.         /**
  51.          * Called when the bottom sheet changes its state.
  52.          *
  53.          * @param bottomSheet The bottom sheet view.
  54.          * @param oldState    The old state. This will be one of {@link #STATE_DRAGGING},
  55.          *                    {@link #STATE_SETTLING}, {@link #STATE_EXPANDED},
  56.          *                    {@link #STATE_COLLAPSED}, {@link #STATE_ANCHORED} or
  57.          *                    {@link #STATE_HIDDEN}.
  58.          * @param newState    The new state. This will be one of {@link #STATE_DRAGGING},
  59.          *                    {@link #STATE_SETTLING}, {@link #STATE_EXPANDED},
  60.          *                    {@link #STATE_COLLAPSED}, {@link #STATE_ANCHORED} or
  61.          *                    {@link #STATE_HIDDEN}.
  62.          */
  63.         public abstract void onStateChanged(@NonNull View bottomSheet, @State int oldState, @State int newState);
  64.  
  65.         /**
  66.          * Called when the bottom sheet is being dragged.
  67.          *
  68.          * @param bottomSheet The bottom sheet view.
  69.          * @param slideOffset The new offset of this bottom sheet within [-1,1] range. Offset
  70.          *                    increases as this bottom sheet is moving upward. From 0 to 1 the sheet
  71.          *                    is between collapsed and expanded states and from -1 to 0 it is
  72.          *                    between hidden and collapsed states.
  73.          */
  74.         public abstract void onSlide(@NonNull View bottomSheet, float slideOffset);
  75.     }
  76.  
  77.     /**
  78.      * Stub/no-op implementations of all methods of {@link BottomSheetCallback}.
  79.      * Override this if you only care about a few of the available callback methods.
  80.      */
  81.     public abstract static class SimpleBottomSheetCallback extends BottomSheetCallback {
  82.         @Override
  83.         public void onStateChanged(@NonNull View bottomSheet, @State int oldState, @State int newState) {
  84.         }
  85.  
  86.         @Override
  87.         public void onSlide(@NonNull View bottomSheet, float slideOffset) {
  88.         }
  89.     }
  90.  
  91.     /**
  92.      * The bottom sheet is dragging.
  93.      */
  94.     public static final int STATE_DRAGGING = 1;
  95.  
  96.     /**
  97.      * The bottom sheet is settling.
  98.      */
  99.     public static final int STATE_SETTLING = 2;
  100.  
  101.     /**
  102.      * The bottom sheet is expanded.
  103.      */
  104.     public static final int STATE_EXPANDED = 3;
  105.  
  106.     /**
  107.      * The bottom sheet is collapsed.
  108.      */
  109.     public static final int STATE_COLLAPSED = 4;
  110.  
  111.     /**
  112.      * The bottom sheet is hidden.
  113.      */
  114.     public static final int STATE_HIDDEN = 5;
  115.  
  116.     /**
  117.      * The bottom sheet is anchored.
  118.      */
  119.     public static final int STATE_ANCHORED = 6;
  120.  
  121.     /**
  122.      * @hide
  123.      */
  124.     @RestrictTo(GROUP_ID)
  125.     @IntDef({STATE_EXPANDED, STATE_COLLAPSED, STATE_DRAGGING, STATE_SETTLING, STATE_HIDDEN, STATE_ANCHORED})
  126.     @Retention(RetentionPolicy.SOURCE)
  127.     public @interface State {
  128.     }
  129.  
  130.     /**
  131.      * Peek at the 16:9 ratio keyline of its parent.
  132.      * <p>
  133.      * <p>This can be used as a parameter for {@link #setPeekHeight(int)}.
  134.      * {@link #getPeekHeight()} will return this when the value is set.</p>
  135.      */
  136.     public static final int PEEK_HEIGHT_AUTO = -1;
  137.  
  138.     private static final float HIDE_THRESHOLD = 0.5f;
  139.  
  140.     private static final float HIDE_FRICTION = 0.1f;
  141.  
  142.     private static final float EXPAND_FRICTION = 0.2f;
  143.  
  144.     private static final float COLLAPSE_FRICTION = 0.2f;
  145.  
  146.     private float mMinimumVelocity;
  147.  
  148.     private float mMaximumVelocity;
  149.  
  150.     private int mPeekHeight;
  151.  
  152.     private boolean mPeekHeightAuto;
  153.  
  154.     private int mPeekHeightMin;
  155.  
  156.     private int mAnchorOffset;
  157.  
  158.     private boolean mAllowUserDragging = true;
  159.  
  160.     int mMinOffset;
  161.  
  162.     int mMaxOffset;
  163.  
  164.     boolean mHideable;
  165.  
  166.     private boolean mSkipCollapsed;
  167.  
  168.     private boolean mSkipAnchored;
  169.  
  170.     private boolean mDisableExpanded;
  171.  
  172.     @CustomBottomSheetBehavior.State
  173.     int mState = STATE_COLLAPSED;
  174.  
  175.     @CustomBottomSheetBehavior.State
  176.     int mPrevState = STATE_COLLAPSED;
  177.  
  178.     ViewDragHelper mViewDragHelper;
  179.  
  180.     private boolean mIgnoreEvents;
  181.  
  182.     private boolean mNestedScrolled;
  183.  
  184.     int mParentHeight;
  185.  
  186.     WeakReference<V> mViewRef;
  187.  
  188.     WeakReference<View> mNestedScrollingChildRef;
  189.  
  190.     private List<BottomSheetCallback> mCallbacks = new ArrayList<>(2);
  191.  
  192.     private VelocityTracker mVelocityTracker;
  193.  
  194.     int mActivePointerId;
  195.  
  196.     private int mInitialY;
  197.  
  198.     boolean mTouchingScrollingChild;
  199.  
  200.  
  201.     private int mMediaPlayerBarHeight;
  202.  
  203.     /**
  204.      * Default constructor for instantiating AnchorBottomSheetBehaviors.
  205.      */
  206.     public CustomBottomSheetBehavior() {
  207.     }
  208.  
  209.     /**
  210.      * Default constructor for inflating AnchorBottomSheetBehaviors from layout.
  211.      *
  212.      * @param context The {@link Context}.
  213.      * @param attrs   The {@link AttributeSet}.
  214.      */
  215.     public CustomBottomSheetBehavior(Context context, AttributeSet attrs) {
  216.         super(context, attrs);
  217.         TypedArray a = context.obtainStyledAttributes(attrs,
  218.                 com.google.android.material.R.styleable.BottomSheetBehavior_Layout);
  219.         TypedValue value = a.peekValue(com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight);
  220.         if (value != null && value.data == PEEK_HEIGHT_AUTO) {
  221.             setPeekHeight(value.data);
  222.         } else {
  223.             setPeekHeight(a.getDimensionPixelSize(
  224.                     com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_peekHeight, PEEK_HEIGHT_AUTO));
  225.         }
  226.         setHideable(a.getBoolean(com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_hideablefalse));
  227.         setSkipCollapsed(a.getBoolean(com.google.android.material.R.styleable.BottomSheetBehavior_Layout_behavior_skipCollapsed,
  228.                 false));
  229.         setSkipAnchored(a.getBoolean(R.styleable.CustomBottomSheetBehavior_behavior_skipAnchoredfalse));
  230.         a.recycle();
  231.  
  232.         a = context.obtainStyledAttributes(attrs, R.styleable.CustomBottomSheetBehavior);
  233.         mAnchorOffset = (int) a.getDimension(R.styleable.CustomBottomSheetBehavior_behavior_anchorOffset0);
  234.         //noinspection WrongConstant
  235.         mState = a.getInt(R.styleable.CustomBottomSheetBehavior_behavior_defaultState, mState);
  236.         a.recycle();
  237.  
  238.         ViewConfiguration configuration = ViewConfiguration.get(context);
  239.         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
  240.         mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
  241.     }
  242.  
  243.     public void setMediaPlayerBarHeight(int mediaPlayerBarHeight) {
  244.         this.mMediaPlayerBarHeight = mediaPlayerBarHeight;
  245.     }
  246.  
  247.     void invalidateScrollingChild() {
  248.         final View scrollingChild = findScrollingChild(mViewRef.get());
  249.         mNestedScrollingChildRef = new WeakReference<>(scrollingChild);
  250.     }
  251.  
  252.     @Override
  253.     public Parcelable onSaveInstanceState(CoordinatorLayout parent, V child) {
  254.         return (Parcelable) new SavedState(super.onSaveInstanceState(parent, child), mState);
  255.     }
  256.  
  257.     @Override
  258.     public void onRestoreInstanceState(CoordinatorLayout parent, V child, Parcelable state) {
  259.         SavedState ss = (SavedState) state;
  260.         super.onRestoreInstanceState(parent, child, ss.getSuperState());
  261.         // Intermediate states are restored as collapsed state
  262.         if (ss.state == STATE_DRAGGING || ss.state == STATE_SETTLING) {
  263.             mState = STATE_COLLAPSED;
  264.         } else {
  265.             mState = ss.state;
  266.         }
  267.     }
  268.  
  269.     @Override
  270.     public boolean onLayoutChild(CoordinatorLayout parent, V child, int layoutDirection) {
  271.         if (ViewCompat.getFitsSystemWindows(parent) && !ViewCompat.getFitsSystemWindows(child)) {
  272.             ViewCompat.setFitsSystemWindows(child, true);
  273.         }
  274.         int savedTop = child.getTop();
  275.         // First let the parent lay it out
  276.         parent.onLayoutChild(child, layoutDirection);
  277.         // Offset the bottom sheet
  278.         mParentHeight = parent.getHeight();
  279.         int peekHeight;
  280.         if (mPeekHeightAuto) {
  281.             if (mPeekHeightMin == 0) {
  282.                 mPeekHeightMin = parent.getResources().getDimensionPixelSize(
  283.                         com.google.android.material.R.dimen.design_bottom_sheet_peek_height_min);
  284.             }
  285.             peekHeight = Math.max(mPeekHeightMin, mParentHeight - parent.getWidth() * 9 / 16);
  286.         } else {
  287.             peekHeight = mPeekHeight;
  288.         }
  289.         mMinOffset = Math.max(0, mParentHeight - child.getHeight());
  290.         if (mDisableExpanded) {
  291.             mMinOffset = mAnchorOffset;
  292.         }
  293.         mMaxOffset = Math.max(mParentHeight - peekHeight, mMinOffset);
  294.         if (mState == STATE_EXPANDED) {
  295.             ViewCompat.offsetTopAndBottom(child, mMinOffset);
  296.         } else if (mHideable && mState == STATE_HIDDEN) {
  297.             ViewCompat.offsetTopAndBottom(child, mParentHeight);
  298.         } else if (mState == STATE_COLLAPSED) {
  299.             ViewCompat.offsetTopAndBottom(child, mMaxOffset);
  300.         } else if (mState == STATE_DRAGGING || mState == STATE_SETTLING) {
  301.             ViewCompat.offsetTopAndBottom(child, savedTop - child.getTop());
  302.         } else if (mState == STATE_ANCHORED) {
  303.             if (mAnchorOffset > mMinOffset) {
  304.                 ViewCompat.offsetTopAndBottom(child, mAnchorOffset);
  305.             } else {
  306.                 mState = STATE_EXPANDED;
  307.                 ViewCompat.offsetTopAndBottom(child, mMinOffset);
  308.             }
  309.         }
  310.         if (mViewDragHelper == null) {
  311.             mViewDragHelper = ViewDragHelper.create(parent, mDragCallback);
  312.         }
  313.         mViewRef = new WeakReference<>(child);
  314.         mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child));
  315.         return true;
  316.     }
  317.  
  318.     @Override
  319.     public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
  320.         if (!child.isShown() || !mAllowUserDragging) {
  321.             mIgnoreEvents = true;
  322.             return false;
  323.         }
  324.         int action = event.getActionMasked();
  325.         // Record the velocity
  326.         if (action == MotionEvent.ACTION_DOWN) {
  327.             reset();
  328.         }
  329.         if (mVelocityTracker == null) {
  330.             mVelocityTracker = VelocityTracker.obtain();
  331.         }
  332.         mVelocityTracker.addMovement(event);
  333.         switch (action) {
  334.             case MotionEvent.ACTION_UP:
  335.             case MotionEvent.ACTION_CANCEL:
  336.                 mTouchingScrollingChild = false;
  337.                 mActivePointerId = MotionEvent.INVALID_POINTER_ID;
  338.                 // Reset the ignore flag
  339.                 if (mIgnoreEvents) {
  340.                     mIgnoreEvents = false;
  341.                     return false;
  342.                 }
  343.                 break;
  344.             case MotionEvent.ACTION_DOWN:
  345.                 int initialX = (int) event.getX();
  346.                 mInitialY = (int) event.getY();
  347.                 View scroll = mNestedScrollingChildRef.get();
  348.                 if (scroll != null && parent.isPointInChildBounds(scroll, initialX, mInitialY)) {
  349.                     mActivePointerId = event.getPointerId(event.getActionIndex());
  350.                     mTouchingScrollingChild = true;
  351.                 }
  352.                 mIgnoreEvents = mActivePointerId == MotionEvent.INVALID_POINTER_ID &&
  353.                         !parent.isPointInChildBounds(child, initialX, mInitialY);
  354.                 break;
  355.         }
  356.         if (!mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event)) {
  357.             return true;
  358.         }
  359.         // We have to handle cases that the ViewDragHelper does not capture the bottom sheet because
  360.         // it is not the top most view of its parent. This is not necessary when the touch event is
  361.         // happening over the scrolling content as nested scrolling logic handles that case.
  362.         View scroll = mNestedScrollingChildRef.get();
  363.         return action == MotionEvent.ACTION_MOVE && scroll != null &&
  364.                 !mIgnoreEvents && mState != STATE_DRAGGING &&
  365.                 !parent.isPointInChildBounds(scroll, (int) event.getX()(int) event.getY()) &&
  366.                 Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop();
  367.     }
  368.  
  369.  
  370.     @Override
  371.     public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) {
  372.         if (!child.isShown() || !mAllowUserDragging) {
  373.             return false;
  374.         }
  375.  
  376.         if (event.getAction() == MotionEvent.ACTION_DOWN) {
  377.             if(getState() != STATE_DRAGGING) {
  378.                 this.mPrevState = getState();
  379.                 Log.i(TAG, "Set Prev State to : " + getState());
  380.             }
  381.         }
  382.  
  383.         int action = event.getActionMasked();
  384.         if (mState == STATE_DRAGGING && action == MotionEvent.ACTION_DOWN) {
  385.             return true;
  386.         }
  387.         if (mViewDragHelper != null) {
  388.             mViewDragHelper.processTouchEvent(event);
  389.         }
  390.         // Record the velocity
  391.         if (action == MotionEvent.ACTION_DOWN) {
  392.             reset();
  393.         }
  394.         if (mVelocityTracker == null) {
  395.             mVelocityTracker = VelocityTracker.obtain();
  396.         }
  397.         mVelocityTracker.addMovement(event);
  398.         // The ViewDragHelper tries to capture only the top-most View. We have to explicitly tell it
  399.         // to capture the bottom sheet in case it is not captured and the touch slop is passed.
  400.         if (action == MotionEvent.ACTION_MOVE && !mIgnoreEvents) {
  401.             if (Math.abs(mInitialY - event.getY()) > mViewDragHelper.getTouchSlop()) {
  402.                 mViewDragHelper.captureChildView(child, event.getPointerId(event.getActionIndex()));
  403.             }
  404.         }
  405.         return !mIgnoreEvents;
  406.     }
  407.  
  408.     @Override
  409.     public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child,
  410.                                        View directTargetChild, View target, int nestedScrollAxes) {
  411.         if (!mAllowUserDragging) {
  412.             return false;
  413.         }
  414.         mNestedScrolled = false;
  415.         return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
  416.     }
  417.  
  418.     @Override
  419.     public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx,
  420.                                   int dy, int[] consumed) {
  421.         if (!mAllowUserDragging) {
  422.             return;
  423.         }
  424.         View scrollingChild = mNestedScrollingChildRef.get();
  425.         if (target != scrollingChild) {
  426.             return;
  427.         }
  428.         int currentTop = child.getTop();
  429.         int newTop = currentTop - dy;
  430.         if (dy > 0) { // Upward
  431.             if (newTop < mMinOffset) {
  432.                 consumed[1] = currentTop - mMinOffset;
  433.                 ViewCompat.offsetTopAndBottom(child, -consumed[1]);
  434.                 setStateInternal(STATE_EXPANDED);
  435.             } else {
  436.                 consumed[1] = dy;
  437.                 ViewCompat.offsetTopAndBottom(child, -dy);
  438.                 setStateInternal(STATE_DRAGGING);
  439.             }
  440.         } else if (dy < 0) { // Downward
  441.             if (!ViewCompat.canScrollVertically(target, -1)) {
  442.                 if (newTop <= mMaxOffset || mHideable) {
  443.                     consumed[1] = dy;
  444.                     ViewCompat.offsetTopAndBottom(child, -dy);
  445.                     setStateInternal(STATE_DRAGGING);
  446.                 } else {
  447.                     consumed[1] = currentTop - mMaxOffset;
  448.                     ViewCompat.offsetTopAndBottom(child, -consumed[1]);
  449.                     setStateInternal(STATE_COLLAPSED);
  450.                 }
  451.             }
  452.         }
  453.         dispatchOnSlide(child.getTop());
  454.         mNestedScrolled = true;
  455.     }
  456.  
  457.     @Override
  458.     public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) {
  459.         if (!mAllowUserDragging) {
  460.             return;
  461.         }
  462.         if (child.getTop() == mMinOffset) {
  463.             setStateInternal(STATE_EXPANDED);
  464.             return;
  465.         }
  466.         if (target != mNestedScrollingChildRef.get() || !mNestedScrolled) {
  467.             return;
  468.         }
  469.  
  470.         mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
  471.         float xvel = mVelocityTracker.getXVelocity(mActivePointerId);
  472.         float yvel = mVelocityTracker.getYVelocity(mActivePointerId);
  473.  
  474.         int[] out = new int[2];
  475.         calculateTopAndTargetState(child, xvel, yvel, out);
  476.         int top = out[0];
  477.         int targetState = out[1];
  478.  
  479.         if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
  480.             setStateInternal(STATE_SETTLING);
  481.             ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState));
  482.         } else {
  483.             setStateInternal(targetState);
  484.         }
  485.         mNestedScrolled = false;
  486.     }
  487.  
  488.     @Override
  489.     public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target,
  490.                                     float velocityX, float velocityY) {
  491.         if (!mAllowUserDragging) {
  492.             return false;
  493.         }
  494.         return target == mNestedScrollingChildRef.get() &&
  495.                 (mState != STATE_EXPANDED ||
  496.                         super.onNestedPreFling(coordinatorLayout, child, target,
  497.                                 velocityX, velocityY));
  498.     }
  499.  
  500.     /**
  501.      * Sets the height of the bottom sheet when it is collapsed.
  502.      *
  503.      * @param peekHeight The height of the collapsed bottom sheet in pixels, or
  504.      *                   {@link #PEEK_HEIGHT_AUTO} to configure the sheet to peek automatically
  505.      *                   at 16:9 ratio keyline.
  506.      * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_peekHeight
  507.      */
  508.     public final void setPeekHeight(int peekHeight) {
  509.         boolean layout = false;
  510.         if (peekHeight == PEEK_HEIGHT_AUTO) {
  511.             if (!mPeekHeightAuto) {
  512.                 mPeekHeightAuto = true;
  513.                 layout = true;
  514.             }
  515.         } else if (mPeekHeightAuto || mPeekHeight != peekHeight) {
  516.             mPeekHeightAuto = false;
  517.             mPeekHeight = Math.max(0, peekHeight);
  518.             mMaxOffset = mParentHeight - peekHeight;
  519.             layout = true;
  520.         }
  521.         if (layout && mState == STATE_COLLAPSED && mViewRef != null) {
  522.             V view = mViewRef.get();
  523.             if (view != null) {
  524.                 view.requestLayout();
  525.             }
  526.         }
  527.     }
  528.  
  529.     /**
  530.      * Gets the height of the bottom sheet when it is collapsed.
  531.      *
  532.      * @return The height of the collapsed bottom sheet in pixels, or {@link #PEEK_HEIGHT_AUTO}
  533.      * if the sheet is configured to peek automatically at 16:9 ratio keyline
  534.      * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_peekHeight
  535.      */
  536.     public final int getPeekHeight() {
  537.         return mPeekHeightAuto ? PEEK_HEIGHT_AUTO : mPeekHeight;
  538.     }
  539.  
  540.     /**
  541.      * Sets the offset of the bottom sheet when it is anchored.
  542.      *
  543.      * @param anchorOffset The offset of the anchored bottom sheet in pixels.
  544.      * @attr ref com.trafi.anchorbottomsheetbehavior.R.styleable#AnchorBottomSheetBehavior_Layout_behavior_anchorOffset
  545.      */
  546.     public final void setAnchorOffset(int anchorOffset) {
  547.         if (mAnchorOffset != anchorOffset) {
  548.             mAnchorOffset = anchorOffset;
  549.  
  550.             if (mDisableExpanded) {
  551.                 mMinOffset = mAnchorOffset;
  552.             }
  553.  
  554.             if (mState == STATE_ANCHORED) {
  555.                 setStateInternal(STATE_SETTLING);
  556.                 setState(STATE_ANCHORED);
  557.             }
  558.         }
  559.     }
  560.  
  561.     /**
  562.      * Gets the offset of the bottom sheet when it is anchored.
  563.      *
  564.      * @return The offset of the anchored bottom sheet in pixels.
  565.      * @attr ref com.trafi.anchorbottomsheetbehavior.R.styleable#AnchorBottomSheetBehavior_Layout_behavior_anchorOffset
  566.      */
  567.     public final int getAnchorOffset() {
  568.         return mAnchorOffset;
  569.     }
  570.  
  571.     /**
  572.      * Sets whether this bottom sheet can hide when it is swiped down.
  573.      *
  574.      * @param hideable {@code true} to make this bottom sheet hideable.
  575.      * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_hideable
  576.      */
  577.     public void setHideable(boolean hideable) {
  578.         mHideable = hideable;
  579.     }
  580.  
  581.     /**
  582.      * Gets whether this bottom sheet can hide when it is swiped down.
  583.      *
  584.      * @return {@code true} if this bottom sheet can hide.
  585.      * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_hideable
  586.      */
  587.     public boolean isHideable() {
  588.         return mHideable;
  589.     }
  590.  
  591.     /**
  592.      * Sets whether this bottom sheet should skip the collapsed state when it is being hidden
  593.      * after it is expanded once. Setting this to true has no effect unless the sheet is hideable.
  594.      *
  595.      * @param skipCollapsed True if the bottom sheet should skip the collapsed state.
  596.      * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_skipCollapsed
  597.      */
  598.     public void setSkipCollapsed(boolean skipCollapsed) {
  599.         mSkipCollapsed = skipCollapsed;
  600.     }
  601.  
  602.     /**
  603.      * Sets whether this bottom sheet should skip the collapsed state when it is being hidden
  604.      * after it is expanded once.
  605.      *
  606.      * @return Whether the bottom sheet should skip the collapsed state.
  607.      * @attr ref android.support.design.R.styleable#BottomSheetBehavior_Layout_behavior_skipCollapsed
  608.      */
  609.     public boolean getSkipCollapsed() {
  610.         return mSkipCollapsed;
  611.     }
  612.  
  613.     /**
  614.      * Sets whether this bottom sheet should skip the anchored state when it is being collapsed from
  615.      * an expanded state or when it is being expanded from a collapsed state.
  616.      *
  617.      * @param skipAnchored True if the bottom sheet should skip the anchored state.
  618.      * @attr ref R.styleable#AnchorBottomSheetBehavior_Layout_behavior_skipAnchored
  619.      */
  620.     public void setSkipAnchored(boolean skipAnchored) {
  621.         mSkipAnchored = skipAnchored;
  622.     }
  623.  
  624.     /**
  625.      * Sets whether this bottom sheet should skip the anchored state when it is being collapsed from
  626.      * an expanded state or when it is being expanded from a collapsed state.
  627.      *
  628.      * @return Whether the bottom sheet should skip the anchored state.
  629.      * @attr ref R.styleable#AnchorBottomSheetBehavior_Layout_behavior_skipAnchored
  630.      */
  631.     public boolean getSkipAnchored() {
  632.         return mSkipAnchored;
  633.     }
  634.  
  635.     public boolean isDisableExpanded() {
  636.         return mDisableExpanded;
  637.     }
  638.  
  639.     public void setDisableExpanded(boolean mDisableExpanded) {
  640.         this.mDisableExpanded = mDisableExpanded;
  641.     }
  642.  
  643.     /**
  644.      * Registers a callback to be notified of bottom sheet events.
  645.      *
  646.      * @param callback The callback to notify when bottom sheet events occur.
  647.      */
  648.     public void addBottomSheetCallback(CustomBottomSheetBehavior.BottomSheetCallback callback) {
  649.         mCallbacks.add(callback);
  650.     }
  651.  
  652.     public void removeBottomSheetCallback(CustomBottomSheetBehavior.BottomSheetCallback callback) {
  653.         mCallbacks.remove(callback);
  654.     }
  655.  
  656.     public void setAllowUserDragging(boolean allowUserDragging) {
  657.         mAllowUserDragging = allowUserDragging;
  658.     }
  659.  
  660.     public boolean getAllowUserDragging() {
  661.         return mAllowUserDragging;
  662.     }
  663.  
  664.     /**
  665.      * Sets the state of the bottom sheet. The bottom sheet will transition to that state with
  666.      * animation.
  667.      *
  668.      * @param state One of {@link #STATE_COLLAPSED}, {@link #STATE_EXPANDED},
  669.      *              {@link #STATE_ANCHORED} or{@link #STATE_HIDDEN}.
  670.      */
  671.     public final void setState(final @CustomBottomSheetBehavior.State int state) {
  672.         if (state == mState) {
  673.             return;
  674.         }
  675.         if (mViewRef == null) {
  676.             // The view is not laid out yet; modify mState and let onLayoutChild handle it later
  677.             if (state == STATE_COLLAPSED || state == STATE_EXPANDED || state == STATE_ANCHORED ||
  678.                     (mHideable && state == STATE_HIDDEN)) {
  679.                 mState = state;
  680.             }
  681.             return;
  682.         }
  683.         final V child = mViewRef.get();
  684.         if (child == null) {
  685.             return;
  686.         }
  687.         // Start the animation; wait until a pending layout if there is one.
  688.         ViewParent parent = child.getParent();
  689.         if (parent != null && parent.isLayoutRequested() && ViewCompat.isAttachedToWindow(child)) {
  690.             child.post(new Runnable() {
  691.                 @Override
  692.                 public void run() {
  693.                     startSettlingAnimation(child, state);
  694.                 }
  695.             });
  696.         } else {
  697.             startSettlingAnimation(child, state);
  698.         }
  699.     }
  700.  
  701.     /**
  702.      * Gets the current state of the bottom sheet.
  703.      *
  704.      * @return One of {@link #STATE_EXPANDED}, {@link #STATE_ANCHORED}, {@link #STATE_COLLAPSED},
  705.      * {@link #STATE_DRAGGING}, and {@link #STATE_SETTLING}.
  706.      */
  707.     @CustomBottomSheetBehavior.State
  708.     public final int getState() {
  709.         return mState;
  710.     }
  711.  
  712.     void setStateInternal(@CustomBottomSheetBehavior.State int state) {
  713.         if (mState == state) {
  714.             return;
  715.         }
  716.         int oldState = mState;
  717.         mState = state;
  718.         View bottomSheet = mViewRef.get();
  719.         if (bottomSheet != null) {
  720.             for (int i = 0; i < mCallbacks.size(); i++) {
  721.                 mCallbacks.get(i).onStateChanged(bottomSheet, oldState, state);
  722.             }
  723.         }
  724.     }
  725.  
  726.     private void reset() {
  727.         mActivePointerId = ViewDragHelper.INVALID_POINTER;
  728.         if (mVelocityTracker != null) {
  729.             mVelocityTracker.recycle();
  730.             mVelocityTracker = null;
  731.         }
  732.     }
  733.  
  734.     private void calculateTopAndTargetState(View child, float xvel, float yvel, int[] out) {
  735.         int top;
  736.         @CustomBottomSheetBehavior.State int targetState;
  737.  
  738.         if (yvel < 0 && Math.abs(yvel) > mMinimumVelocity && Math.abs(yvel) > Math.abs(xvel)) {
  739.             // scrolling up, i.e. expanding
  740.             if (shouldExpand(child, yvel)) {
  741.  
  742.                 if(mPrevState == STATE_COLLAPSED) {
  743.                     top = mAnchorOffset;
  744.                     targetState = STATE_ANCHORED;
  745.                 }
  746.                 else {
  747.                     top = mMinOffset;
  748.                     targetState = STATE_EXPANDED;
  749.                 }
  750.             } else {
  751.                 top = mAnchorOffset;
  752.                 targetState = STATE_ANCHORED;
  753.             }
  754.         } else if (mHideable && shouldHide(child, yvel)) {
  755.             top = mParentHeight;
  756.             targetState = STATE_HIDDEN;
  757.         } else if (yvel > 0 && Math.abs(yvel) > mMinimumVelocity && Math.abs(yvel) > Math.abs(xvel)) {
  758.             // scrolling down, i.e. collapsing
  759.             if (shouldCollapse(child, yvel)) {
  760.  
  761.                 if(mPrevState == STATE_EXPANDED) {
  762.                     top = mAnchorOffset;
  763.                     targetState = STATE_ANCHORED;
  764.                 }
  765.                 else {
  766.                     top = mMaxOffset;
  767.                     targetState = STATE_COLLAPSED;
  768.                 }
  769.             } else {
  770.                 top = mAnchorOffset;
  771.                 targetState = STATE_ANCHORED;
  772.             }
  773.         } else {
  774.             // not scrolling much, i.e. stationary
  775.             int currentTop = child.getTop();
  776.             int distanceToExpanded = Math.abs(currentTop - mMinOffset);
  777.             int distanceToCollapsed = Math.abs(currentTop - mMaxOffset);
  778.             int distanceToAnchor = Math.abs(currentTop - mAnchorOffset);
  779.             if (mAnchorOffset > mMinOffset
  780.                     && distanceToAnchor < distanceToExpanded
  781.                     && distanceToAnchor < distanceToCollapsed) {
  782.                 top = mAnchorOffset;
  783.                 targetState = STATE_ANCHORED;
  784.             } else if (distanceToExpanded < distanceToCollapsed) {
  785.                 top = mMinOffset;
  786.                 targetState = STATE_EXPANDED;
  787.             } else {
  788.                 top = mMaxOffset;
  789.                 targetState = STATE_COLLAPSED;
  790.             }
  791.         }
  792.  
  793.         out[0] = top;
  794.         out[1] = targetState;
  795.     }
  796.  
  797.     boolean shouldHide(View child, float yvel) {
  798.         if (mSkipCollapsed) {
  799.             return true;
  800.         }
  801.         if (child.getTop() < mMaxOffset) {
  802.             // It should not hide, but collapse.
  803.             return false;
  804.         }
  805.         final float newTop = child.getTop() + yvel * HIDE_FRICTION;
  806.         return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD;
  807.     }
  808.  
  809.     boolean shouldExpand(View child, float yvel) {
  810.         if (mSkipAnchored || mMinOffset >= mAnchorOffset) {
  811.             return true;
  812.         }
  813.         int currentTop = child.getTop();
  814.         if (currentTop < mAnchorOffset) {
  815.             return true;
  816.         }
  817.         final float newTop = currentTop + yvel * EXPAND_FRICTION;
  818.         return newTop < mAnchorOffset;
  819.     }
  820.  
  821.     boolean shouldCollapse(View child, float yvel) {
  822.         if (mSkipAnchored || mMinOffset >= mAnchorOffset) {
  823.             return true;
  824.         }
  825.         int currentTop = child.getTop();
  826.         if (currentTop > mAnchorOffset) {
  827.             return true;
  828.         }
  829.         final float newTop = currentTop + yvel * COLLAPSE_FRICTION;
  830.         return newTop > mAnchorOffset;
  831.     }
  832.  
  833.     private View findScrollingChild(View view) {
  834.         if (view instanceof NestedScrollingChild) {
  835.             return view;
  836.         }
  837.         //ToDo
  838.         if (view instanceof ViewPager2) {
  839.             ViewPager2 viewPager = (ViewPager2) view;
  840.             /*View currentViewPagerChild = ViewPager2Utils.getCurrentView(viewPager);
  841.             View scrollingChild = findScrollingChild(currentViewPagerChild);
  842.             if (scrollingChild != null) {
  843.                 return scrollingChild;
  844.             }*/
  845.         } else if (view instanceof ViewPager) {
  846.             ViewPager viewPager = (ViewPager) view;
  847.             /*View currentViewPagerChild = ViewPagerUtils.getCurrentView(viewPager);
  848.             View scrollingChild = findScrollingChild(currentViewPagerChild);
  849.             if (scrollingChild != null) {
  850.                 return scrollingChild;
  851.             }*/
  852.         } else if (view instanceof ViewGroup) {
  853.             ViewGroup group = (ViewGroup) view;
  854.             for (int i = 0, count = group.getChildCount(); i < count; i++) {
  855.                 View scrollingChild = findScrollingChild(group.getChildAt(i));
  856.                 if (scrollingChild != null) {
  857.                     return scrollingChild;
  858.                 }
  859.             }
  860.         }
  861.         return null;
  862.     }
  863.  
  864.     void startSettlingAnimation(View child, int state) {
  865.         int top;
  866.         if (state == STATE_COLLAPSED) {
  867.             top = mMaxOffset;
  868.         } else if (state == STATE_EXPANDED) {
  869.             top = mMinOffset;
  870.         } else if (state == STATE_ANCHORED) {
  871.             if (mAnchorOffset > mMinOffset) {
  872.                 top = mAnchorOffset;
  873.             } else {
  874.                 state = STATE_EXPANDED;
  875.                 top = mMinOffset;
  876.             }
  877.         } else if (mHideable && state == STATE_HIDDEN) {
  878.             top = mParentHeight;
  879.         } else {
  880.             throw new IllegalArgumentException("Illegal state argument: " + state);
  881.         }
  882.         setStateInternal(STATE_SETTLING);
  883.         if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) {
  884.             ViewCompat.postOnAnimation(child, new SettleRunnable(child, state));
  885.         }
  886.     }
  887.  
  888.     private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() {
  889.  
  890.         @Override
  891.         public boolean tryCaptureView(View child, int pointerId) {
  892.             if (mState == STATE_DRAGGING) {
  893.                 return false;
  894.             }
  895.             if (mTouchingScrollingChild) {
  896.                 return false;
  897.             }
  898.             if (mState == STATE_EXPANDED && mActivePointerId == pointerId) {
  899.                 View scroll = mNestedScrollingChildRef.get();
  900.                 if (scroll != null && ViewCompat.canScrollVertically(scroll, -1)) {
  901.                     // Let the content scroll up
  902.                     return false;
  903.                 }
  904.             }
  905.             return mViewRef != null && mViewRef.get() == child;
  906.         }
  907.  
  908.         @Override
  909.         public void onViewPositionChanged(View changedView, int left, int top, int dx, int dy) {
  910.             dispatchOnSlide(top);
  911.         }
  912.  
  913.         @Override
  914.         public void onViewDragStateChanged(int state) {
  915.             if (state == ViewDragHelper.STATE_DRAGGING) {
  916.                 setStateInternal(STATE_DRAGGING);
  917.             }
  918.         }
  919.  
  920.         @Override
  921.         public void onViewReleased(View releasedChild, float xvel, float yvel) {
  922.             int[] out = new int[2];
  923.             calculateTopAndTargetState(releasedChild, xvel, yvel, out);
  924.             int top = out[0];
  925.             @CustomBottomSheetBehavior.State int targetState = out[1];
  926.  
  927.             if (mViewDragHelper.settleCapturedViewAt(releasedChild.getLeft(), top)) {
  928.                 setStateInternal(STATE_SETTLING);
  929.                 ViewCompat.postOnAnimation(releasedChild,
  930.                         new SettleRunnable(releasedChild, targetState));
  931.             } else {
  932.                 setStateInternal(targetState);
  933.             }
  934.         }
  935.  
  936.         @Override
  937.         public int clampViewPositionVertical(View child, int top, int dy) {
  938.             if(mPrevState == STATE_COLLAPSED) {
  939.                 //Log.i(TAG, "Prev State is Collapsed, Top = " + (mParentHeight - (getAnchorOffset()+getPeekHeight())));
  940.                 int maxTop = getAnchorOffset() - 64;
  941.  
  942.                 return Math.max(top, maxTop);
  943.             }
  944.             if(mPrevState == STATE_EXPANDED) {
  945.                 //Log.i(TAG, "Prev State is Collapsed, Top = " + (mParentHeight - (getAnchorOffset()+getPeekHeight())));
  946.  
  947.                 Log.i(TAG, "Dy : " + dy);
  948.                 Log.i(TAG, "Top : " + top);
  949.                 Log.i(TAG, "Min Offset : " + mMinOffset);
  950.  
  951.                 int maxTop = getAnchorOffset() + 64;
  952.                 Log.i(TAG, "Max offset : " + maxTop);
  953.                 return Math.min(Math.max(top, mMinOffset), maxTop);
  954.             }
  955.             else {
  956.                 return constrain(top, mMinOffset, mHideable ? mParentHeight : mMaxOffset);
  957.             }
  958.         }
  959.  
  960.         private int constrain(int amount, int low, int high) {
  961.             return amount < low ? low : (amount > high ? high : amount);
  962.         }
  963.  
  964.         @Override
  965.         public int clampViewPositionHorizontal(View child, int left, int dx) {
  966.             return child.getLeft();
  967.         }
  968.  
  969.         @Override
  970.         public int getViewVerticalDragRange(View child) {
  971.             if (mHideable) {
  972.                 return mParentHeight - mMinOffset;
  973.             } else {
  974.                 return mMaxOffset - mMinOffset;
  975.             }
  976.         }
  977.     };
  978.  
  979.     void dispatchOnSlide(int top) {
  980.         View bottomSheet = mViewRef.get();
  981.         if (bottomSheet != null) {
  982.             float slideOffset;
  983.             if (top > mMaxOffset) {
  984.                 slideOffset = (float) (mMaxOffset - top) / (mParentHeight - mMaxOffset);
  985.             } else {
  986.                 slideOffset = (float) (mMaxOffset - top) / (mMaxOffset - mMinOffset);
  987.             }
  988.  
  989.             for (int i = 0; i < mCallbacks.size(); i++) {
  990.                 mCallbacks.get(i).onSlide(bottomSheet, slideOffset);
  991.             }
  992.         }
  993.     }
  994.  
  995.     @VisibleForTesting
  996.     int getPeekHeightMin() {
  997.         return mPeekHeightMin;
  998.     }
  999.  
  1000.     private class SettleRunnable implements Runnable {
  1001.  
  1002.         private final View mView;
  1003.  
  1004.         @CustomBottomSheetBehavior.State
  1005.         private final int mTargetState;
  1006.  
  1007.         SettleRunnable(View view, @CustomBottomSheetBehavior.State int targetState) {
  1008.             mView = view;
  1009.             mTargetState = targetState;
  1010.         }
  1011.  
  1012.         @Override
  1013.         public void run() {
  1014.             if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) {
  1015.                 ViewCompat.postOnAnimation(mView, this);
  1016.             } else {
  1017.                 setStateInternal(mTargetState);
  1018.             }
  1019.         }
  1020.     }
  1021.  
  1022.     protected static class SavedState extends AbsSavedState {
  1023.         @CustomBottomSheetBehavior.State
  1024.         final int state;
  1025.  
  1026.         public SavedState(Parcel source) {
  1027.             this(source, null);
  1028.         }
  1029.  
  1030.         public SavedState(Parcel source, ClassLoader loader) {
  1031.             super(source, loader);
  1032.             //noinspection ResourceType
  1033.             state = source.readInt();
  1034.         }
  1035.  
  1036.         public SavedState(Parcelable superState, @CustomBottomSheetBehavior.State int state) {
  1037.             super(superState);
  1038.             this.state = state;
  1039.         }
  1040.  
  1041.         @Override
  1042.         public void writeToParcel(Parcel out, int flags) {
  1043.             super.writeToParcel(out, flags);
  1044.             out.writeInt(state);
  1045.         }
  1046.  
  1047.         public static final Parcelable.Creator<SavedState> CREATOR = ParcelableCompat.newCreator(
  1048.                 new ParcelableCompatCreatorCallbacks<SavedState>() {
  1049.                     @Override
  1050.                     public SavedState createFromParcel(Parcel in, ClassLoader loader) {
  1051.                         return new SavedState(in, loader);
  1052.                     }
  1053.  
  1054.                     @Override
  1055.                     public SavedState[] newArray(int size) {
  1056.                         return new SavedState[size];
  1057.                     }
  1058.                 });
  1059.     }
  1060.  
  1061.     /**
  1062.      * A utility function to get the {@link CustomBottomSheetBehavior} associated with the {@code view}.
  1063.      *
  1064.      * @param view The {@link View} with {@link CustomBottomSheetBehavior}.
  1065.      * @return The {@link CustomBottomSheetBehavior} associated with the {@code view}.
  1066.      */
  1067.     @SuppressWarnings("unchecked")
  1068.     public static <extends View> CustomBottomSheetBehavior<V> from(V view) {
  1069.         ViewGroup.LayoutParams params = view.getLayoutParams();
  1070.         if (!(params instanceof CoordinatorLayout.LayoutParams)) {
  1071.             throw new IllegalArgumentException("The view is not a child of CoordinatorLayout");
  1072.         }
  1073.         CoordinatorLayout.Behavior behavior = ((CoordinatorLayout.LayoutParams) params)
  1074.                 .getBehavior();
  1075.         if (!(behavior instanceof CustomBottomSheetBehavior)) {
  1076.             throw new IllegalArgumentException(
  1077.                     "The view is not associated with AnchorBottomSheetBehavior");
  1078.         }
  1079.         return (CustomBottomSheetBehavior<V>) behavior;
  1080.     }
  1081. }

Editor

You can edit this paste and save as new: