1e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar/* 2e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Copyright (C) 2015 The Android Open Source Project 3e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 4e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License"); 5e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * you may not use this file except in compliance with the License. 6e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * You may obtain a copy of the License at 7e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 8e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * http://www.apache.org/licenses/LICENSE-2.0 9e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 10e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Unless required by applicable law or agreed to in writing, software 11e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS, 12e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * See the License for the specific language governing permissions and 14e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * limitations under the License. 15e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 16e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 17e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarpackage android.support.v7.widget.helper; 18e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 19e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.graphics.Canvas; 20e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.graphics.Rect; 21e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.os.Build; 22e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v4.animation.ValueAnimatorCompat; 23e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v4.animation.AnimatorCompatHelper; 24e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v4.animation.AnimatorListenerCompat; 25e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v4.animation.AnimatorUpdateListenerCompat; 26e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v4.view.GestureDetectorCompat; 27e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v4.view.MotionEventCompat; 28e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v4.view.VelocityTrackerCompat; 29e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v4.view.ViewCompat; 30e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v7.widget.LinearLayoutManager; 31e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v7.widget.RecyclerView; 32e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.util.Log; 33e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.GestureDetector; 34e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.HapticFeedbackConstants; 35e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.MotionEvent; 36e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.VelocityTracker; 37e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.View; 38e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.ViewConfiguration; 39e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.ViewParent; 40e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v7.recyclerview.R; 41e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 42e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport java.util.ArrayList; 43e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport java.util.List; 44e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 45e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v7.widget.RecyclerView.OnItemTouchListener; 46e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.support.v7.widget.RecyclerView.ViewHolder; 47e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarimport android.view.animation.Interpolator; 48e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 49e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar/** 50e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This is a utility class to add swipe to dismiss and drag & drop support to RecyclerView. 51e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 52e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * It works with a RecyclerView and a Callback class, which configures what type of interactions 53e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * are enabled and also receives events when user performs these actions. 54e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 55e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Depending on which functionality you support, you should override 56e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder)} and / or 57e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link Callback#onSwiped(ViewHolder, int)}. 58e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 59e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This class is designed to work with any LayoutManager but for certain situations, it can be 60e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * optimized for your custom LayoutManager by extending methods in the 61e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link ItemTouchHelper.Callback} class or implementing {@link ItemTouchHelper.ViewDropHandler} 62e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * interface in your LayoutManager. 63e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 64e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * By default, ItemTouchHelper moves the items' translateX/Y properties to reposition them. On 65e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * platforms older than Honeycomb, ItemTouchHelper uses canvas translations and View's visibility 66e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * property to move items in response to touch events. You can customize these behaviors by 67e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * overriding {@link Callback#onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int, 68e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * boolean)} 69e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * or {@link Callback#onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, 70e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * boolean)}. 71e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p/> 72e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Most of the time, you only need to override <code>onChildDraw</code> but due to limitations of 73e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * platform prior to Honeycomb, you may need to implement <code>onChildDrawOver</code> as well. 74e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 75e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyarpublic class ItemTouchHelper extends RecyclerView.ItemDecoration 76e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar implements RecyclerView.OnChildAttachStateChangeListener { 77e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 78e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 79e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Up direction, used for swipe & drag control. 80e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 81e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int UP = 1; 82e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 83e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 84e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Down direction, used for swipe & drag control. 85e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 86e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int DOWN = 1 << 1; 87e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 88e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 89e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Left direction, used for swipe & drag control. 90e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 91e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int LEFT = 1 << 2; 92e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 93e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 94e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Right direction, used for swipe & drag control. 95e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 96e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int RIGHT = 1 << 3; 97e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 98e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // If you change these relative direction values, update Callback#convertToAbsoluteDirection, 99e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // Callback#convertToRelativeDirection. 100e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 101e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Horizontal start direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout 102e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * direction. Used for swipe & drag control. 103e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 104e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int START = LEFT << 2; 105e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 106e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 107e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Horizontal end direction. Resolved to LEFT or RIGHT depending on RecyclerView's layout 108e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * direction. Used for swipe & drag control. 109e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 110e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int END = RIGHT << 2; 111e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 112e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 113e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper is in idle state. At this state, either there is no related motion event by 114e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * the user or latest motion events have not yet triggered a swipe or drag. 115e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 116e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int ACTION_STATE_IDLE = 0; 117e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 118e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 119e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * A View is currently being swiped. 120e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 121e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int ACTION_STATE_SWIPE = 1; 122e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 123e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 124e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * A View is currently being dragged. 125e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 126e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int ACTION_STATE_DRAG = 2; 127e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 128e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 129e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Animation type for views which are swiped successfully. 130e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 131e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int ANIMATION_TYPE_SWIPE_SUCCESS = 1 << 1; 132e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 133e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 134e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Animation type for views which are not completely swiped thus will animate back to their 135e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * original position. 136e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 137e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int ANIMATION_TYPE_SWIPE_CANCEL = 1 << 2; 138e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 139e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 140e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Animation type for views that were dragged and now will animate to their final position. 141e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 142e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int ANIMATION_TYPE_DRAG = 1 << 3; 143e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 144e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final String TAG = "ItemTouchHelper"; 145e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 146e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final boolean DEBUG = false; 147e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 148e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final int ACTIVE_POINTER_ID_NONE = -1; 149e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 150e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final int DIRECTION_FLAG_COUNT = 8; 151e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 152e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final int ACTION_MODE_IDLE_MASK = (1 << DIRECTION_FLAG_COUNT) - 1; 153e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 154e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final int ACTION_MODE_SWIPE_MASK = ACTION_MODE_IDLE_MASK << DIRECTION_FLAG_COUNT; 155e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 156e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final int ACTION_MODE_DRAG_MASK = ACTION_MODE_SWIPE_MASK << DIRECTION_FLAG_COUNT; 157e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 158e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 159e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Views, whose state should be cleared after they are detached from RecyclerView. 160e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This is necessary after swipe dismissing an item. We wait until animator finishes its job 161e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * to clean these views. 162e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 163e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final List<View> mPendingCleanup = new ArrayList<View>(); 164e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 165e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 166e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Re-use array to calculate dx dy for a ViewHolder 167e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 168e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private final float[] mTmpPosition = new float[2]; 169e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 170e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 171e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Currently selected view holder 172e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 173e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder mSelected = null; 174e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 175e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 176e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * The reference coordinates for the action start. For drag & drop, this is the time long 177e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * press is completed vs for swipe, this is the initial touch point. 178e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 179e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float mInitialTouchX; 180e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 181e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float mInitialTouchY; 182e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 183e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 184e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * The diff between the last event and initial touch. 185e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 186e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float mDx; 187e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 188e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float mDy; 189e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 190e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 191e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * The coordinates of the selected view at the time it is selected. We record these values 192e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * when action starts so that we can consistently position it even if LayoutManager moves the 193e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * View. 194e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 195e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float mSelectedStartX; 196e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 197e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float mSelectedStartY; 198e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 199e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 200e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * The pointer we are tracking. 201e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 202e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int mActivePointerId = ACTIVE_POINTER_ID_NONE; 203e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 204e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 205e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Developer callback which controls the behavior of ItemTouchHelper. 206e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 207e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Callback mCallback; 208e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 209e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 210e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Current mode. 211e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 212e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int mActionState = ACTION_STATE_IDLE; 213e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 214e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 215e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * The direction flags obtained from unmasking 216e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link Callback#getAbsoluteMovementFlags(RecyclerView, ViewHolder)} for the current 217e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * action state. 218e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 219e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int mSelectedFlags; 220e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 221e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 222e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * When a View is dragged or swiped and needs to go back to where it was, we create a Recover 223e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Animation and animate it to its location using this custom Animator, instead of using 224e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * framework Animators. 225e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Using framework animators has the side effect of clashing with ItemAnimator, creating 226e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * jumpy UIs. 227e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 228e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar List<RecoverAnimation> mRecoverAnimations = new ArrayList<RecoverAnimation>(); 229e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 230e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int mSlop; 231e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 232e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private RecyclerView mRecyclerView; 233e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 234e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 235e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * When user drags a view to the edge, we start scrolling the LayoutManager as long as View 236e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * is partially out of bounds. 237e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 238e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private final Runnable mScrollRunnable = new Runnable() { 239e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 240e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void run() { 241e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected != null && scrollIfNecessary()) { 242e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected != null) { //it might be lost during scrolling 243e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar moveIfNecessary(mSelected); 244e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 245e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.removeCallbacks(mScrollRunnable); 246e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewCompat.postOnAnimation(mRecyclerView, this); 247e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 248e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 249e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }; 250e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 251e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 252e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Used for detecting fling swipe 253e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 254e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private VelocityTracker mVelocityTracker; 255e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 256e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar //re-used list for selecting a swap target 257e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private List<ViewHolder> mSwapTargets; 258e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 259e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar //re used for for sorting swap targets 260e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private List<Integer> mDistances; 261e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 262e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 263e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If drag & drop is supported, we use child drawing order to bring them to front. 264e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 265e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private RecyclerView.ChildDrawingOrderCallback mChildDrawingOrderCallback = null; 266e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 267e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 268e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This keeps a reference to the child dragged by the user. Even after user stops dragging, 269e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * until view reaches its final position (end of recover animation), we keep a reference so 270e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * that it can be drawn above other children. 271e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 272e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private View mOverdrawChild = null; 273e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 274e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 275e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * We cache the position of the overdraw child to avoid recalculating it each time child 276e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * position callback is called. This value is invalidated whenever a child is attached or 277e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * detached. 278e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 279e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int mOverdrawChildPosition = -1; 280e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 281e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 282e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Used to detect long press. 283e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 284e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private GestureDetectorCompat mGestureDetector; 285e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 286e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private final OnItemTouchListener mOnItemTouchListener 287e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar = new OnItemTouchListener() { 288e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 289e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public boolean onInterceptTouchEvent(RecyclerView recyclerView, MotionEvent event) { 290e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mGestureDetector.onTouchEvent(event); 291e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (DEBUG) { 292e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Log.d(TAG, "intercept: x:" + event.getX() + ",y:" + event.getY() + ", " + event); 293e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 294e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int action = MotionEventCompat.getActionMasked(event); 295e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (action == MotionEvent.ACTION_DOWN) { 296e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mActivePointerId = MotionEventCompat.getPointerId(event, 0); 297e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mInitialTouchX = event.getX(); 298e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mInitialTouchY = event.getY(); 299e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar obtainVelocityTracker(); 300e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected == null) { 301e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecoverAnimation animation = findAnimation(event); 302e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (animation != null) { 303e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mInitialTouchX -= animation.mX; 304e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mInitialTouchY -= animation.mY; 305e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar endRecoverAnimation(animation.mViewHolder, true); 306e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mPendingCleanup.remove(animation.mViewHolder.itemView)) { 307e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.clearView(mRecyclerView, animation.mViewHolder); 308e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 309e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(animation.mViewHolder, animation.mActionState); 310e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar updateDxDy(event, mSelectedFlags, 0); 311e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 312e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 313e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) { 314e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mActivePointerId = ACTIVE_POINTER_ID_NONE; 315e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(null, ACTION_STATE_IDLE); 316e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else if (mActivePointerId != ACTIVE_POINTER_ID_NONE) { 317e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // in a non scroll orientation, if distance change is above threshold, we 318e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // can select the item 319e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int index = MotionEventCompat.findPointerIndex(event, mActivePointerId); 320e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (DEBUG) { 321e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Log.d(TAG, "pointer index " + index); 322e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 323e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (index >= 0) { 324e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar checkSelectForSwipe(action, event, index); 325e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 326e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 327e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mVelocityTracker != null) { 328e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mVelocityTracker.addMovement(event); 329e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 330e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return mSelected != null; 331e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 332e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 333e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 334e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onTouchEvent(RecyclerView recyclerView, MotionEvent event) { 335e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mGestureDetector.onTouchEvent(event); 336e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (DEBUG) { 337e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Log.d(TAG, 338e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar "on touch: x:" + mInitialTouchX + ",y:" + mInitialTouchY + ", :" + event); 339e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 340e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mVelocityTracker != null) { 341e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mVelocityTracker.addMovement(event); 342e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 343e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mActivePointerId == ACTIVE_POINTER_ID_NONE) { 344e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 345e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 346e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int action = MotionEventCompat.getActionMasked(event); 347e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int activePointerIndex = MotionEventCompat 348e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .findPointerIndex(event, mActivePointerId); 349e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (activePointerIndex >= 0) { 350e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar checkSelectForSwipe(action, event, activePointerIndex); 351e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 352e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder viewHolder = mSelected; 353e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (viewHolder == null) { 354e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 355e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 356e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar switch (action) { 357e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case MotionEvent.ACTION_MOVE: { 358e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // Find the index of the active pointer and fetch its position 359e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (activePointerIndex >= 0) { 360e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar updateDxDy(event, mSelectedFlags, activePointerIndex); 361e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar moveIfNecessary(viewHolder); 362e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.removeCallbacks(mScrollRunnable); 363e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mScrollRunnable.run(); 364e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.invalidate(); 365e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 366e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar break; 367e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 368e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case MotionEvent.ACTION_CANCEL: 369e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case MotionEvent.ACTION_UP: 370e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mVelocityTracker != null) { 371e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mVelocityTracker 372e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .computeCurrentVelocity(1000, mRecyclerView.getMaxFlingVelocity()); 373e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 374e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(null, ACTION_STATE_IDLE); 375e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mActivePointerId = ACTIVE_POINTER_ID_NONE; 376e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar break; 377e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case MotionEvent.ACTION_POINTER_UP: { 378e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int pointerIndex = MotionEventCompat.getActionIndex(event); 379e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int pointerId = MotionEventCompat.getPointerId(event, pointerIndex); 380e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (pointerId == mActivePointerId) { 381e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mVelocityTracker != null) { 382e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mVelocityTracker 383e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .computeCurrentVelocity(1000, 384e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.getMaxFlingVelocity()); 385e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 386e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // This was our active pointer going up. Choose a new 387e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // active pointer and adjust accordingly. 388e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 389e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mActivePointerId = MotionEventCompat.getPointerId(event, newPointerIndex); 390e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar updateDxDy(event, mSelectedFlags, pointerIndex); 391e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 392e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar break; 393e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 394e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 395e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 396e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 397e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 398e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept) { 399e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (!disallowIntercept) { 400e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 401e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 402e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(null, ACTION_STATE_IDLE); 403e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 404e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }; 405e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 406e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 407e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Temporary rect instance that is used when we need to lookup Item decorations. 408e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 409e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private Rect mTmpRect; 410e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 411e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 412e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * When user started to drag scroll. Reset when we don't scroll 413e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 414e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private long mDragScrollStartTimeInMs; 415e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 416e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 417e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Creates an ItemTouchHelper that will work with the given Callback. 418e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 419e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * You can attach ItemTouchHelper to a RecyclerView via 420e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #attachToRecyclerView(RecyclerView)}. Upon attaching, it will add an item decoration, 421e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * an onItemTouchListener and a Child attach / detach listener to the RecyclerView. 422e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 423e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param callback The Callback which controls the behavior of this touch helper. 424e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 425e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public ItemTouchHelper(Callback callback) { 426e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback = callback; 427e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 428e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 429e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static boolean hitTest(View child, float x, float y, float left, float top) { 430e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return x >= left && 431e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar x <= left + child.getWidth() && 432e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar y >= top && 433e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar y <= top + child.getHeight(); 434e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 435e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 436e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 437e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Attaches the ItemTouchHelper to the provided RecyclerView. If TouchHelper is already 438e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * attached 439e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * to a RecyclerView, it will first detach from the previous one. 440e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 441e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView instance to which you want to add this helper. 442e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 443e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void attachToRecyclerView(RecyclerView recyclerView) { 444e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mRecyclerView == recyclerView) { 445e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; // nothing to do 446e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 447e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mRecyclerView != null) { 448e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar destroyCallbacks(); 449e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 450e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView = recyclerView; 451e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mRecyclerView != null) { 452e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar setupCallbacks(); 453e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 454e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 455e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 456e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void setupCallbacks() { 457e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewConfiguration vc = ViewConfiguration.get(mRecyclerView.getContext()); 458e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSlop = vc.getScaledTouchSlop(); 459e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.addItemDecoration(this); 460e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.addOnItemTouchListener(mOnItemTouchListener); 461e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.addOnChildAttachStateChangeListener(this); 462e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar initGestureDetector(); 463e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 464e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 465e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void destroyCallbacks() { 466e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.removeItemDecoration(this); 467e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.removeOnItemTouchListener(mOnItemTouchListener); 468e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.removeOnChildAttachStateChangeListener(this); 469e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // clean all attached 470e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int recoverAnimSize = mRecoverAnimations.size(); 471e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = recoverAnimSize - 1; i >= 0; i--) { 472e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecoverAnimation recoverAnimation = mRecoverAnimations.get(0); 473e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.clearView(mRecyclerView, recoverAnimation.mViewHolder); 474e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 475e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecoverAnimations.clear(); 476e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mOverdrawChild = null; 477e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mOverdrawChildPosition = -1; 478e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar releaseVelocityTracker(); 479e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 480e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 481e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void initGestureDetector() { 482e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mGestureDetector != null) { 483e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 484e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 485e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mGestureDetector = new GestureDetectorCompat(mRecyclerView.getContext(), 486e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar new ItemTouchHelperGestureListener()); 487e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 488e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 489e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void getSelectedDxDy(float[] outPosition) { 490e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((mSelectedFlags & (LEFT | RIGHT)) != 0) { 491e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft(); 492e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 493e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar outPosition[0] = ViewCompat.getTranslationX(mSelected.itemView); 494e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 495e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((mSelectedFlags & (UP | DOWN)) != 0) { 496e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop(); 497e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 498e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar outPosition[1] = ViewCompat.getTranslationY(mSelected.itemView); 499e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 500e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 501e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 502e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 503e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) { 504e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float dx = 0, dy = 0; 505e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected != null) { 506e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getSelectedDxDy(mTmpPosition); 507e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar dx = mTmpPosition[0]; 508e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar dy = mTmpPosition[1]; 509e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 510e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.onDrawOver(c, parent, mSelected, 511e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecoverAnimations, mActionState, dx, dy); 512e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 513e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 514e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 515e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) { 516e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // we don't know if RV changed something so we should invalidate this index. 517e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mOverdrawChildPosition = -1; 518e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float dx = 0, dy = 0; 519e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected != null) { 520e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getSelectedDxDy(mTmpPosition); 521e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar dx = mTmpPosition[0]; 522e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar dy = mTmpPosition[1]; 523e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 524e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.onDraw(c, parent, mSelected, 525e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecoverAnimations, mActionState, dx, dy); 526e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 527e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 528e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 529e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Starts dragging or swiping the given View. Call with null if you want to clear it. 530e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 531e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param selected The ViewHolder to drag or swipe. Can be null if you want to cancel the 532e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * current action 533e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param actionState The type of action 534e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 535e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void select(ViewHolder selected, int actionState) { 536e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (selected == mSelected && actionState == mActionState) { 537e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 538e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 539e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDragScrollStartTimeInMs = Long.MIN_VALUE; 540e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int prevActionState = mActionState; 541e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // prevent duplicate animations 542e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar endRecoverAnimation(selected, true); 543e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mActionState = actionState; 544e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (actionState == ACTION_STATE_DRAG) { 545e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // we remove after animation is complete. this means we only elevate the last drag 546e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // child but that should perform good enough as it is very hard to start dragging a 547e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // new child before the previous one settles. 548e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mOverdrawChild = selected.itemView; 549e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar addChildDrawingOrderCallback(); 550e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 551e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int actionStateMask = (1 << (DIRECTION_FLAG_COUNT + DIRECTION_FLAG_COUNT * actionState)) 552e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar - 1; 553e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar boolean preventLayout = false; 554e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 555e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected != null) { 556e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ViewHolder prevSelected = mSelected; 557e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (prevSelected.itemView.getParent() != null) { 558e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int swipeDir = prevActionState == ACTION_STATE_DRAG ? 0 559e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar : swipeIfNecessary(prevSelected); 560e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar releaseVelocityTracker(); 561e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // find where we should animate to 562e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float targetTranslateX, targetTranslateY; 563e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int animationType; 564e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar switch (swipeDir) { 565e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case LEFT: 566e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case RIGHT: 567e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case START: 568e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case END: 569e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar targetTranslateY = 0; 570e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar targetTranslateX = Math.signum(mDx) * mRecyclerView.getWidth(); 571e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar break; 572e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case UP: 573e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar case DOWN: 574e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar targetTranslateX = 0; 575e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar targetTranslateY = Math.signum(mDy) * mRecyclerView.getHeight(); 576e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar break; 577e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar default: 578e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar targetTranslateX = 0; 579e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar targetTranslateY = 0; 580e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 581e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (prevActionState == ACTION_STATE_DRAG) { 582e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar animationType = ANIMATION_TYPE_DRAG; 583e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else if (swipeDir > 0) { 584e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar animationType = ANIMATION_TYPE_SWIPE_SUCCESS; 585e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 586e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar animationType = ANIMATION_TYPE_SWIPE_CANCEL; 587e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 588e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getSelectedDxDy(mTmpPosition); 589e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float currentTranslateX = mTmpPosition[0]; 590e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float currentTranslateY = mTmpPosition[1]; 591e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecoverAnimation rv = new RecoverAnimation(prevSelected, animationType, 592e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar prevActionState, currentTranslateX, currentTranslateY, 593e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar targetTranslateX, targetTranslateY) { 594e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 595e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onAnimationEnd(ValueAnimatorCompat animation) { 596e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar super.onAnimationEnd(animation); 597e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (this.mOverridden) { 598e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 599e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 600e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (swipeDir <= 0) { 601e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // this is a drag or failed swipe. recover immediately 602e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.clearView(mRecyclerView, prevSelected); 603e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // full cleanup will happen on onDrawOver 604e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 605e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // wait until remove animation is complete. 606e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mPendingCleanup.add(prevSelected.itemView); 607e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mIsPendingCleanup = true; 608e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (swipeDir > 0) { 609e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // Animation might be ended by other animators during a layout. 610e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // We defer callback to avoid editing adapter during a layout. 611e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar postDispatchSwipe(this, swipeDir); 612e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 613e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 614e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // removed from the list after it is drawn for the last time 615e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mOverdrawChild == prevSelected.itemView) { 616e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView); 617e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 618e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 619e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }; 620e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final long duration = mCallback.getAnimationDuration(mRecyclerView, animationType, 621e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar targetTranslateX - currentTranslateX, targetTranslateY - currentTranslateY); 622e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar rv.setDuration(duration); 623e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecoverAnimations.add(rv); 624e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar rv.start(); 625e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar preventLayout = true; 626e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 627e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar removeChildDrawingOrderCallbackIfNecessary(prevSelected.itemView); 628e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.clearView(mRecyclerView, prevSelected); 629e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 630e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSelected = null; 631e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 632e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (selected != null) { 633e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSelectedFlags = 634e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar (mCallback.getAbsoluteMovementFlags(mRecyclerView, selected) & actionStateMask) 635e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar >> (mActionState * DIRECTION_FLAG_COUNT); 636e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSelectedStartX = selected.itemView.getLeft(); 637e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSelectedStartY = selected.itemView.getTop(); 638e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSelected = selected; 639e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 640e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (actionState == ACTION_STATE_DRAG) { 641e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSelected.itemView.performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 642e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 643e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 644e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ViewParent rvParent = mRecyclerView.getParent(); 645e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (rvParent != null) { 646e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar rvParent.requestDisallowInterceptTouchEvent(mSelected != null); 647e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 648e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (!preventLayout) { 649e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.getLayoutManager().requestSimpleAnimationsInNextLayout(); 650e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 651e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.onSelectedChanged(mSelected, mActionState); 652e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.invalidate(); 653e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 654e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 655e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void postDispatchSwipe(final RecoverAnimation anim, final int swipeDir) { 656e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // wait until animations are complete. 657e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.post(new Runnable() { 658e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 659e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void run() { 660e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mRecyclerView != null && mRecyclerView.isAttachedToWindow() && 661e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar !anim.mOverridden && 662e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar anim.mViewHolder.getAdapterPosition() != RecyclerView.NO_POSITION) { 663e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecyclerView.ItemAnimator animator = mRecyclerView.getItemAnimator(); 664e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // if animator is running or we have other active recover animations, we try 665e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // not to call onSwiped because DefaultItemAnimator is not good at merging 666e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // animations. Instead, we wait and batch. 667e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((animator == null || !animator.isRunning(null)) 668e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar && !hasRunningRecoverAnim()) { 669e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.onSwiped(anim.mViewHolder, swipeDir); 670e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 671e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.post(this); 672e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 673e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 674e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 675e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }); 676e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 677e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 678e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private boolean hasRunningRecoverAnim() { 679e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int size = mRecoverAnimations.size(); 680e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = 0; i < size; i++) { 681e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (!mRecoverAnimations.get(i).mEnded) { 682e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return true; 683e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 684e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 685e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 686e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 687e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 688e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 689e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If user drags the view to the edge, trigger a scroll if necessary. 690e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 691e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private boolean scrollIfNecessary() { 692e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected == null) { 693e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDragScrollStartTimeInMs = Long.MIN_VALUE; 694e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 695e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 696e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final long now = System.currentTimeMillis(); 697e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final long scrollDuration = mDragScrollStartTimeInMs 698e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar == Long.MIN_VALUE ? 0 : now - mDragScrollStartTimeInMs; 699e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager(); 700e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mTmpRect == null) { 701e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mTmpRect = new Rect(); 702e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 703e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int scrollX = 0; 704e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int scrollY = 0; 705e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar lm.calculateItemDecorationsForChild(mSelected.itemView, mTmpRect); 706e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (lm.canScrollHorizontally()) { 707e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int curX = (int) (mSelectedStartX + mDx); 708e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int leftDiff = curX - mTmpRect.left - mRecyclerView.getPaddingLeft(); 709e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mDx < 0 && leftDiff < 0) { 710e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollX = leftDiff; 711e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else if (mDx > 0) { 712e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int rightDiff = 713e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar curX + mSelected.itemView.getWidth() + mTmpRect.right 714e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar - (mRecyclerView.getWidth() - mRecyclerView.getPaddingRight()); 715e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (rightDiff > 0) { 716e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollX = rightDiff; 717e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 718e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 719e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 720e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (lm.canScrollVertically()) { 721e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int curY = (int) (mSelectedStartY + mDy); 722e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int topDiff = curY - mTmpRect.top - mRecyclerView.getPaddingTop(); 723e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mDy < 0 && topDiff < 0) { 724e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollY = topDiff; 725e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else if (mDy > 0) { 726e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int bottomDiff = curY + mSelected.itemView.getHeight() + mTmpRect.bottom - 727e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar (mRecyclerView.getHeight() - mRecyclerView.getPaddingBottom()); 728e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (bottomDiff > 0) { 729e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollY = bottomDiff; 730e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 731e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 732e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 733e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (scrollX != 0) { 734e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollX = mCallback.interpolateOutOfBoundsScroll(mRecyclerView, 735e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSelected.itemView.getWidth(), scrollX, 736e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.getWidth(), scrollDuration); 737e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 738e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (scrollY != 0) { 739e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar scrollY = mCallback.interpolateOutOfBoundsScroll(mRecyclerView, 740e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSelected.itemView.getHeight(), scrollY, 741e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.getHeight(), scrollDuration); 742e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 743e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (scrollX != 0 || scrollY != 0) { 744e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mDragScrollStartTimeInMs == Long.MIN_VALUE) { 745e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDragScrollStartTimeInMs = now; 746e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 747e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.scrollBy(scrollX, scrollY); 748e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return true; 749e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 750e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDragScrollStartTimeInMs = Long.MIN_VALUE; 751e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 752e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 753e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 754e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private List<ViewHolder> findSwapTargets(ViewHolder viewHolder) { 755e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSwapTargets == null) { 756e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSwapTargets = new ArrayList<ViewHolder>(); 757e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDistances = new ArrayList<Integer>(); 758e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 759e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSwapTargets.clear(); 760e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDistances.clear(); 761e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 762e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int margin = mCallback.getBoundingBoxMargin(); 763e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int left = Math.round(mSelectedStartX + mDx) - margin; 764e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int top = Math.round(mSelectedStartY + mDy) - margin; 765e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int right = left + viewHolder.itemView.getWidth() + 2 * margin; 766e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int bottom = top + viewHolder.itemView.getHeight() + 2 * margin; 767e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int centerX = (left + right) / 2; 768e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int centerY = (top + bottom) / 2; 769e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager(); 770e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int childCount = lm.getChildCount(); 771e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = 0; i < childCount; i++) { 772e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar View other = lm.getChildAt(i); 773e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (other == viewHolder.itemView) { 774e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar continue;//myself! 775e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 776e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (other.getBottom() < top || other.getTop() > bottom 777e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar || other.getRight() < left || other.getLeft() > right) { 778e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar continue; 779e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 780e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ViewHolder otherVh = mRecyclerView.getChildViewHolder(other); 781e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mCallback.canDropOver(mRecyclerView, mSelected, otherVh)) { 782e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // find the index to add 783e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int dx = Math.abs(centerX - (other.getLeft() + other.getRight()) / 2); 784e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int dy = Math.abs(centerY - (other.getTop() + other.getBottom()) / 2); 785e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int dist = dx * dx + dy * dy; 786e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 787e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int pos = 0; 788e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int cnt = mSwapTargets.size(); 789e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int j = 0; j < cnt; j++) { 790e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dist > mDistances.get(j)) { 791e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar pos++; 792e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 793e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar break; 794e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 795e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 796e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSwapTargets.add(pos, otherVh); 797e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDistances.add(pos, dist); 798e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 799e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 800e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return mSwapTargets; 801e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 802e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 803e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 804e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Checks if we should swap w/ another view holder. 805e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 806e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void moveIfNecessary(ViewHolder viewHolder) { 807e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mRecyclerView.isLayoutRequested()) { 808e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 809e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 810e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mActionState != ACTION_STATE_DRAG) { 811e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 812e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 813e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 814e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float threshold = mCallback.getMoveThreshold(viewHolder); 815e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int x = (int) (mSelectedStartX + mDx); 816e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int y = (int) (mSelectedStartY + mDy); 817e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (Math.abs(y - viewHolder.itemView.getTop()) < viewHolder.itemView.getHeight() * threshold 818e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar && Math.abs(x - viewHolder.itemView.getLeft()) 819e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar < viewHolder.itemView.getWidth() * threshold) { 820e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 821e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 822e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar List<ViewHolder> swapTargets = findSwapTargets(viewHolder); 823e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (swapTargets.size() == 0) { 824e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 825e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 826e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // may swap. 827e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder target = mCallback.chooseDropTarget(viewHolder, swapTargets, x, y); 828e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (target == null) { 829e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mSwapTargets.clear(); 830e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDistances.clear(); 831e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 832e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 833e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int toPosition = target.getAdapterPosition(); 834e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int fromPosition = viewHolder.getAdapterPosition(); 835e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mCallback.onMove(mRecyclerView, viewHolder, target)) { 836e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // keep target visible 837e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.onMoved(mRecyclerView, viewHolder, fromPosition, 838e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar target, toPosition, x, y); 839e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 840e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 841e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 842e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 843e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onChildViewAttachedToWindow(View view) { 844e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 845e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 846e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 847e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onChildViewDetachedFromWindow(View view) { 848e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar removeChildDrawingOrderCallbackIfNecessary(view); 849e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ViewHolder holder = mRecyclerView.getChildViewHolder(view); 850e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (holder == null) { 851e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 852e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 853e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected != null && holder == mSelected) { 854e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(null, ACTION_STATE_IDLE); 855e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 856e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar endRecoverAnimation(holder, false); // this may push it into pending cleanup list. 857e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mPendingCleanup.remove(holder.itemView)) { 858e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCallback.clearView(mRecyclerView, holder); 859e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 860e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 861e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 862e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 863e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 864e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Returns the animation type or 0 if cannot be found. 865e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 866e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int endRecoverAnimation(ViewHolder viewHolder, boolean override) { 867e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int recoverAnimSize = mRecoverAnimations.size(); 868e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = recoverAnimSize - 1; i >= 0; i--) { 869e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecoverAnimation anim = mRecoverAnimations.get(i); 870e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (anim.mViewHolder == viewHolder) { 871e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar anim.mOverridden |= override; 872e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (!anim.mEnded) { 873e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar anim.cancel(); 874e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 875e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecoverAnimations.remove(i); 876e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar anim.mViewHolder.setIsRecyclable(true); 877e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return anim.mAnimationType; 878e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 879e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 880e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return 0; 881e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 882e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 883e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 884e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void getItemOffsets(Rect outRect, View view, RecyclerView parent, 885e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar RecyclerView.State state) { 886e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar outRect.setEmpty(); 887e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 888e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 889e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void obtainVelocityTracker() { 890e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mVelocityTracker != null) { 891e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mVelocityTracker.recycle(); 892e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 893e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mVelocityTracker = VelocityTracker.obtain(); 894e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 895e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 896e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void releaseVelocityTracker() { 897e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mVelocityTracker != null) { 898e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mVelocityTracker.recycle(); 899e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mVelocityTracker = null; 900e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 901e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 902e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 903e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private ViewHolder findSwipedView(MotionEvent motionEvent) { 904e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecyclerView.LayoutManager lm = mRecyclerView.getLayoutManager(); 905e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mActivePointerId == ACTIVE_POINTER_ID_NONE) { 906e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return null; 907e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 908e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int pointerIndex = MotionEventCompat.findPointerIndex(motionEvent, mActivePointerId); 909e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float dx = MotionEventCompat.getX(motionEvent, pointerIndex) - mInitialTouchX; 910e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float dy = MotionEventCompat.getY(motionEvent, pointerIndex) - mInitialTouchY; 911e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float absDx = Math.abs(dx); 912e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float absDy = Math.abs(dy); 913e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 914e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (absDx < mSlop && absDy < mSlop) { 915e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return null; 916e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 917e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (absDx > absDy && lm.canScrollHorizontally()) { 918e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return null; 919e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else if (absDy > absDx && lm.canScrollVertically()) { 920e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return null; 921e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 922e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar View child = findChildView(motionEvent); 923e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (child == null) { 924e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return null; 925e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 926e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return mRecyclerView.getChildViewHolder(child); 927e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 928e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 929e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 930e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Checks whether we should select a View for swiping. 931e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 932e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private boolean checkSelectForSwipe(int action, MotionEvent motionEvent, int pointerIndex) { 933e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected != null || action != MotionEvent.ACTION_MOVE 934e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar || mActionState == ACTION_STATE_DRAG || !mCallback.isItemViewSwipeEnabled()) { 935e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 936e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 937e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mRecyclerView.getScrollState() == RecyclerView.SCROLL_STATE_DRAGGING) { 938e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 939e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 940e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ViewHolder vh = findSwipedView(motionEvent); 941e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (vh == null) { 942e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 943e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 944e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int movementFlags = mCallback.getAbsoluteMovementFlags(mRecyclerView, vh); 945e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 946e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int swipeFlags = (movementFlags & ACTION_MODE_SWIPE_MASK) 947e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar >> (DIRECTION_FLAG_COUNT * ACTION_STATE_SWIPE); 948e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 949e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (swipeFlags == 0) { 950e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 951e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 952e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 953e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // mDx and mDy are only set in allowed directions. We use custom x/y here instead of 954e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // updateDxDy to avoid swiping if user moves more in the other direction 955e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float x = MotionEventCompat.getX(motionEvent, pointerIndex); 956e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float y = MotionEventCompat.getY(motionEvent, pointerIndex); 957e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 958e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // Calculate the distance moved 959e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float dx = x - mInitialTouchX; 960e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float dy = y - mInitialTouchY; 961e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // swipe target is chose w/o applying flags so it does not really check if swiping in that 962e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // direction is allowed. This why here, we use mDx mDy to check slope value again. 963e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float absDx = Math.abs(dx); 964e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float absDy = Math.abs(dy); 965e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 966e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (absDx < mSlop && absDy < mSlop) { 967e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 968e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 969e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (absDx > absDy) { 970e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dx < 0 && (swipeFlags & LEFT) == 0) { 971e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 972e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 973e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dx > 0 && (swipeFlags & RIGHT) == 0) { 974e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 975e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 976e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 977e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dy < 0 && (swipeFlags & UP) == 0) { 978e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 979e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 980e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dy > 0 && (swipeFlags & DOWN) == 0) { 981e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return false; 982e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 983e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 984e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDx = mDy = 0f; 985e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mActivePointerId = MotionEventCompat.getPointerId(motionEvent, 0); 986e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(vh, ACTION_STATE_SWIPE); 987e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return true; 988e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 989e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 990e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private View findChildView(MotionEvent event) { 991e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // first check elevated views, if none, then call RV 992e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float x = event.getX(); 993e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float y = event.getY(); 994e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mSelected != null) { 995e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final View selectedView = mSelected.itemView; 996e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (hitTest(selectedView, x, y, mSelectedStartX + mDx, mSelectedStartY + mDy)) { 997e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return selectedView; 998e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 999e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1000e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) { 1001e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecoverAnimation anim = mRecoverAnimations.get(i); 1002e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final View view = anim.mViewHolder.itemView; 1003e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (hitTest(view, x, y, anim.mX, anim.mY)) { 1004e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return view; 1005e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1006e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1007e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return mRecyclerView.findChildViewUnder(x, y); 1008e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1009e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1010e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1011e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Starts dragging the provided ViewHolder. By default, ItemTouchHelper starts a drag when a 1012e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * View is long pressed. You can disable that behavior via 1013e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link ItemTouchHelper.Callback#isLongPressDragEnabled()}. 1014e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1015e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * For this method to work: 1016e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <ul> 1017e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <li>The provided ViewHolder must be a child of the RecyclerView to which this 1018e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper 1019e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * is attached.</li> 1020e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <li>{@link ItemTouchHelper.Callback} must have dragging enabled.</li> 1021e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <li>There must be a previous touch event that was reported to the ItemTouchHelper 1022e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener 1023e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * grabs previous events, this should work as expected.</li> 1024e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * </ul> 1025e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1026e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * For example, if you would like to let your user to be able to drag an Item by touching one 1027e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * of its descendants, you may implement it as follows: 1028e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <pre> 1029e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() { 1030e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * public boolean onTouch(View v, MotionEvent event) { 1031e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { 1032e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * mItemTouchHelper.startDrag(viewHolder); 1033e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * } 1034e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * return false; 1035e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * } 1036e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * }); 1037e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * </pre> 1038e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1039e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1040e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder to start dragging. It must be a direct child of 1041e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * RecyclerView. 1042e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see ItemTouchHelper.Callback#isItemViewSwipeEnabled() 1043e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1044e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void startDrag(ViewHolder viewHolder) { 1045e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (!mCallback.hasDragFlag(mRecyclerView, viewHolder)) { 1046e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Log.e(TAG, "Start drag has been called but swiping is not enabled"); 1047e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 1048e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1049e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (viewHolder.itemView.getParent() != mRecyclerView) { 1050e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Log.e(TAG, "Start drag has been called with a view holder which is not a child of " 1051e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar + "the RecyclerView which is controlled by this ItemTouchHelper."); 1052e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 1053e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1054e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar obtainVelocityTracker(); 1055e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDx = mDy = 0f; 1056e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(viewHolder, ACTION_STATE_DRAG); 1057e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1058e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1059e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1060e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Starts swiping the provided ViewHolder. By default, ItemTouchHelper starts swiping a View 1061e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * when user swipes their finger (or mouse pointer) over the View. You can disable this 1062e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * behavior 1063e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * by overriding {@link ItemTouchHelper.Callback} 1064e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1065e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * For this method to work: 1066e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <ul> 1067e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <li>The provided ViewHolder must be a child of the RecyclerView to which this 1068e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper is attached.</li> 1069e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <li>{@link ItemTouchHelper.Callback} must have swiping enabled.</li> 1070e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <li>There must be a previous touch event that was reported to the ItemTouchHelper 1071e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * through RecyclerView's ItemTouchListener mechanism. As long as no other ItemTouchListener 1072e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * grabs previous events, this should work as expected.</li> 1073e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * </ul> 1074e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1075e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * For example, if you would like to let your user to be able to swipe an Item by touching one 1076e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * of its descendants, you may implement it as follows: 1077e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <pre> 1078e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * viewHolder.dragButton.setOnTouchListener(new View.OnTouchListener() { 1079e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * public boolean onTouch(View v, MotionEvent event) { 1080e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * if (MotionEventCompat.getActionMasked(event) == MotionEvent.ACTION_DOWN) { 1081e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * mItemTouchHelper.startSwipe(viewHolder); 1082e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * } 1083e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * return false; 1084e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * } 1085e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * }); 1086e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * </pre> 1087e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1088e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder to start swiping. It must be a direct child of 1089e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * RecyclerView. 1090e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1091e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void startSwipe(ViewHolder viewHolder) { 1092e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (!mCallback.hasSwipeFlag(mRecyclerView, viewHolder)) { 1093e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Log.e(TAG, "Start swipe has been called but dragging is not enabled"); 1094e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 1095e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1096e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (viewHolder.itemView.getParent() != mRecyclerView) { 1097e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Log.e(TAG, "Start swipe has been called with a view holder which is not a child of " 1098e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar + "the RecyclerView controlled by this ItemTouchHelper."); 1099e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 1100e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1101e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar obtainVelocityTracker(); 1102e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDx = mDy = 0f; 1103e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(viewHolder, ACTION_STATE_SWIPE); 1104e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1105e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1106e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private RecoverAnimation findAnimation(MotionEvent event) { 1107e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mRecoverAnimations.isEmpty()) { 1108e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return null; 1109e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1110e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar View target = findChildView(event); 1111e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = mRecoverAnimations.size() - 1; i >= 0; i--) { 1112e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecoverAnimation anim = mRecoverAnimations.get(i); 1113e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (anim.mViewHolder.itemView == target) { 1114e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return anim; 1115e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1116e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1117e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return null; 1118e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1119e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1120e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void updateDxDy(MotionEvent ev, int directionFlags, int pointerIndex) { 1121e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float x = MotionEventCompat.getX(ev, pointerIndex); 1122e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float y = MotionEventCompat.getY(ev, pointerIndex); 1123e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1124e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // Calculate the distance moved 1125e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDx = x - mInitialTouchX; 1126e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDy = y - mInitialTouchY; 1127e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((directionFlags & LEFT) == 0) { 1128e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDx = Math.max(0, mDx); 1129e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1130e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((directionFlags & RIGHT) == 0) { 1131e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDx = Math.min(0, mDx); 1132e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1133e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((directionFlags & UP) == 0) { 1134e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDy = Math.max(0, mDy); 1135e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1136e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((directionFlags & DOWN) == 0) { 1137e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDy = Math.min(0, mDy); 1138e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1139e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1140e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1141e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int swipeIfNecessary(ViewHolder viewHolder) { 1142e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mActionState == ACTION_STATE_DRAG) { 1143e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return 0; 1144e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1145e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int originalMovementFlags = mCallback.getMovementFlags(mRecyclerView, viewHolder); 1146e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int absoluteMovementFlags = mCallback.convertToAbsoluteDirection( 1147e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar originalMovementFlags, 1148e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewCompat.getLayoutDirection(mRecyclerView)); 1149e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int flags = (absoluteMovementFlags 1150e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT); 1151e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (flags == 0) { 1152e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return 0; 1153e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1154e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int originalFlags = (originalMovementFlags 1155e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar & ACTION_MODE_SWIPE_MASK) >> (ACTION_STATE_SWIPE * DIRECTION_FLAG_COUNT); 1156e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int swipeDir; 1157e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (Math.abs(mDx) > Math.abs(mDy)) { 1158e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) { 1159e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // if swipe dir is not in original flags, it should be the relative direction 1160e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((originalFlags & swipeDir) == 0) { 1161e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // convert to relative 1162e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return Callback.convertToRelativeDirection(swipeDir, 1163e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewCompat.getLayoutDirection(mRecyclerView)); 1164e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1165e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return swipeDir; 1166e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1167e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) { 1168e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return swipeDir; 1169e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1170e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 1171e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((swipeDir = checkVerticalSwipe(viewHolder, flags)) > 0) { 1172e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return swipeDir; 1173e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1174e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((swipeDir = checkHorizontalSwipe(viewHolder, flags)) > 0) { 1175e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // if swipe dir is not in original flags, it should be the relative direction 1176e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((originalFlags & swipeDir) == 0) { 1177e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // convert to relative 1178e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return Callback.convertToRelativeDirection(swipeDir, 1179e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewCompat.getLayoutDirection(mRecyclerView)); 1180e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1181e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return swipeDir; 1182e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1183e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1184e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return 0; 1185e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1186e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1187e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int checkHorizontalSwipe(ViewHolder viewHolder, int flags) { 1188e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((flags & (LEFT | RIGHT)) != 0) { 1189e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int dirFlag = mDx > 0 ? RIGHT : LEFT; 1190e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mVelocityTracker != null && mActivePointerId > -1) { 1191e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float xVelocity = VelocityTrackerCompat 1192e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .getXVelocity(mVelocityTracker, mActivePointerId); 1193e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int velDirFlag = xVelocity > 0f ? RIGHT : LEFT; 1194e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((velDirFlag & flags) != 0 && dirFlag == velDirFlag && 1195e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Math.abs(xVelocity) >= mRecyclerView.getMinFlingVelocity()) { 1196e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return velDirFlag; 1197e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1198e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1199e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1200e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float threshold = mRecyclerView.getWidth() * mCallback 1201e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .getSwipeThreshold(viewHolder); 1202e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1203e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((flags & dirFlag) != 0 && Math.abs(mDx) > threshold) { 1204e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return dirFlag; 1205e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1206e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1207e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return 0; 1208e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1209e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1210e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int checkVerticalSwipe(ViewHolder viewHolder, int flags) { 1211e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((flags & (UP | DOWN)) != 0) { 1212e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int dirFlag = mDy > 0 ? DOWN : UP; 1213e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mVelocityTracker != null && mActivePointerId > -1) { 1214e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float yVelocity = VelocityTrackerCompat 1215e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .getYVelocity(mVelocityTracker, mActivePointerId); 1216e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int velDirFlag = yVelocity > 0f ? DOWN : UP; 1217e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((velDirFlag & flags) != 0 && velDirFlag == dirFlag && 1218e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Math.abs(yVelocity) >= mRecyclerView.getMinFlingVelocity()) { 1219e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return velDirFlag; 1220e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1221e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1222e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1223e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float threshold = mRecyclerView.getHeight() * mCallback 1224e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .getSwipeThreshold(viewHolder); 1225e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if ((flags & dirFlag) != 0 && Math.abs(mDy) > threshold) { 1226e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return dirFlag; 1227e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1228e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1229e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return 0; 1230e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1231e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1232e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void addChildDrawingOrderCallback() { 1233e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (Build.VERSION.SDK_INT >= 21) { 1234e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return;// we use elevation on Lollipop 1235e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1236e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mChildDrawingOrderCallback == null) { 1237e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() { 1238e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 1239e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int onGetChildDrawingOrder(int childCount, int i) { 1240e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mOverdrawChild == null) { 1241e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return i; 1242e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1243e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int childPosition = mOverdrawChildPosition; 1244e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (childPosition == -1) { 1245e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar childPosition = mRecyclerView.indexOfChild(mOverdrawChild); 1246e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mOverdrawChildPosition = childPosition; 1247e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1248e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (i == childCount - 1) { 1249e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return childPosition; 1250e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1251e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return i < childPosition ? i : i + 1; 1252e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1253e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }; 1254e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1255e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.setChildDrawingOrderCallback(mChildDrawingOrderCallback); 1256e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1257e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1258e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void removeChildDrawingOrderCallbackIfNecessary(View view) { 1259e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (view == mOverdrawChild) { 1260e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mOverdrawChild = null; 1261e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // only remove if we've added 1262e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mChildDrawingOrderCallback != null) { 1263e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mRecyclerView.setChildDrawingOrderCallback(null); 1264e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1265e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1266e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1267e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1268e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1269e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * An interface which can be implemented by LayoutManager for better integration with 1270e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link ItemTouchHelper}. 1271e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1272e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static interface ViewDropHandler { 1273e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1274e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1275e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called by the {@link ItemTouchHelper} after a View is dropped over another View. 1276e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1277e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * A LayoutManager should implement this interface to get ready for the upcoming move 1278e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * operation. 1279e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1280e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * For example, LinearLayoutManager sets up a "scrollToPositionWithOffset" calls so that 1281e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * the View under drag will be used as an anchor View while calculating the next layout, 1282e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * making layout stay consistent. 1283e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1284e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param view The View which is being dragged. It is very likely that user is still 1285e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * dragging this View so there might be other 1286e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #prepareForDrop(View, View, int, int)} after this one. 1287e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param target The target view which is being dropped on. 1288e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param x The <code>left</code> offset of the View that is being dragged. This value 1289e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * includes the movement caused by the user. 1290e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param y The <code>top</code> offset of the View that is being dragged. This value 1291e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * includes the movement caused by the user. 1292e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1293e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void prepareForDrop(View view, View target, int x, int y); 1294e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1295e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1296e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1297e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This class is the contract between ItemTouchHelper and your application. It lets you control 1298e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * which touch behaviors are enabled per each ViewHolder and also receive callbacks when user 1299e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * performs these actions. 1300e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1301e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * To control which actions user can take on each view, you should override 1302e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #getMovementFlags(RecyclerView, ViewHolder)} and return appropriate set 1303e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * of direction flags. ({@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link #END}, 1304e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #UP}, {@link #DOWN}). You can use 1305e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #makeMovementFlags(int, int)} to easily construct it. Alternatively, you can use 1306e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link SimpleCallback}. 1307e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1308e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If user drags an item, ItemTouchHelper will call 1309e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link Callback#onMove(RecyclerView, ViewHolder, ViewHolder) 1310e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * onMove(recyclerView, dragged, target)}. 1311e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Upon receiving this callback, you should move the item from the old position 1312e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ({@code dragged.getAdapterPosition()}) to new position ({@code target.getAdapterPosition()}) 1313e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * in your adapter and also call {@link RecyclerView.Adapter#notifyItemMoved(int, int)}. 1314e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * To control where a View can be dropped, you can override 1315e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #canDropOver(RecyclerView, ViewHolder, ViewHolder)}. When a 1316e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * dragging View overlaps multiple other views, Callback chooses the closest View with which 1317e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * dragged View might have changed positions. Although this approach works for many use cases, 1318e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * if you have a custom LayoutManager, you can override 1319e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #chooseDropTarget(ViewHolder, java.util.List, int, int)} to select a 1320e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * custom drop target. 1321e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1322e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * When a View is swiped, ItemTouchHelper animates it until it goes out of bounds, then calls 1323e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #onSwiped(ViewHolder, int)}. At this point, you should update your 1324e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * adapter (e.g. remove the item) and call related Adapter#notify event. 1325e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1326e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @SuppressWarnings("UnusedParameters") 1327e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public abstract static class Callback { 1328e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1329e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int DEFAULT_DRAG_ANIMATION_DURATION = 200; 1330e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1331e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static final int DEFAULT_SWIPE_ANIMATION_DURATION = 250; 1332e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1333e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar static final int RELATIVE_DIR_FLAGS = START | END | 1334e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ((START | END) << DIRECTION_FLAG_COUNT) | 1335e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ((START | END) << (2 * DIRECTION_FLAG_COUNT)); 1336e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1337c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar private static final ItemTouchUIUtil sUICallback; 1338e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1339e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final int ABS_HORIZONTAL_DIR_FLAGS = LEFT | RIGHT | 1340e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ((LEFT | RIGHT) << DIRECTION_FLAG_COUNT) | 1341e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ((LEFT | RIGHT) << (2 * DIRECTION_FLAG_COUNT)); 1342e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1343e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final Interpolator sDragScrollInterpolator = new Interpolator() { 1344e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public float getInterpolation(float t) { 1345e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return t * t * t * t * t; 1346e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1347e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }; 1348e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1349e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final Interpolator sDragViewScrollCapInterpolator = new Interpolator() { 1350e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public float getInterpolation(float t) { 1351e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar t -= 1.0f; 1352e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return t * t * t * t * t + 1.0f; 1353e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1354e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }; 1355e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1356e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1357e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Drag scroll speed keeps accelerating until this many milliseconds before being capped. 1358e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1359e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private static final long DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS = 2000; 1360e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1361e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int mCachedMaxScrollSpeed = -1; 1362e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1363e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar static { 1364e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (Build.VERSION.SDK_INT >= 21) { 1365c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar sUICallback = new ItemTouchUIUtilImpl.Lollipop(); 1366e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else if (Build.VERSION.SDK_INT >= 11) { 1367c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar sUICallback = new ItemTouchUIUtilImpl.Honeycomb(); 1368e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 1369c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar sUICallback = new ItemTouchUIUtilImpl.Gingerbread(); 1370e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1371e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1372e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1373e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1374c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * Returns the {@link ItemTouchUIUtil} that is used by the {@link Callback} class for visual 1375c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * changes on Views in response to user interactions. {@link ItemTouchUIUtil} has different 1376c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * implementations for different platform versions. 1377c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * <p> 1378c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * By default, {@link Callback} applies these changes on 1379c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * {@link RecyclerView.ViewHolder#itemView}. 1380c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * <p> 1381c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * For example, if you have a use case where you only want the text to move when user 1382c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * swipes over the view, you can do the following: 1383c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * <pre> 1384c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * public void clearView(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder){ 1385c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * getDefaultUIUtil().clearView(((ItemTouchViewHolder) viewHolder).textView); 1386c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * } 1387c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * public void onSelectedChanged(RecyclerView.ViewHolder viewHolder, int actionState) { 1388c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * if (viewHolder != null){ 1389c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * getDefaultUIUtil().onSelected(((ItemTouchViewHolder) viewHolder).textView); 1390c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * } 1391c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * } 1392c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * public void onChildDraw(Canvas c, RecyclerView recyclerView, 1393c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, 1394c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * boolean isCurrentlyActive) { 1395c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * getDefaultUIUtil().onDraw(c, recyclerView, 1396c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * ((ItemTouchViewHolder) viewHolder).textView, dX, dY, 1397c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * actionState, isCurrentlyActive); 1398c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * return true; 1399c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * } 1400c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * public void onChildDrawOver(Canvas c, RecyclerView recyclerView, 1401c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, 1402c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * boolean isCurrentlyActive) { 1403c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * getDefaultUIUtil().onDrawOver(c, recyclerView, 1404c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * ((ItemTouchViewHolder) viewHolder).textView, dX, dY, 1405c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * actionState, isCurrentlyActive); 1406c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * return true; 1407c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * } 1408c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * </pre> 1409c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * 1410c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar * @return The {@link ItemTouchUIUtil} instance that is used by the {@link Callback} 1411c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar */ 1412c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar public static ItemTouchUIUtil getDefaultUIUtil() { 1413c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar return sUICallback; 1414c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar } 1415c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar 1416c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar /** 1417e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Replaces a movement direction with its relative version by taking layout direction into 1418e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * account. 1419e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1420e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param flags The flag value that include any number of movement flags. 1421e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param layoutDirection The layout direction of the View. Can be obtained from 1422e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link ViewCompat#getLayoutDirection(android.view.View)}. 1423e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return Updated flags which uses relative flags ({@link #START}, {@link #END}) instead 1424e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * of {@link #LEFT}, {@link #RIGHT}. 1425e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #convertToAbsoluteDirection(int, int) 1426e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1427e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static int convertToRelativeDirection(int flags, int layoutDirection) { 1428e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int masked = flags & ABS_HORIZONTAL_DIR_FLAGS; 1429e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (masked == 0) { 1430e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return flags;// does not have any abs flags, good. 1431e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1432e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar flags &= ~masked; //remove left / right. 1433e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) { 1434e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // no change. just OR with 2 bits shifted mask and return 1435e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar flags |= masked << 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT. 1436e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return flags; 1437e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 1438e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // add RIGHT flag as START 1439e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar flags |= ((masked << 1) & ~ABS_HORIZONTAL_DIR_FLAGS); 1440e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // first clean RIGHT bit then add LEFT flag as END 1441e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar flags |= ((masked << 1) & ABS_HORIZONTAL_DIR_FLAGS) << 2; 1442e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1443e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return flags; 1444e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1445e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1446e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1447e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Convenience method to create movement flags. 1448e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1449e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * For instance, if you want to let your items be drag & dropped vertically and swiped 1450e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * left to be dismissed, you can call this method with: 1451e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <code>makeMovementFlags(UP | DOWN, LEFT);</code> 1452e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1453e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param dragFlags The directions in which the item can be dragged. 1454e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param swipeFlags The directions in which the item can be swiped. 1455e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return Returns an integer composed of the given drag and swipe flags. 1456e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1457e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static int makeMovementFlags(int dragFlags, int swipeFlags) { 1458e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags) | 1459e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar makeFlag(ACTION_STATE_SWIPE, swipeFlags) | makeFlag(ACTION_STATE_DRAG, 1460e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar dragFlags); 1461e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1462e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1463e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1464e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Shifts the given direction flags to the offset of the given action state. 1465e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1466e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param actionState The action state you want to get flags in. Should be one of 1467e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #ACTION_STATE_IDLE}, {@link #ACTION_STATE_SWIPE} or 1468e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #ACTION_STATE_DRAG}. 1469e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param directions The direction flags. Can be composed from {@link #UP}, {@link #DOWN}, 1470e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #RIGHT}, {@link #LEFT} {@link #START} and {@link #END}. 1471e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return And integer that represents the given directions in the provided actionState. 1472e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1473e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public static int makeFlag(int actionState, int directions) { 1474e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return directions << (actionState * DIRECTION_FLAG_COUNT); 1475e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1476e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1477e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1478e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Should return a composite flag which defines the enabled move directions in each state 1479e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * (idle, swiping, dragging). 1480e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1481e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Instead of composing this flag manually, you can use {@link #makeMovementFlags(int, 1482e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * int)} 1483e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * or {@link #makeFlag(int, int)}. 1484e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1485e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This flag is composed of 3 sets of 8 bits, where first 8 bits are for IDLE state, next 1486e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 8 bits are for SWIPE state and third 8 bits are for DRAG state. 1487e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Each 8 bit sections can be constructed by simply OR'ing direction flags defined in 1488e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link ItemTouchHelper}. 1489e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1490e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * For example, if you want it to allow swiping LEFT and RIGHT but only allow starting to 1491e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * swipe by swiping RIGHT, you can return: 1492e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <pre> 1493e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * makeFlag(ACTION_STATE_IDLE, RIGHT) | makeFlag(ACTION_STATE_SWIPE, LEFT | RIGHT); 1494e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * </pre> 1495e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This means, allow right movement while IDLE and allow right and left movement while 1496e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * swiping. 1497e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1498e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView to which ItemTouchHelper is attached. 1499e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder for which the movement information is necessary. 1500e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return flags specifying which movements are allowed on this ViewHolder. 1501e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #makeMovementFlags(int, int) 1502e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #makeFlag(int, int) 1503e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1504e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public abstract int getMovementFlags(RecyclerView recyclerView, 1505e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder viewHolder); 1506e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1507e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1508e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Converts a given set of flags to absolution direction which means {@link #START} and 1509e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #END} are replaced with {@link #LEFT} and {@link #RIGHT} depending on the layout 1510e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * direction. 1511e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1512e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param flags The flag value that include any number of movement flags. 1513e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param layoutDirection The layout direction of the RecyclerView. 1514e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return Updated flags which includes only absolute direction values. 1515e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1516e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int convertToAbsoluteDirection(int flags, int layoutDirection) { 1517e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int masked = flags & RELATIVE_DIR_FLAGS; 1518e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (masked == 0) { 1519e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return flags;// does not have any relative flags, good. 1520e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1521e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar flags &= ~masked; //remove start / end 1522e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (layoutDirection == ViewCompat.LAYOUT_DIRECTION_LTR) { 1523e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // no change. just OR with 2 bits shifted mask and return 1524e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar flags |= masked >> 2; // START is 2 bits after LEFT, END is 2 bits after RIGHT. 1525e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return flags; 1526e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 1527e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // add START flag as RIGHT 1528e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar flags |= ((masked >> 1) & ~RELATIVE_DIR_FLAGS); 1529e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // first clean start bit then add END flag as LEFT 1530e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar flags |= ((masked >> 1) & RELATIVE_DIR_FLAGS) >> 2; 1531e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1532e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return flags; 1533e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1534e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1535e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int getAbsoluteMovementFlags(RecyclerView recyclerView, 1536e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder viewHolder) { 1537e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int flags = getMovementFlags(recyclerView, viewHolder); 1538e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return convertToAbsoluteDirection(flags, ViewCompat.getLayoutDirection(recyclerView)); 1539e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1540e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1541e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private boolean hasDragFlag(RecyclerView recyclerView, ViewHolder viewHolder) { 1542e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder); 1543e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return (flags & ACTION_MODE_DRAG_MASK) != 0; 1544e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1545e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1546e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private boolean hasSwipeFlag(RecyclerView recyclerView, 1547e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder viewHolder) { 1548e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int flags = getAbsoluteMovementFlags(recyclerView, viewHolder); 1549e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return (flags & ACTION_MODE_SWIPE_MASK) != 0; 1550e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1551e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1552e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1553e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Return true if the current ViewHolder can be dropped over the the target ViewHolder. 1554e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1555e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This method is used when selecting drop target for the dragged View. After Views are 1556e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * eliminated either via bounds check or via this method, resulting set of views will be 1557e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * passed to {@link #chooseDropTarget(ViewHolder, java.util.List, int, int)}. 1558e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1559e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default implementation returns true. 1560e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1561e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to. 1562e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param current The ViewHolder that user is dragging. 1563e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param target The ViewHolder which is below the dragged ViewHolder. 1564e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return True if the dragged ViewHolder can be replaced with the target ViewHolder, false 1565e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * otherwise. 1566e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1567e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public boolean canDropOver(RecyclerView recyclerView, ViewHolder current, 1568e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder target) { 1569e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return true; 1570e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1571e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1572e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1573e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called when ItemTouchHelper wants to move the dragged item from its old position to 1574e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * the new position. 1575e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1576e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If this method returns true, ItemTouchHelper assumes {@code viewHolder} has been moved 1577e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * to the adapter position of {@code target} ViewHolder 1578e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ({@link ViewHolder#getAdapterPosition() 1579e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ViewHolder#getAdapterPosition()}). 1580e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1581e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If you don't support drag & drop, this method will never be called. 1582e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1583e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to. 1584e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder which is being dragged by the user. 1585e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param target The ViewHolder over which the currently active item is being 1586e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * dragged. 1587e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return True if the {@code viewHolder} has been moved to the adapter position of 1588e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@code target}. 1589e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #onMoved(RecyclerView, ViewHolder, int, ViewHolder, int, int, int) 1590e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1591e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public abstract boolean onMove(RecyclerView recyclerView, 1592e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder viewHolder, ViewHolder target); 1593e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1594e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1595e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Returns whether ItemTouchHelper should start a drag and drop operation if an item is 1596e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * long pressed. 1597e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1598e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default value returns true but you may want to disable this if you want to start 1599e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * dragging on a custom view touch using {@link #startDrag(ViewHolder)}. 1600e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1601e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return True if ItemTouchHelper should start dragging an item when it is long pressed, 1602e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * false otherwise. Default value is <code>true</code>. 1603e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #startDrag(ViewHolder) 1604e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1605e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public boolean isLongPressDragEnabled() { 1606e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return true; 1607e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1608e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1609e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1610e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Returns whether ItemTouchHelper should start a swipe operation if a pointer is swiped 1611e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * over the View. 1612e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1613e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default value returns true but you may want to disable this if you want to start 1614e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * swiping on a custom view touch using {@link #startSwipe(ViewHolder)}. 1615e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1616e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return True if ItemTouchHelper should start swiping an item when user swipes a pointer 1617e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * over the View, false otherwise. Default value is <code>true</code>. 1618e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #startSwipe(ViewHolder) 1619e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1620e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public boolean isItemViewSwipeEnabled() { 1621e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return true; 1622e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1623e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1624e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1625e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * When finding views under a dragged view, by default, ItemTouchHelper searches for views 1626e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * that overlap with the dragged View. By overriding this method, you can extend or shrink 1627e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * the search box. 1628e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1629e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return The extra margin to be added to the hit box of the dragged View. 1630e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1631e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int getBoundingBoxMargin() { 1632e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return 0; 1633e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1634e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1635e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1636e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Returns the fraction that the user should move the View to be considered as swiped. 1637e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * The fraction is calculated with respect to RecyclerView's bounds. 1638e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1639e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default value is .5f, which means, to swipe a View, user must move the View at least 1640e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * half of RecyclerView's width or height, depending on the swipe direction. 1641e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1642e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder that is being dragged. 1643e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return A float value that denotes the fraction of the View size. Default value 1644e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * is .5f . 1645e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1646e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public float getSwipeThreshold(ViewHolder viewHolder) { 1647e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return .5f; 1648e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1649e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1650e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1651e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Returns the fraction that the user should move the View to be considered as it is 1652e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * dragged. After a view is moved this amount, ItemTouchHelper starts checking for Views 1653e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * below it for a possible drop. 1654e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1655e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder that is being dragged. 1656e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return A float value that denotes the fraction of the View size. Default value is 1657e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * .5f . 1658e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1659e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public float getMoveThreshold(ViewHolder viewHolder) { 1660e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return .5f; 1661e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1662e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1663e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1664e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called by ItemTouchHelper to select a drop target from the list of ViewHolders that 1665e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * are under the dragged View. 1666e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1667e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default implementation filters the View with which dragged item have changed position 1668e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * in the drag direction. For instance, if the view is dragged UP, it compares the 1669e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <code>view.getTop()</code> of the two views before and after drag started. If that value 1670e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * is different, the target view passes the filter. 1671e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1672e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Among these Views which pass the test, the one closest to the dragged view is chosen. 1673e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1674e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This method is called on the main thread every time user moves the View. If you want to 1675e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * override it, make sure it does not do any expensive operations. 1676e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1677e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param selected The ViewHolder being dragged by the user. 1678e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param dropTargets The list of ViewHolder that are under the dragged View and 1679e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * candidate as a drop. 1680e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param curX The updated left value of the dragged View after drag translations 1681e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * are applied. This value does not include margins added by 1682e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link RecyclerView.ItemDecoration}s. 1683e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param curY The updated top value of the dragged View after drag translations 1684e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * are applied. This value does not include margins added by 1685e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link RecyclerView.ItemDecoration}s. 1686e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return A ViewHolder to whose position the dragged ViewHolder should be 1687e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * moved to. 1688e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1689e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public ViewHolder chooseDropTarget(ViewHolder selected, 1690e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar List<ViewHolder> dropTargets, int curX, int curY) { 1691e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int right = curX + selected.itemView.getWidth(); 1692e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int bottom = curY + selected.itemView.getHeight(); 1693e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder winner = null; 1694e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int winnerScore = -1; 1695e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int dx = curX - selected.itemView.getLeft(); 1696e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int dy = curY - selected.itemView.getTop(); 1697e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int targetsSize = dropTargets.size(); 1698e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = 0; i < targetsSize; i++) { 1699e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ViewHolder target = dropTargets.get(i); 1700e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dx > 0) { 1701e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int diff = target.itemView.getRight() - right; 1702e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (diff < 0 && target.itemView.getRight() > selected.itemView.getRight()) { 1703e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int score = Math.abs(diff); 1704e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (score > winnerScore) { 1705e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar winnerScore = score; 1706e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar winner = target; 1707e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1708e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1709e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1710e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dx < 0) { 1711e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int diff = target.itemView.getLeft() - curX; 1712e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (diff > 0 && target.itemView.getLeft() < selected.itemView.getLeft()) { 1713e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int score = Math.abs(diff); 1714e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (score > winnerScore) { 1715e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar winnerScore = score; 1716e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar winner = target; 1717e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1718e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1719e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1720e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dy < 0) { 1721e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int diff = target.itemView.getTop() - curY; 1722e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (diff > 0 && target.itemView.getTop() < selected.itemView.getTop()) { 1723e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int score = Math.abs(diff); 1724e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (score > winnerScore) { 1725e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar winnerScore = score; 1726e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar winner = target; 1727e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1728e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1729e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1730e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1731e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (dy > 0) { 1732e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int diff = target.itemView.getBottom() - bottom; 1733e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (diff < 0 && target.itemView.getBottom() > selected.itemView.getBottom()) { 1734e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int score = Math.abs(diff); 1735e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (score > winnerScore) { 1736e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar winnerScore = score; 1737e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar winner = target; 1738e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1739e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1740e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1741e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1742e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return winner; 1743e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1744e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1745e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1746e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called when a ViewHolder is swiped by the user. 1747e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1748e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If you are returning relative directions ({@link #START} , {@link #END}) from the 1749e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #getMovementFlags(RecyclerView, ViewHolder)} method, this method 1750e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * will also use relative directions. Otherwise, it will use absolute directions. 1751e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1752e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If you don't support swiping, this method will never be called. 1753e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1754e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper will keep a reference to the View until it is detached from 1755e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * RecyclerView. 1756e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * As soon as it is detached, ItemTouchHelper will call 1757e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #clearView(RecyclerView, ViewHolder)}. 1758e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1759e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder which has been swiped by the user. 1760e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param direction The direction to which the ViewHolder is swiped. It is one of 1761e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #UP}, {@link #DOWN}, 1762e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #LEFT} or {@link #RIGHT}. If your 1763e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #getMovementFlags(RecyclerView, ViewHolder)} 1764e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * method 1765e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * returned relative flags instead of {@link #LEFT} / {@link #RIGHT}; 1766e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * `direction` will be relative as well. ({@link #START} or {@link 1767e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * #END}). 1768e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1769e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public abstract void onSwiped(ViewHolder viewHolder, int direction); 1770e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1771e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1772e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called when the ViewHolder swiped or dragged by the ItemTouchHelper is changed. 1773e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p/> 1774e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If you override this method, you should call super. 1775e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1776e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The new ViewHolder that is being swiped or dragged. Might be null if 1777e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * it is cleared. 1778e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param actionState One of {@link ItemTouchHelper#ACTION_STATE_IDLE}, 1779e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link ItemTouchHelper#ACTION_STATE_SWIPE} or 1780e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link ItemTouchHelper#ACTION_STATE_DRAG}. 1781e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1782e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #clearView(RecyclerView, RecyclerView.ViewHolder) 1783e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1784e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onSelectedChanged(ViewHolder viewHolder, int actionState) { 1785e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (viewHolder != null) { 1786c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar sUICallback.onSelected(viewHolder.itemView); 1787e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1788e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1789e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1790e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int getMaxDragScroll(RecyclerView recyclerView) { 1791e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mCachedMaxScrollSpeed == -1) { 1792e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mCachedMaxScrollSpeed = recyclerView.getResources().getDimensionPixelSize( 1793e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar R.dimen.item_touch_helper_max_drag_scroll_per_frame); 1794e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1795e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return mCachedMaxScrollSpeed; 1796e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1797e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1798e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1799e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called when {@link #onMove(RecyclerView, ViewHolder, ViewHolder)} returns true. 1800e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1801e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper does not create an extra Bitmap or View while dragging, instead, it 1802e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * modifies the existing View. Because of this reason, it is important that the View is 1803e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * still part of the layout after it is moved. This may not work as intended when swapped 1804e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Views are close to RecyclerView bounds or there are gaps between them (e.g. other Views 1805e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * which were not eligible for dropping over). 1806e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1807e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This method is responsible to give necessary hint to the LayoutManager so that it will 1808e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * keep the View in visible area. For example, for LinearLayoutManager, this is as simple 1809e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * as calling {@link LinearLayoutManager#scrollToPositionWithOffset(int, int)}. 1810e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1811e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default implementation calls {@link RecyclerView#scrollToPosition(int)} if the View's 1812e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * new position is likely to be out of bounds. 1813e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1814e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * It is important to ensure the ViewHolder will stay visible as otherwise, it might be 1815e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * removed by the LayoutManager if the move causes the View to go out of bounds. In that 1816e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * case, drag will end prematurely. 1817e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1818e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView controlled by the ItemTouchHelper. 1819e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder under user's control. 1820e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param fromPos The previous adapter position of the dragged item (before it was 1821e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * moved). 1822e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param target The ViewHolder on which the currently active item has been dropped. 1823e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param toPos The new adapter position of the dragged item. 1824e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param x The updated left value of the dragged View after drag translations 1825e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * are applied. This value does not include margins added by 1826e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link RecyclerView.ItemDecoration}s. 1827e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param y The updated top value of the dragged View after drag translations 1828e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * are applied. This value does not include margins added by 1829e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link RecyclerView.ItemDecoration}s. 1830e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1831e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onMoved(final RecyclerView recyclerView, 1832e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ViewHolder viewHolder, int fromPos, final ViewHolder target, int toPos, int x, 1833e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int y) { 1834e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); 1835e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (layoutManager instanceof ViewDropHandler) { 1836e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ((ViewDropHandler) layoutManager).prepareForDrop(viewHolder.itemView, 1837e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar target.itemView, x, y); 1838e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 1839e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1840e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1841e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // if layout manager cannot handle it, do some guesswork 1842e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (layoutManager.canScrollHorizontally()) { 1843e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int minLeft = layoutManager.getDecoratedLeft(target.itemView); 1844e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (minLeft <= recyclerView.getPaddingLeft()) { 1845e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar recyclerView.scrollToPosition(toPos); 1846e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1847e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int maxRight = layoutManager.getDecoratedRight(target.itemView); 1848e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (maxRight >= recyclerView.getWidth() - recyclerView.getPaddingRight()) { 1849e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar recyclerView.scrollToPosition(toPos); 1850e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1851e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1852e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1853e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (layoutManager.canScrollVertically()) { 1854e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int minTop = layoutManager.getDecoratedTop(target.itemView); 1855e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (minTop <= recyclerView.getPaddingTop()) { 1856e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar recyclerView.scrollToPosition(toPos); 1857e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1858e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int maxBottom = layoutManager.getDecoratedBottom(target.itemView); 1859e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (maxBottom >= recyclerView.getHeight() - recyclerView.getPaddingBottom()) { 1860e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar recyclerView.scrollToPosition(toPos); 1861e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1862e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1863e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1864e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1865e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void onDraw(Canvas c, RecyclerView parent, ViewHolder selected, 1866e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar List<ItemTouchHelper.RecoverAnimation> recoverAnimationList, 1867e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int actionState, float dX, float dY) { 1868e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int recoverAnimSize = recoverAnimationList.size(); 1869e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = 0; i < recoverAnimSize; i++) { 1870e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i); 1871e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar anim.update(); 1872e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int count = c.save(); 1873e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar onChildDraw(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState, 1874e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar false); 1875e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar c.restoreToCount(count); 1876e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1877e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (selected != null) { 1878e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int count = c.save(); 1879e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar onChildDraw(c, parent, selected, dX, dY, actionState, true); 1880e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar c.restoreToCount(count); 1881e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1882e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1883e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1884e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private void onDrawOver(Canvas c, RecyclerView parent, ViewHolder selected, 1885e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar List<ItemTouchHelper.RecoverAnimation> recoverAnimationList, 1886e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int actionState, float dX, float dY) { 1887e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int recoverAnimSize = recoverAnimationList.size(); 1888e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = 0; i < recoverAnimSize; i++) { 1889e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ItemTouchHelper.RecoverAnimation anim = recoverAnimationList.get(i); 1890e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int count = c.save(); 1891e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar onChildDrawOver(c, parent, anim.mViewHolder, anim.mX, anim.mY, anim.mActionState, 1892e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar false); 1893e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar c.restoreToCount(count); 1894e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1895e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (selected != null) { 1896e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int count = c.save(); 1897e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar onChildDrawOver(c, parent, selected, dX, dY, actionState, true); 1898e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar c.restoreToCount(count); 1899e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1900e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar boolean hasRunningAnimation = false; 1901e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar for (int i = recoverAnimSize - 1; i >= 0; i--) { 1902e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecoverAnimation anim = recoverAnimationList.get(i); 1903e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (anim.mEnded && !anim.mIsPendingCleanup) { 1904e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar recoverAnimationList.remove(i); 1905e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar anim.mViewHolder.setIsRecyclable(true); 1906e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else if (!anim.mEnded) { 1907e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar hasRunningAnimation = true; 1908e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1909e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1910e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (hasRunningAnimation) { 1911e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar parent.invalidate(); 1912e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1913e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1914e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1915e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1916e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called by the ItemTouchHelper when the user interaction with an element is over and it 1917e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * also completed its animation. 1918e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1919e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This is a good place to clear all changes on the View that was done in 1920e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #onSelectedChanged(RecyclerView.ViewHolder, int)}, 1921e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #onChildDraw(Canvas, RecyclerView, ViewHolder, float, float, int, 1922e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * boolean)} or 1923e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, boolean)}. 1924e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1925e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView which is controlled by the ItemTouchHelper. 1926e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The View that was interacted by the user. 1927e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1928e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void clearView(RecyclerView recyclerView, ViewHolder viewHolder) { 1929c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar sUICallback.clearView(viewHolder.itemView); 1930e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1931e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1932e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1933e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called by ItemTouchHelper on RecyclerView's onDraw callback. 1934e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1935e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If you would like to customize how your View's respond to user interactions, this is 1936e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * a good place to override. 1937e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1938e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default implementation translates the child by the given <code>dX</code>, 1939e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <code>dY</code>. 1940e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper also takes care of drawing the child after other children if it is being 1941e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this 1942e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * is 1943e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L 1944e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * and after, it changes View's elevation value to be greater than all other children.) 1945e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1946e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param c The canvas which RecyclerView is drawing its children 1947e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to 1948e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder which is being interacted by the User or it was 1949e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * interacted and simply animating to its original position 1950e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param dX The amount of horizontal displacement caused by user's action 1951e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param dY The amount of vertical displacement caused by user's action 1952e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param actionState The type of interaction on the View. Is either {@link 1953e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}. 1954e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param isCurrentlyActive True if this view is currently being controlled by the user or 1955e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * false it is simply animating back to its original state. 1956e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, 1957e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * boolean) 1958e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1959e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onChildDraw(Canvas c, RecyclerView recyclerView, 1960e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder viewHolder, 1961e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float dX, float dY, int actionState, boolean isCurrentlyActive) { 1962c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar sUICallback.onDraw(c, recyclerView, viewHolder.itemView, dX, dY, actionState, 1963c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar isCurrentlyActive); 1964e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1965e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 1966e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 1967e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called by ItemTouchHelper on RecyclerView's onDraw callback. 1968e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1969e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * If you would like to customize how your View's respond to user interactions, this is 1970e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * a good place to override. 1971e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 1972e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default implementation translates the child by the given <code>dX</code>, 1973e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <code>dY</code>. 1974e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper also takes care of drawing the child after other children if it is being 1975e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * dragged. This is done using child re-ordering mechanism. On platforms prior to L, this 1976e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * is 1977e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * achieved via {@link android.view.ViewGroup#getChildDrawingOrder(int, int)} and on L 1978e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * and after, it changes View's elevation value to be greater than all other children.) 1979e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 1980e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param c The canvas which RecyclerView is drawing its children 1981e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView to which ItemTouchHelper is attached to 1982e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The ViewHolder which is being interacted by the User or it was 1983e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * interacted and simply animating to its original position 1984e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param dX The amount of horizontal displacement caused by user's action 1985e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param dY The amount of vertical displacement caused by user's action 1986e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param actionState The type of interaction on the View. Is either {@link 1987e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * #ACTION_STATE_DRAG} or {@link #ACTION_STATE_SWIPE}. 1988e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param isCurrentlyActive True if this view is currently being controlled by the user or 1989e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * false it is simply animating back to its original state. 1990e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @see #onChildDrawOver(Canvas, RecyclerView, ViewHolder, float, float, int, 1991e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * boolean) 1992e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 1993e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onChildDrawOver(Canvas c, RecyclerView recyclerView, 1994e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder viewHolder, 1995e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float dX, float dY, int actionState, boolean isCurrentlyActive) { 1996c0459105252a3db52bc44ad62ecff4288860180dYigit Boyar sUICallback.onDrawOver(c, recyclerView, viewHolder.itemView, dX, dY, actionState, 1997e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar isCurrentlyActive); 1998e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 1999e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2000e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2001e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called by the ItemTouchHelper when user action finished on a ViewHolder and now the View 2002e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * will be animated to its final position. 2003e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 2004e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default implementation uses ItemAnimator's duration values. If 2005e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <code>animationType</code> is {@link #ANIMATION_TYPE_DRAG}, it returns 2006e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link RecyclerView.ItemAnimator#getMoveDuration()}, otherwise, it returns 2007e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link RecyclerView.ItemAnimator#getRemoveDuration()}. If RecyclerView does not have 2008e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * any {@link RecyclerView.ItemAnimator} attached, this method returns 2009e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@code DEFAULT_DRAG_ANIMATION_DURATION} or {@code DEFAULT_SWIPE_ANIMATION_DURATION} 2010e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * depending on the animation type. 2011e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2012e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to. 2013e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param animationType The type of animation. Is one of {@link #ANIMATION_TYPE_DRAG}, 2014e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #ANIMATION_TYPE_SWIPE_CANCEL} or 2015e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #ANIMATION_TYPE_SWIPE_SUCCESS}. 2016e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param animateDx The horizontal distance that the animation will offset 2017e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param animateDy The vertical distance that the animation will offset 2018e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return The duration for the animation 2019e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2020e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public long getAnimationDuration(RecyclerView recyclerView, int animationType, 2021e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float animateDx, float animateDy) { 2022e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final RecyclerView.ItemAnimator itemAnimator = recyclerView.getItemAnimator(); 2023e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (itemAnimator == null) { 2024e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return animationType == ANIMATION_TYPE_DRAG ? DEFAULT_DRAG_ANIMATION_DURATION 2025e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar : DEFAULT_SWIPE_ANIMATION_DURATION; 2026e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 2027e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return animationType == ANIMATION_TYPE_DRAG ? itemAnimator.getMoveDuration() 2028e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar : itemAnimator.getRemoveDuration(); 2029e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2030e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2031e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2032e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2033e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Called by the ItemTouchHelper when user is dragging a view out of bounds. 2034e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <p> 2035e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * You can override this method to decide how much RecyclerView should scroll in response 2036e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * to this action. Default implementation calculates a value based on the amount of View 2037e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * out of bounds and the time it spent there. The longer user keeps the View out of bounds, 2038e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * the faster the list will scroll. Similarly, the larger portion of the View is out of 2039e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * bounds, the faster the RecyclerView will scroll. 2040e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2041e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView instance to which ItemTouchHelper is attached 2042e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * to. 2043e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewSize The total size of the View in scroll direction, excluding 2044e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * item decorations. 2045e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewSizeOutOfBounds The total size of the View that is out of bounds. This value 2046e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * is negative if the View is dragged towards left or top edge. 2047e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param totalSize The total size of RecyclerView in the scroll direction. 2048e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param msSinceStartScroll The time passed since View is kept out of bounds. 2049e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2050e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return The amount that RecyclerView should scroll. Keep in mind that this value will 2051e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * be passed to {@link RecyclerView#scrollBy(int, int)} method. 2052e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2053e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int interpolateOutOfBoundsScroll(RecyclerView recyclerView, 2054e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int viewSize, int viewSizeOutOfBounds, 2055e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int totalSize, long msSinceStartScroll) { 2056e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int maxScroll = getMaxDragScroll(recyclerView); 2057e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int absOutOfBounds = Math.abs(viewSizeOutOfBounds); 2058e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int direction = (int) Math.signum(viewSizeOutOfBounds); 2059e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // might be negative if other direction 2060e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float outOfBoundsRatio = Math.min(1f, 1f * absOutOfBounds / viewSize); 2061e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int cappedScroll = (int) (direction * maxScroll * 2062e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar sDragViewScrollCapInterpolator.getInterpolation(outOfBoundsRatio)); 2063e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float timeRatio; 2064e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (msSinceStartScroll > DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS) { 2065e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar timeRatio = 1f; 2066e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 2067e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar timeRatio = (float) msSinceStartScroll / DRAG_SCROLL_ACCELERATION_LIMIT_TIME_MS; 2068e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2069e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int value = (int) (cappedScroll * sDragScrollInterpolator 2070e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .getInterpolation(timeRatio)); 2071e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (value == 0) { 2072e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return viewSizeOutOfBounds > 0 ? 1 : -1; 2073e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2074e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return value; 2075e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2076e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2077e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2078e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2079e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * A simple wrapper to the default Callback which you can construct with drag and swipe 2080e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * directions and this class will handle the flag callbacks. You should still override onMove 2081e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * or 2082e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * onSwiped depending on your use case. 2083e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2084e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * <pre> 2085e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper mIth = new ItemTouchHelper( 2086e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * new ItemTouchHelper.SimpleCallback(ItemTouchHelper.UP | ItemTouchHelper.DOWN, 2087e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ItemTouchHelper.LEFT) { 2088e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * public abstract boolean onMove(RecyclerView recyclerView, 2089e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * ViewHolder viewHolder, ViewHolder target) { 2090e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * final int fromPos = viewHolder.getAdapterPosition(); 2091e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * final int toPos = viewHolder.getAdapterPosition(); 2092e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * // move item in `fromPos` to `toPos` in adapter. 2093e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * return true;// true if moved, false otherwise 2094e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * } 2095e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * public void onSwiped(ViewHolder viewHolder, int direction) { 2096e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * // remove from adapter 2097e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * } 2098e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * }); 2099e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * </pre> 2100e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2101e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public abstract static class SimpleCallback extends Callback { 2102e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2103e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int mDefaultSwipeDirs; 2104e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2105e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private int mDefaultDragDirs; 2106e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2107e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2108e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Creates a Callback for the given drag and swipe allowance. These values serve as 2109e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * defaults 2110e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * and if you want to customize behavior per ViewHolder, you can override 2111e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #getSwipeDirs(RecyclerView, ViewHolder)} 2112e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * and / or {@link #getDragDirs(RecyclerView, ViewHolder)}. 2113e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2114e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param dragDirs Binary OR of direction flags in which the Views can be dragged. Must be 2115e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link 2116e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * #END}, 2117e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #UP} and {@link #DOWN}. 2118e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param swipeDirs Binary OR of direction flags in which the Views can be swiped. Must be 2119e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * composed of {@link #LEFT}, {@link #RIGHT}, {@link #START}, {@link 2120e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * #END}, 2121e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #UP} and {@link #DOWN}. 2122e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2123e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public SimpleCallback(int dragDirs, int swipeDirs) { 2124e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDefaultSwipeDirs = swipeDirs; 2125e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDefaultDragDirs = dragDirs; 2126e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2127e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2128e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2129e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Updates the default swipe directions. For example, you can use this method to toggle 2130e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * certain directions depending on your use case. 2131e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2132e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param defaultSwipeDirs Binary OR of directions in which the ViewHolders can be swiped. 2133e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2134e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void setDefaultSwipeDirs(int defaultSwipeDirs) { 2135e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDefaultSwipeDirs = defaultSwipeDirs; 2136e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2137e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2138e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2139e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Updates the default drag directions. For example, you can use this method to toggle 2140e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * certain directions depending on your use case. 2141e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2142e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param defaultDragDirs Binary OR of directions in which the ViewHolders can be dragged. 2143e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2144e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void setDefaultDragDirs(int defaultDragDirs) { 2145e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDefaultDragDirs = defaultDragDirs; 2146e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2147e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2148e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2149e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Returns the swipe directions for the provided ViewHolder. 2150e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default implementation returns the swipe directions that was set via constructor or 2151e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #setDefaultSwipeDirs(int)}. 2152e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2153e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to. 2154e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The RecyclerView for which the swipe drection is queried. 2155e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return A binary OR of direction flags. 2156e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2157e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int getSwipeDirs(RecyclerView recyclerView, ViewHolder viewHolder) { 2158e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return mDefaultSwipeDirs; 2159e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2160e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2161e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2162e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Returns the drag directions for the provided ViewHolder. 2163e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * Default implementation returns the drag directions that was set via constructor or 2164e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * {@link #setDefaultDragDirs(int)}. 2165e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * 2166e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param recyclerView The RecyclerView to which the ItemTouchHelper is attached to. 2167e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @param viewHolder The RecyclerView for which the swipe drection is queried. 2168e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * @return A binary OR of direction flags. 2169e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2170e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int getDragDirs(RecyclerView recyclerView, ViewHolder viewHolder) { 2171e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return mDefaultDragDirs; 2172e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2173e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2174e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 2175e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public int getMovementFlags(RecyclerView recyclerView, ViewHolder viewHolder) { 2176e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return makeMovementFlags(getDragDirs(recyclerView, viewHolder), 2177e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar getSwipeDirs(recyclerView, viewHolder)); 2178e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2179e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2180e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2181e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private class ItemTouchHelperGestureListener extends GestureDetector.SimpleOnGestureListener { 2182e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2183e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 2184e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public boolean onDown(MotionEvent e) { 2185e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return true; 2186e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2187e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2188e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 2189e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onLongPress(MotionEvent e) { 2190e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar View child = findChildView(e); 2191e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (child != null) { 2192e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar ViewHolder vh = mRecyclerView.getChildViewHolder(child); 2193e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (vh != null) { 2194e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (!mCallback.hasDragFlag(mRecyclerView, vh)) { 2195e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar return; 2196e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2197e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int pointerId = MotionEventCompat.getPointerId(e, 0); 2198e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // Long press is deferred. 2199e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // Check w/ active pointer id to avoid selecting after motion 2200e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // event is canceled. 2201e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (pointerId == mActivePointerId) { 2202e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int index = MotionEventCompat 2203e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar .findPointerIndex(e, mActivePointerId); 2204e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float x = MotionEventCompat.getX(e, index); 2205e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float y = MotionEventCompat.getY(e, index); 2206e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mInitialTouchX = x; 2207e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mInitialTouchY = y; 2208e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mDx = mDy = 0f; 2209e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (DEBUG) { 2210e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar Log.d(TAG, 2211e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar "onlong press: x:" + mInitialTouchX + ",y:" + mInitialTouchY); 2212e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2213e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mCallback.isLongPressDragEnabled()) { 2214e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar select(vh, ACTION_STATE_DRAG); 2215e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2216e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2217e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2218e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2219e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2220e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2221e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2222e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private class RecoverAnimation implements AnimatorListenerCompat { 2223e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2224e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float mStartDx; 2225e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2226e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float mStartDy; 2227e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2228e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float mTargetX; 2229e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2230e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final float mTargetY; 2231e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2232e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final ViewHolder mViewHolder; 2233e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2234e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar final int mActionState; 2235e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2236e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private final ValueAnimatorCompat mValueAnimator; 2237e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2238e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private final int mAnimationType; 2239e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2240e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public boolean mIsPendingCleanup; 2241e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2242e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float mX; 2243e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2244e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar float mY; 2245e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2246e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // if user starts touching a recovering view, we put it into interaction mode again, 2247e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar // instantly. 2248e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar boolean mOverridden = false; 2249e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2250e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private boolean mEnded = false; 2251e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2252e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar private float mFraction; 2253e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2254e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public RecoverAnimation(ViewHolder viewHolder, int animationType, 2255e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar int actionState, float startDx, float startDy, float targetX, float targetY) { 2256e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mActionState = actionState; 2257e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mAnimationType = animationType; 2258e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mViewHolder = viewHolder; 2259e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mStartDx = startDx; 2260e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mStartDy = startDy; 2261e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mTargetX = targetX; 2262e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mTargetY = targetY; 2263e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mValueAnimator = AnimatorCompatHelper.emptyValueAnimator(); 2264e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mValueAnimator.addUpdateListener( 2265e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar new AnimatorUpdateListenerCompat() { 2266e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 2267e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onAnimationUpdate(ValueAnimatorCompat animation) { 2268e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar setFraction(animation.getAnimatedFraction()); 2269e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2270e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar }); 2271e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mValueAnimator.setTarget(viewHolder.itemView); 2272e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mValueAnimator.addListener(this); 2273e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar setFraction(0f); 2274e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2275e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2276e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void setDuration(long duration) { 2277e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mValueAnimator.setDuration(duration); 2278e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2279e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2280e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void start() { 2281e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mViewHolder.setIsRecyclable(false); 2282e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mValueAnimator.start(); 2283e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2284e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2285e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void cancel() { 2286e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mValueAnimator.cancel(); 2287e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2288e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2289e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void setFraction(float fraction) { 2290e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mFraction = fraction; 2291e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2292e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2293e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar /** 2294e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * We run updates on onDraw method but use the fraction from animator callback. 2295e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar * This way, we can sync translate x/y values w/ the animators to avoid one-off frames. 2296e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar */ 2297e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void update() { 2298e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mStartDx == mTargetX) { 2299e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mX = ViewCompat.getTranslationX(mViewHolder.itemView); 2300e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 2301e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mX = mStartDx + mFraction * (mTargetX - mStartDx); 2302e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2303e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar if (mStartDy == mTargetY) { 2304e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mY = ViewCompat.getTranslationY(mViewHolder.itemView); 2305e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } else { 2306e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mY = mStartDy + mFraction * (mTargetY - mStartDy); 2307e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2308e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2309e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2310e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 2311e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onAnimationStart(ValueAnimatorCompat animation) { 2312e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2313e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2314e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2315e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 2316e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onAnimationEnd(ValueAnimatorCompat animation) { 2317e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar mEnded = true; 2318e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2319e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2320e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 2321e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onAnimationCancel(ValueAnimatorCompat animation) { 2322e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar setFraction(1f); //make sure we recover the view's state. 2323e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2324e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2325e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar @Override 2326e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar public void onAnimationRepeat(ValueAnimatorCompat animation) { 2327e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar 2328e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2329e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar } 2330e71a1df9b3c0e1bd3c21a1b3dd20a41790d4a950Yigit Boyar}