From bc759d80feb2736e37bd881c4a46853b764feed7 Mon Sep 17 00:00:00 2001 From: Yuichi Araki Date: Wed, 30 Nov 2016 03:44:50 +0000 Subject: [PATCH] Merge "Fix BottomSheet over ScrollView" into mnc-ub-dev am: 129ef23221 am: 23848059a9 * commit '23848059a9a6265fe9c6db056e7e0d9b5f193f45': Fix BottomSheet over ScrollView GitOrigin-RevId=7dfefd41cc64cc46d41d9906526e013d92ef745f PiperOrigin-RevId: 140559487 --- res/layout/design_bottom_sheet_dialog.xml | 2 +- .../design/widget/BottomSheetBehavior.java | 168 +++++------------- .../design/widget/BottomSheetDialog.java | 2 + .../widget/BottomSheetDialogFragment.java | 5 +- .../support/design/widget/ViewGroupUtils.java | 4 + 5 files changed, 53 insertions(+), 128 deletions(-) diff --git a/res/layout/design_bottom_sheet_dialog.xml b/res/layout/design_bottom_sheet_dialog.xml index 56ef5ea42..cd5fb1857 100644 --- a/res/layout/design_bottom_sheet_dialog.xml +++ b/res/layout/design_bottom_sheet_dialog.xml @@ -24,7 +24,7 @@ diff --git a/src/android/support/design/widget/BottomSheetBehavior.java b/src/android/support/design/widget/BottomSheetBehavior.java index 4e27eeab6..bb9fe799b 100644 --- a/src/android/support/design/widget/BottomSheetBehavior.java +++ b/src/android/support/design/widget/BottomSheetBehavior.java @@ -24,14 +24,12 @@ import android.os.Parcelable; import android.support.annotation.IntDef; import android.support.design.R; import android.support.v4.view.MotionEventCompat; -import android.support.v4.view.VelocityTrackerCompat; +import android.support.v4.view.NestedScrollingChild; import android.support.v4.view.ViewCompat; import android.support.v4.widget.ViewDragHelper; import android.util.AttributeSet; import android.view.MotionEvent; -import android.view.VelocityTracker; import android.view.View; -import android.view.ViewConfiguration; import android.view.ViewGroup; import android.view.ViewParent; @@ -106,8 +104,6 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav // Whether to enable workaround for black non-rendered square private static final boolean NEEDS_INVALIDATING = Build.VERSION.SDK_INT < 23; - private float mMaximumVelocity; - private int mPeekHeight; private int mMinOffset; @@ -123,17 +119,15 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav private boolean mIgnoreEvents; - private int mLastNestedScrollDy; - private int mParentHeight; private WeakReference mViewRef; + private WeakReference mNestedScrollingChildRef; + private BottomSheetCallback mCallback; - private VelocityTracker mVelocityTracker; - - private int mActivePointerId; + private int mScrollingChildPointerId = MotionEvent.INVALID_POINTER_ID; /** * Default constructor for instantiating BottomSheetBehaviors. @@ -155,8 +149,6 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav R.styleable.BottomSheetBehavior_Params_behavior_peekHeight, 0)); setHideable(a.getBoolean(R.styleable.BottomSheetBehavior_Params_behavior_hideable, false)); a.recycle(); - ViewConfiguration configuration = ViewConfiguration.get(context); - mMaximumVelocity = configuration.getScaledMaximumFlingVelocity(); } @Override @@ -197,23 +189,17 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav mViewDragHelper = ViewDragHelper.create(parent, mDragCallback); } mViewRef = new WeakReference<>(child); + mNestedScrollingChildRef = new WeakReference<>(findScrollingChild(child)); return true; } @Override public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { int action = MotionEventCompat.getActionMasked(event); - // Record the velocity - if (action == MotionEvent.ACTION_DOWN) { - reset(); - } - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(event); switch (action) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: + mScrollingChildPointerId = MotionEvent.INVALID_POINTER_ID; // Reset the ignore flag if (mIgnoreEvents) { mIgnoreEvents = false; @@ -221,9 +207,16 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav } break; case MotionEvent.ACTION_DOWN: - mIgnoreEvents = !parent.isPointInChildBounds(child, - (int) event.getX(), (int) event.getY()); - mActivePointerId = MotionEventCompat.getPointerId(event, 0); + int x = (int) event.getX(); + int y = (int) event.getY(); + View scroll = mNestedScrollingChildRef.get(); + if (scroll != null && parent.isPointInChildBounds(scroll, x, y)) { + mScrollingChildPointerId = event.getPointerId(event.getActionIndex()); + } else { + mScrollingChildPointerId = MotionEvent.INVALID_POINTER_ID; + } + mIgnoreEvents = mScrollingChildPointerId == MotionEvent.INVALID_POINTER_ID && + !parent.isPointInChildBounds(child, x, y); break; } return !mIgnoreEvents && mViewDragHelper.shouldInterceptTouchEvent(event); @@ -232,98 +225,9 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav @Override public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { mViewDragHelper.processTouchEvent(event); - // Record the velocity - if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { - reset(); - } - if (mVelocityTracker == null) { - mVelocityTracker = VelocityTracker.obtain(); - } - mVelocityTracker.addMovement(event); return true; } - @Override - public boolean onStartNestedScroll(CoordinatorLayout coordinatorLayout, V child, - View directTargetChild, View target, int nestedScrollAxes) { - mLastNestedScrollDy = 0; - return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; - } - - @Override - public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, V child, View target, int dx, - int dy, int[] consumed) { - int currentTop = child.getTop(); - int newTop = currentTop - dy; - if (dy > 0) { // Upward - if (newTop < mMinOffset) { - consumed[1] = currentTop - mMinOffset; - child.offsetTopAndBottom(-consumed[1]); - if (NEEDS_INVALIDATING && mState != STATE_EXPANDED) { - child.invalidate(); - } - setStateInternal(STATE_EXPANDED); - } else { - consumed[1] = dy; - child.offsetTopAndBottom(-dy); - setStateInternal(STATE_DRAGGING); - if (NEEDS_INVALIDATING) { - child.invalidate(); - } - } - } else if (dy < 0) { // Downward - if (!ViewCompat.canScrollVertically(target, -1)) { - if (newTop <= mMaxOffset || mHideable) { - consumed[1] = dy; - child.offsetTopAndBottom(-dy); - setStateInternal(STATE_DRAGGING); - if (NEEDS_INVALIDATING) { - coordinatorLayout.invalidate(child.getLeft(), currentTop, - child.getRight(), coordinatorLayout.getHeight()); - } - } else { - consumed[1] = currentTop - mMaxOffset; - child.offsetTopAndBottom(-consumed[1]); - setStateInternal(STATE_COLLAPSED); - } - } - } - dispatchOnSlide(child.getTop()); - mLastNestedScrollDy = dy; - } - - @Override - public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, V child, View target) { - if (child.getTop() == mMinOffset) { - return; - } - int top; - int targetState; - if (mLastNestedScrollDy > 0) { - top = mMinOffset; - targetState = STATE_EXPANDED; - } else if (mHideable && shouldHide(child, getYVelocity())) { - top = mParentHeight; - targetState = STATE_HIDDEN; - } else { - top = mMaxOffset; - targetState = STATE_COLLAPSED; - } - if (mViewDragHelper.smoothSlideViewTo(child, child.getLeft(), top)) { - setStateInternal(STATE_SETTLING); - ViewCompat.postOnAnimation(child, new SettleRunnable(child, targetState)); - } else { - setStateInternal(targetState); - } - } - - @Override - public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, V child, View target, - float velocityX, float velocityY) { - return mState != STATE_EXPANDED || - super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY); - } - /** * Sets the height of the bottom sheet when it is collapsed. * @@ -423,14 +327,6 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav } } - private void reset() { - mActivePointerId = ViewDragHelper.INVALID_POINTER; - if (mVelocityTracker != null) { - mVelocityTracker.recycle(); - mVelocityTracker = null; - } - } - private boolean shouldHide(View child, float yvel) { if (child.getTop() < mMaxOffset) { // It should not hide, but collapse. @@ -440,15 +336,33 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav return Math.abs(newTop - mMaxOffset) / (float) mPeekHeight > HIDE_THRESHOLD; } - private float getYVelocity() { - mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity); - return VelocityTrackerCompat.getYVelocity(mVelocityTracker, mActivePointerId); + private View findScrollingChild(View view) { + if (view instanceof NestedScrollingChild) { + return view; + } + if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup) view; + for (int i = 0, count = group.getChildCount(); i < count; i++) { + View scrollingChild = findScrollingChild(group.getChildAt(i)); + if (scrollingChild != null) { + return scrollingChild; + } + } + } + return null; } private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() { @Override public boolean tryCaptureView(View child, int pointerId) { + if (mState == STATE_EXPANDED && mScrollingChildPointerId == pointerId) { + View scroll = mNestedScrollingChildRef.get(); + if (scroll != null && ViewCompat.canScrollVertically(scroll, -1)) { + // Let the content scroll up + return false; + } + } return mViewRef != null && mViewRef.get() == child; } @@ -509,6 +423,14 @@ public class BottomSheetBehavior extends CoordinatorLayout.Behav return child.getLeft(); } + @Override + public int getViewVerticalDragRange(View child) { + if (mHideable) { + return mParentHeight - mMinOffset; + } else { + return mMaxOffset - mMinOffset; + } + } }; private void dispatchOnSlide(int top) { diff --git a/src/android/support/design/widget/BottomSheetDialog.java b/src/android/support/design/widget/BottomSheetDialog.java index d3d605e50..3399de022 100644 --- a/src/android/support/design/widget/BottomSheetDialog.java +++ b/src/android/support/design/widget/BottomSheetDialog.java @@ -57,6 +57,8 @@ public class BottomSheetDialog extends AppCompatDialog { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); + getWindow().setLayout( + ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); } @Override diff --git a/src/android/support/design/widget/BottomSheetDialogFragment.java b/src/android/support/design/widget/BottomSheetDialogFragment.java index 1ec0dfd40..d2d2a1d9a 100644 --- a/src/android/support/design/widget/BottomSheetDialogFragment.java +++ b/src/android/support/design/widget/BottomSheetDialogFragment.java @@ -31,10 +31,7 @@ public class BottomSheetDialogFragment extends AppCompatDialogFragment { @Override public Dialog onCreateDialog(Bundle savedInstanceState) { - Dialog dialog = new BottomSheetDialog(getActivity(), getTheme()); - dialog.getWindow().setLayout( - ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - return dialog; + return new BottomSheetDialog(getActivity(), getTheme()); } /** @hide */ diff --git a/src/android/support/design/widget/ViewGroupUtils.java b/src/android/support/design/widget/ViewGroupUtils.java index 47cc08072..a5d2f8a88 100644 --- a/src/android/support/design/widget/ViewGroupUtils.java +++ b/src/android/support/design/widget/ViewGroupUtils.java @@ -31,6 +31,10 @@ class ViewGroupUtils { @Override public void offsetDescendantRect(ViewGroup parent, View child, Rect rect) { parent.offsetDescendantRectToMyCoords(child, rect); + // View#offsetDescendantRectToMyCoords includes scroll offsets of the last child. + // We need to reverse it here so that we get the rect of the view itself rather + // than its content. + rect.offset(child.getScrollX(), child.getScrollY()); } }