1b7f9224b1495db47eb8fd813b5912250e900770aChris Banes/* 2b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Copyright (C) 2015 The Android Open Source Project 3b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 4b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Licensed under the Apache License, Version 2.0 (the "License"); 5b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * you may not use this file except in compliance with the License. 6b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * You may obtain a copy of the License at 7b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 8b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * http://www.apache.org/licenses/LICENSE-2.0 9b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 10b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Unless required by applicable law or agreed to in writing, software 11b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * distributed under the License is distributed on an "AS IS" BASIS, 12b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * See the License for the specific language governing permissions and 14b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * limitations under the License. 15b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 16b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 17b7f9224b1495db47eb8fd813b5912250e900770aChris Banespackage android.support.design.widget; 18b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 19b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.annotation.IntDef; 2002751b16719af2e3f8212f93c001da1b0566b1b5Chris Banesimport android.support.annotation.NonNull; 217a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banesimport android.support.v4.view.MotionEventCompat; 22b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.view.ViewCompat; 23b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.support.v4.widget.ViewDragHelper; 24b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.MotionEvent; 25b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.View; 26b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport android.view.ViewGroup; 2790dfc0aa3215c247825ea1001478ed17a767c45dChris Banesimport android.view.ViewParent; 28b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 29b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport java.lang.annotation.Retention; 30b7f9224b1495db47eb8fd813b5912250e900770aChris Banesimport java.lang.annotation.RetentionPolicy; 31b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 32b7f9224b1495db47eb8fd813b5912250e900770aChris Banes/** 33b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * An interaction behavior plugin for child views of {@link CoordinatorLayout} to provide support 34b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * for the 'swipe-to-dismiss' gesture. 35b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 36b7f9224b1495db47eb8fd813b5912250e900770aChris Banespublic class SwipeDismissBehavior<V extends View> extends CoordinatorLayout.Behavior<V> { 37b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 38b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 39b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * A view is not currently being dragged or animating as a result of a fling/snap. 40b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 41b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public static final int STATE_IDLE = ViewDragHelper.STATE_IDLE; 42b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 43b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 44b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * A view is currently being dragged. The position is currently changing as a result 45b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * of user input or simulated user input. 46b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 47b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public static final int STATE_DRAGGING = ViewDragHelper.STATE_DRAGGING; 48b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 49b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 50b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * A view is currently settling into place as a result of a fling or 51b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * predefined non-interactive motion. 52b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 53b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public static final int STATE_SETTLING = ViewDragHelper.STATE_SETTLING; 54b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 55b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** @hide */ 56b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @IntDef({SWIPE_DIRECTION_START_TO_END, SWIPE_DIRECTION_END_TO_START, SWIPE_DIRECTION_ANY}) 57b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Retention(RetentionPolicy.SOURCE) 58b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private @interface SwipeDirection {} 59b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 60b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 61b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Swipe direction that only allows swiping in the direction of start-to-end. That is 62b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * left-to-right in LTR, or right-to-left in RTL. 63b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 64b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public static final int SWIPE_DIRECTION_START_TO_END = 0; 65b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 66b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 67b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Swipe direction that only allows swiping in the direction of end-to-start. That is 68b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * right-to-left in LTR or left-to-right in RTL. 69b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 70b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public static final int SWIPE_DIRECTION_END_TO_START = 1; 71b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 72b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 73b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Swipe direction which allows swiping in either direction. 74b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 75b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public static final int SWIPE_DIRECTION_ANY = 2; 76b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 77b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private static final float DEFAULT_DRAG_DISMISS_THRESHOLD = 0.5f; 78b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private static final float DEFAULT_ALPHA_START_DISTANCE = 0f; 79b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private static final float DEFAULT_ALPHA_END_DISTANCE = DEFAULT_DRAG_DISMISS_THRESHOLD; 80b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 81b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private ViewDragHelper mViewDragHelper; 82b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private OnDismissListener mListener; 83b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private boolean mIgnoreEvents; 84b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 85b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private float mSensitivity = 0f; 86b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private boolean mSensitivitySet; 87b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 88b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private int mSwipeDirection = SWIPE_DIRECTION_ANY; 89b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private float mDragDismissThreshold = DEFAULT_DRAG_DISMISS_THRESHOLD; 90b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private float mAlphaStartSwipeDistance = DEFAULT_ALPHA_START_DISTANCE; 91b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private float mAlphaEndSwipeDistance = DEFAULT_ALPHA_END_DISTANCE; 92b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 93b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 94b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Callback interface used to notify the application that the view has been dismissed. 95b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 96b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public interface OnDismissListener { 97b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 98b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Called when {@code view} has been dismissed via swiping. 99b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 100b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void onDismiss(View view); 101b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 102b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 103b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Called when the drag state has changed. 104b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 105b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * @param state the new state. One of 106b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}. 107b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 108b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void onDragStateChanged(int state); 109b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 110b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 111b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 112b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Set the listener to be used when a dismiss event occurs. 113b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 114b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * @param listener the listener to use. 115b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 116b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void setListener(OnDismissListener listener) { 117b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mListener = listener; 118b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 119b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 120b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 121b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Sets the swipe direction for this behavior. 122b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 123b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * @param direction one of the {@link #SWIPE_DIRECTION_START_TO_END}, 124b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * {@link #SWIPE_DIRECTION_END_TO_START} or {@link #SWIPE_DIRECTION_ANY} 125b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 126b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void setSwipeDirection(@SwipeDirection int direction) { 127b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mSwipeDirection = direction; 128b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 129b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 130b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 131b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Set the threshold for telling if a view has been dragged enough to be dismissed. 132b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 133b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * @param distance a ratio of a view's width, values are clamped to 0 >= x <= 1f; 134b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 135b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void setDragDismissDistance(float distance) { 136b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mDragDismissThreshold = clamp(0f, distance, 1f); 137b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 138b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 139b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 140b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * The minimum swipe distance before the view's alpha is modified. 141b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 142b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * @param fraction the distance as a fraction of the view's width. 143b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 144b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void setStartAlphaSwipeDistance(float fraction) { 145b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mAlphaStartSwipeDistance = clamp(0f, fraction, 1f); 146b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 147b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 148b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 149b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * The maximum swipe distance for the view's alpha is modified. 150b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 151b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * @param fraction the distance as a fraction of the view's width. 152b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 153b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void setEndAlphaSwipeDistance(float fraction) { 154b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mAlphaEndSwipeDistance = clamp(0f, fraction, 1f); 155b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 156b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 157b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 158b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Set the sensitivity used for detecting the start of a swipe. This only takes effect if 159b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * no touch handling has occured yet. 160b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 161b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * @param sensitivity Multiplier for how sensitive we should be about detecting 162b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * the start of a drag. Larger values are more sensitive. 1.0f is normal. 163b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 164b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void setSensitivity(float sensitivity) { 165b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mSensitivity = sensitivity; 166b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mSensitivitySet = true; 167b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 168b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 169b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 170b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public boolean onInterceptTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 1717a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes switch (MotionEventCompat.getActionMasked(event)) { 172b7f9224b1495db47eb8fd813b5912250e900770aChris Banes case MotionEvent.ACTION_UP: 173b7f9224b1495db47eb8fd813b5912250e900770aChris Banes case MotionEvent.ACTION_CANCEL: 174b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // Reset the ignore flag 175b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mIgnoreEvents) { 176b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mIgnoreEvents = false; 177b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return false; 178b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 179b7f9224b1495db47eb8fd813b5912250e900770aChris Banes break; 1807a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes default: 1817a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes mIgnoreEvents = !parent.isPointInChildBounds(child, 1827a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes (int) event.getX(), (int) event.getY()); 1837a13691bbc7b9ac557b69a1e8188a6b1a16b86efChris Banes break; 184b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 185b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 186b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mIgnoreEvents) { 187b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return false; 188b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 189b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 190b7f9224b1495db47eb8fd813b5912250e900770aChris Banes ensureViewDragHelper(parent); 191b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return mViewDragHelper.shouldInterceptTouchEvent(event); 192b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 193b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 194b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 195b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public boolean onTouchEvent(CoordinatorLayout parent, V child, MotionEvent event) { 196b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mViewDragHelper != null) { 197b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mViewDragHelper.processTouchEvent(event); 198b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return true; 199b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 200b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return false; 201b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 202b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 20302751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes /** 20402751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes * Called when the user's input indicates that they want to swipe the given view. 20502751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes * 20602751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes * @param view View the user is attempting to swipe 20702751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes * @return true if the view can be dismissed via swiping, false otherwise 20802751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes */ 20902751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes public boolean canSwipeDismissView(@NonNull View view) { 21002751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes return true; 21102751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes } 21202751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes 213b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private final ViewDragHelper.Callback mDragCallback = new ViewDragHelper.Callback() { 21478d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas private static final int INVALID_POINTER_ID = -1; 21578d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas 216b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private int mOriginalCapturedViewLeft; 21778d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas private int mActivePointerId = INVALID_POINTER_ID; 218b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 219b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 220b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public boolean tryCaptureView(View child, int pointerId) { 22178d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas // Only capture if we don't already have an active pointer id 22278d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas return mActivePointerId == INVALID_POINTER_ID && canSwipeDismissView(child); 22302751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes } 22402751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes 22502751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes @Override 22602751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes public void onViewCaptured(View capturedChild, int activePointerId) { 22778d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas mActivePointerId = activePointerId; 22802751b16719af2e3f8212f93c001da1b0566b1b5Chris Banes mOriginalCapturedViewLeft = capturedChild.getLeft(); 22990dfc0aa3215c247825ea1001478ed17a767c45dChris Banes 23090dfc0aa3215c247825ea1001478ed17a767c45dChris Banes // The view has been captured, and thus a drag is about to start so stop any parents 23190dfc0aa3215c247825ea1001478ed17a767c45dChris Banes // intercepting 23290dfc0aa3215c247825ea1001478ed17a767c45dChris Banes final ViewParent parent = capturedChild.getParent(); 23390dfc0aa3215c247825ea1001478ed17a767c45dChris Banes if (parent != null) { 23490dfc0aa3215c247825ea1001478ed17a767c45dChris Banes parent.requestDisallowInterceptTouchEvent(true); 23590dfc0aa3215c247825ea1001478ed17a767c45dChris Banes } 236b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 237b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 238b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 239b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void onViewDragStateChanged(int state) { 240b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mListener != null) { 241b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mListener.onDragStateChanged(state); 242b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 243b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 244b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 245b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 246b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void onViewReleased(View child, float xvel, float yvel) { 24778d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas // Reset the active pointer ID 24878d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas mActivePointerId = INVALID_POINTER_ID; 24978d6f4ad1fc1e2d57b138e31f47f2267731ee95eAurimas Liutikas 250b7f9224b1495db47eb8fd813b5912250e900770aChris Banes final int childWidth = child.getWidth(); 251b7f9224b1495db47eb8fd813b5912250e900770aChris Banes int targetLeft; 252b7f9224b1495db47eb8fd813b5912250e900770aChris Banes boolean dismiss = false; 253b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 254b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (shouldDismiss(child, xvel)) { 255b7f9224b1495db47eb8fd813b5912250e900770aChris Banes targetLeft = child.getLeft() < mOriginalCapturedViewLeft 256b7f9224b1495db47eb8fd813b5912250e900770aChris Banes ? mOriginalCapturedViewLeft - childWidth 257b7f9224b1495db47eb8fd813b5912250e900770aChris Banes : mOriginalCapturedViewLeft + childWidth; 258b7f9224b1495db47eb8fd813b5912250e900770aChris Banes dismiss = true; 259b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else { 260b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // Else, reset back to the original left 261b7f9224b1495db47eb8fd813b5912250e900770aChris Banes targetLeft = mOriginalCapturedViewLeft; 262b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 263b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 264b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mViewDragHelper.settleCapturedViewAt(targetLeft, child.getTop())) { 265b7f9224b1495db47eb8fd813b5912250e900770aChris Banes ViewCompat.postOnAnimation(child, new SettleRunnable(child, dismiss)); 266b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else if (dismiss && mListener != null) { 267b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mListener.onDismiss(child); 268b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 269b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 270b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 271b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private boolean shouldDismiss(View child, float xvel) { 272b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (xvel != 0f) { 273b7f9224b1495db47eb8fd813b5912250e900770aChris Banes final boolean isRtl = ViewCompat.getLayoutDirection(child) 274b7f9224b1495db47eb8fd813b5912250e900770aChris Banes == ViewCompat.LAYOUT_DIRECTION_RTL; 275b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 276b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mSwipeDirection == SWIPE_DIRECTION_ANY) { 277b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // We don't care about the direction so return true 278b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return true; 279b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) { 280b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // We only allow start-to-end swiping, so the fling needs to be in the 281b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // correct direction 282b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return isRtl ? xvel < 0f : xvel > 0f; 283b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) { 284b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // We only allow end-to-start swiping, so the fling needs to be in the 285b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // correct direction 286b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return isRtl ? xvel > 0f : xvel < 0f; 287b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 288b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else { 289b7f9224b1495db47eb8fd813b5912250e900770aChris Banes final int distance = child.getLeft() - mOriginalCapturedViewLeft; 290b7f9224b1495db47eb8fd813b5912250e900770aChris Banes final int thresholdDistance = Math.round(child.getWidth() * mDragDismissThreshold); 291b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return Math.abs(distance) >= thresholdDistance; 292b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 293b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 294b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return false; 295b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 296b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 297b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 298b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public int getViewHorizontalDragRange(View child) { 299b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return child.getWidth(); 300b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 301b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 302b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 303b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public int clampViewPositionHorizontal(View child, int left, int dx) { 304b7f9224b1495db47eb8fd813b5912250e900770aChris Banes final boolean isRtl = ViewCompat.getLayoutDirection(child) 305b7f9224b1495db47eb8fd813b5912250e900770aChris Banes == ViewCompat.LAYOUT_DIRECTION_RTL; 306b7f9224b1495db47eb8fd813b5912250e900770aChris Banes int min, max; 307b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 308b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mSwipeDirection == SWIPE_DIRECTION_START_TO_END) { 309b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (isRtl) { 310b7f9224b1495db47eb8fd813b5912250e900770aChris Banes min = mOriginalCapturedViewLeft - child.getWidth(); 311b7f9224b1495db47eb8fd813b5912250e900770aChris Banes max = mOriginalCapturedViewLeft; 312b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else { 313b7f9224b1495db47eb8fd813b5912250e900770aChris Banes min = mOriginalCapturedViewLeft; 314b7f9224b1495db47eb8fd813b5912250e900770aChris Banes max = mOriginalCapturedViewLeft + child.getWidth(); 315b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 316b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else if (mSwipeDirection == SWIPE_DIRECTION_END_TO_START) { 317b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (isRtl) { 318b7f9224b1495db47eb8fd813b5912250e900770aChris Banes min = mOriginalCapturedViewLeft; 319b7f9224b1495db47eb8fd813b5912250e900770aChris Banes max = mOriginalCapturedViewLeft + child.getWidth(); 320b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else { 321b7f9224b1495db47eb8fd813b5912250e900770aChris Banes min = mOriginalCapturedViewLeft - child.getWidth(); 322b7f9224b1495db47eb8fd813b5912250e900770aChris Banes max = mOriginalCapturedViewLeft; 323b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 324b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else { 325b7f9224b1495db47eb8fd813b5912250e900770aChris Banes min = mOriginalCapturedViewLeft - child.getWidth(); 326b7f9224b1495db47eb8fd813b5912250e900770aChris Banes max = mOriginalCapturedViewLeft + child.getWidth(); 327b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 328b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 329b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return clamp(min, left, max); 330b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 331b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 332b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 333b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public int clampViewPositionVertical(View child, int top, int dy) { 334b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return child.getTop(); 335b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 336b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 337b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 338b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void onViewPositionChanged(View child, int left, int top, int dx, int dy) { 3392bda001ff9233e3dc3f8e27faf215534656d69afChris Banes final float startAlphaDistance = mOriginalCapturedViewLeft 3402bda001ff9233e3dc3f8e27faf215534656d69afChris Banes + child.getWidth() * mAlphaStartSwipeDistance; 3412bda001ff9233e3dc3f8e27faf215534656d69afChris Banes final float endAlphaDistance = mOriginalCapturedViewLeft 3422bda001ff9233e3dc3f8e27faf215534656d69afChris Banes + child.getWidth() * mAlphaEndSwipeDistance; 343b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 344b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (left <= startAlphaDistance) { 345b7f9224b1495db47eb8fd813b5912250e900770aChris Banes ViewCompat.setAlpha(child, 1f); 346b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else if (left >= endAlphaDistance) { 347b7f9224b1495db47eb8fd813b5912250e900770aChris Banes ViewCompat.setAlpha(child, 0f); 348b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else { 349b7f9224b1495db47eb8fd813b5912250e900770aChris Banes // We're between the start and end distances 350b7f9224b1495db47eb8fd813b5912250e900770aChris Banes final float distance = fraction(startAlphaDistance, endAlphaDistance, left); 351b7f9224b1495db47eb8fd813b5912250e900770aChris Banes ViewCompat.setAlpha(child, clamp(0f, 1f - distance, 1f)); 352b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 353b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 354b7f9224b1495db47eb8fd813b5912250e900770aChris Banes }; 355b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 356b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private void ensureViewDragHelper(ViewGroup parent) { 357b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mViewDragHelper == null) { 358b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mViewDragHelper = mSensitivitySet 359b7f9224b1495db47eb8fd813b5912250e900770aChris Banes ? ViewDragHelper.create(parent, mSensitivity, mDragCallback) 360b7f9224b1495db47eb8fd813b5912250e900770aChris Banes : ViewDragHelper.create(parent, mDragCallback); 361b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 362b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 363b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 364b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private class SettleRunnable implements Runnable { 365b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private final View mView; 366b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private final boolean mDismiss; 367b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 368b7f9224b1495db47eb8fd813b5912250e900770aChris Banes SettleRunnable(View view, boolean dismiss) { 369b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mView = view; 370b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mDismiss = dismiss; 371b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 372b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 373b7f9224b1495db47eb8fd813b5912250e900770aChris Banes @Override 374b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public void run() { 375b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mViewDragHelper != null && mViewDragHelper.continueSettling(true)) { 376b7f9224b1495db47eb8fd813b5912250e900770aChris Banes ViewCompat.postOnAnimation(mView, this); 377b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } else { 378b7f9224b1495db47eb8fd813b5912250e900770aChris Banes if (mDismiss && mListener != null) { 379b7f9224b1495db47eb8fd813b5912250e900770aChris Banes mListener.onDismiss(mView); 380b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 381b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 382b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 383b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 384b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 385b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private static float clamp(float min, float value, float max) { 386b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return Math.min(Math.max(min, value), max); 387b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 388b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 389b7f9224b1495db47eb8fd813b5912250e900770aChris Banes private static int clamp(int min, int value, int max) { 390b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return Math.min(Math.max(min, value), max); 391b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 392b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 393b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 394b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * Retrieve the current drag state of this behavior. This will return one of 395b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * {@link #STATE_IDLE}, {@link #STATE_DRAGGING} or {@link #STATE_SETTLING}. 396b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * 397b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * @return The current drag state 398b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 399b7f9224b1495db47eb8fd813b5912250e900770aChris Banes public int getDragState() { 400b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return mViewDragHelper != null ? mViewDragHelper.getViewDragState() : STATE_IDLE; 401b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 402b7f9224b1495db47eb8fd813b5912250e900770aChris Banes 403b7f9224b1495db47eb8fd813b5912250e900770aChris Banes /** 404b7f9224b1495db47eb8fd813b5912250e900770aChris Banes * The fraction that {@code value} is between {@code startValue} and {@code endValue}. 405b7f9224b1495db47eb8fd813b5912250e900770aChris Banes */ 406b7f9224b1495db47eb8fd813b5912250e900770aChris Banes static float fraction(float startValue, float endValue, float value) { 407b7f9224b1495db47eb8fd813b5912250e900770aChris Banes return (value - startValue) / (endValue - startValue); 408b7f9224b1495db47eb8fd813b5912250e900770aChris Banes } 409b7f9224b1495db47eb8fd813b5912250e900770aChris Banes}