1
1
package com.mikepenz.fastadapter.swipe
2
2
3
+ import android.annotation.SuppressLint
3
4
import android.graphics.Canvas
4
5
import android.graphics.Rect
5
6
import android.view.MotionEvent
@@ -29,10 +30,14 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs:
29
30
// Indicates whether the touchTransmitter has been set on the RecyclerView
30
31
private var touchTransmitterSet = false
31
32
32
- // States of swiped items
33
- // Key = item position
34
- // Value = swiped direction (see {@link ItemTouchHelper})
35
- private val swipedStates = HashMap <Int , Int >()
33
+ /* *
34
+ * States of swiped items
35
+ * Key = item unique ID
36
+ * Value = swiped direction (see {@link androidx.recyclerView.widget.ItemTouchHelper})
37
+ *
38
+ * NB : As this class doesn't listen to the recyclerView, it may contain identifiers for old items that have been removed
39
+ */
40
+ private val swipedStates = HashMap <Long , Int >()
36
41
37
42
// True if a swiping gesture is currently being done
38
43
var isSwiping = false
@@ -108,9 +113,10 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs:
108
113
109
114
override fun onSwiped (viewHolder : RecyclerView .ViewHolder , direction : Int ) {
110
115
val position = viewHolder.adapterPosition
111
- if (position != RecyclerView .NO_POSITION && (! swipedStates.containsKey(position) || swipedStates[position] != direction)) {
116
+ val id = viewHolder.itemId
117
+ if (position != RecyclerView .NO_POSITION && (! swipedStates.containsKey(id) || swipedStates[id] != direction)) {
112
118
itemSwipeCallback?.itemSwiped(position, direction)
113
- swipedStates[position ] = direction
119
+ swipedStates[id ] = direction
114
120
isSwiping = false
115
121
}
116
122
}
@@ -127,10 +133,11 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs:
127
133
override fun getSwipeThreshold (viewHolder : RecyclerView .ViewHolder ): Float {
128
134
// During the "unswipe" gesture, Android doesn't use the threshold value properly
129
135
// => Need to communicate an inverted value for swiped items
130
- return if (swipedStates.containsKey(viewHolder.adapterPosition )) 1f - surfaceThreshold
136
+ return if (swipedStates.containsKey(viewHolder.itemId )) 1f - surfaceThreshold
131
137
else surfaceThreshold
132
138
}
133
139
140
+ @SuppressLint(" ClickableViewAccessibility" )
134
141
override fun onChildDraw (c : Canvas , recyclerView : RecyclerView , viewHolder : RecyclerView .ViewHolder , dX : Float , dY : Float , actionState : Int , isCurrentlyActive : Boolean ) {
135
142
val itemView = viewHolder.itemView
136
143
@@ -152,9 +159,9 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs:
152
159
val dXPercent = dX / recyclerView.width
153
160
154
161
// If unswiped, fire event and update swiped state
155
- if (0f == dX && swipedStates.containsKey(position )) {
162
+ if (0f == dX && swipedStates.containsKey(viewHolder.itemId )) {
156
163
itemSwipeCallback?.itemUnswiped(viewHolder.adapterPosition)
157
- swipedStates.remove(position )
164
+ swipedStates.remove(viewHolder.itemId )
158
165
}
159
166
160
167
// If the position is between "swiped" and "unswiped", then we're swiping
@@ -174,11 +181,17 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs:
174
181
* Android default touch event mechanisms don't transmit these events to the sublayer :
175
182
* any click on the exposed surface just swipes the item back to where it came
176
183
*/
184
+ @SuppressLint(" ClickableViewAccessibility" )
177
185
inner class RecyclerTouchTransmitter : View .OnTouchListener {
178
186
187
+ private var recyclerView: RecyclerView ? = null
188
+
179
189
override fun onTouch (v : View ? , event : MotionEvent ): Boolean {
190
+ // No need for that when nothing is swiped or when swiping is in progress
180
191
if (isSwiping || null == v || v !is ViewGroup ) return false
181
192
193
+ if (v is RecyclerView ) recyclerView = v
194
+
182
195
// Get the first visible View under the clicked coordinates
183
196
val childView = v.getFirstVisibleViewByCoordinates(event.x, event.y)
184
197
// Transmit the ACTION_DOWN and ACTION_UP events to this View
@@ -198,6 +211,13 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs:
198
211
* Return the first visible non-ViewGroup View within the given ViewGroup, at the given coordinates
199
212
*/
200
213
private fun ViewGroup.getFirstVisibleViewByCoordinates (x : Float , y : Float ): View ? {
214
+
215
+ // If target viewHolder isn't swiped, don't bother going further
216
+ if (this .parent == recyclerView) {
217
+ val viewHolder = recyclerView?.getChildViewHolder(this )
218
+ if (! swipedStates.containsKey(viewHolder?.itemId)) return null
219
+ }
220
+
201
221
(childCount - 1 downTo 0 )
202
222
.map { this .getChildAt(it) }
203
223
.forEach {
0 commit comments