From de273123dd8b2926ad50262142b28a15e41ea080 Mon Sep 17 00:00:00 2001 From: Robb Date: Wed, 25 Nov 2020 22:07:34 +0100 Subject: [PATCH 1/5] Fix broken swipe distance when item width is smaller than the recyclerView's width (e.g. grid layout) --- .../fastadapter/swipe/SimpleSwipeDrawerCallback.kt | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt b/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt index f8c918c3f..c56cc7897 100644 --- a/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt +++ b/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt @@ -10,6 +10,8 @@ import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.IItem +import kotlin.math.max +import kotlin.math.min /** @@ -153,7 +155,11 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: var swipeableView = itemView if (viewHolder is IDrawerSwipeableViewHolder) swipeableView = viewHolder.swipeableView - swipeableView.translationX = dX * swipeWidthPc + // Android's ItemTouchHelper incorrectly sets dX to recyclerView.width when swiping to the max, + // which breaks the animation when itemView is smaller than that (e.g. two columns layout) + // => fix animation by limiting dX to the itemView's width + val realDx = if (dX < 0) max(dX, -itemView.width * 1f) else min(dX, itemView.width * 1f) + swipeableView.translationX = realDx * swipeWidthPc } else super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) } From d24377891583f3c3aede97b54674564c989fa0b3 Mon Sep 17 00:00:00 2001 From: Robb Date: Sun, 29 Nov 2020 09:19:15 +0100 Subject: [PATCH 2/5] Better reaction to "unswipe" gesture when using a gridview --- .../fastadapter/swipe/SimpleSwipeDrawerCallback.kt | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt b/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt index c56cc7897..039fac55e 100644 --- a/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt +++ b/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt @@ -10,8 +10,6 @@ import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.IItem -import kotlin.math.max -import kotlin.math.min /** @@ -137,11 +135,16 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: } val position = viewHolder.adapterPosition + if (position == RecyclerView.NO_POSITION) return if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) { // Careful, dX is not the delta of user's movement, it's the new offset of the swiped view's left side ! val isLeftArea = dX < 0 + // Android's ItemTouchHelper incorrectly sets dX to recyclerView.width when swiping to the max, + // which breaks the animation when itemView is smaller than that (e.g. two columns layout) + // => fix animation by limiting dX to the itemView's width + val itemDx = (dX / recyclerView.width) * itemView.width // If unswiped, fire event and update swiped state if (0f == dX && swipedStates.containsKey(position)) { @@ -155,11 +158,7 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: var swipeableView = itemView if (viewHolder is IDrawerSwipeableViewHolder) swipeableView = viewHolder.swipeableView - // Android's ItemTouchHelper incorrectly sets dX to recyclerView.width when swiping to the max, - // which breaks the animation when itemView is smaller than that (e.g. two columns layout) - // => fix animation by limiting dX to the itemView's width - val realDx = if (dX < 0) max(dX, -itemView.width * 1f) else min(dX, itemView.width * 1f) - swipeableView.translationX = realDx * swipeWidthPc + swipeableView.translationX = itemDx * swipeWidthPc } else super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) } From 669418f34cdedda4ba75d6111a9da90a602383a5 Mon Sep 17 00:00:00 2001 From: Robb Date: Sun, 29 Nov 2020 10:08:22 +0100 Subject: [PATCH 3/5] Align swipeable view height to its parent's --- app/src/main/res/layout/swipeable_drawer_item.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/layout/swipeable_drawer_item.xml b/app/src/main/res/layout/swipeable_drawer_item.xml index f08e5240a..4ea8e25b3 100644 --- a/app/src/main/res/layout/swipeable_drawer_item.xml +++ b/app/src/main/res/layout/swipeable_drawer_item.xml @@ -76,7 +76,7 @@ Date: Sun, 29 Nov 2020 12:47:42 +0100 Subject: [PATCH 4/5] Prevent RecyclerTouchTransmitter.onTouch from interfering in standard swipe moves while a swipe gesture is occuring --- .../swipe/SimpleSwipeDrawerCallback.kt | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt b/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt index 039fac55e..5922bc50a 100644 --- a/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt +++ b/library-extensions-swipe/src/main/java/com/mikepenz/fastadapter/swipe/SimpleSwipeDrawerCallback.kt @@ -10,6 +10,7 @@ import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.RecyclerView import com.mikepenz.fastadapter.FastAdapter import com.mikepenz.fastadapter.IItem +import kotlin.math.abs /** @@ -33,6 +34,9 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: // Value = swiped direction (see {@link ItemTouchHelper}) private val swipedStates = HashMap() + // True if a swiping gesture is currently being done + var isSwiping = false + interface ItemSwipeCallback { /** @@ -107,6 +111,7 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: if (position != RecyclerView.NO_POSITION && (!swipedStates.containsKey(position) || swipedStates[position] != direction)) { itemSwipeCallback?.itemSwiped(position, direction) swipedStates[position] = direction + isSwiping = false } } @@ -144,7 +149,7 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: // Android's ItemTouchHelper incorrectly sets dX to recyclerView.width when swiping to the max, // which breaks the animation when itemView is smaller than that (e.g. two columns layout) // => fix animation by limiting dX to the itemView's width - val itemDx = (dX / recyclerView.width) * itemView.width + val dXPercent = dX / recyclerView.width // If unswiped, fire event and update swiped state if (0f == dX && swipedStates.containsKey(position)) { @@ -152,13 +157,14 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: swipedStates.remove(position) } - var swipeWidthPc = recyclerView.context.resources.displayMetrics.density / itemView.width - swipeWidthPc *= if (isLeftArea) swipeWidthLeftDp else swipeWidthRightDp + // If the position is between "swiped" and "unswiped", then we're swiping + isSwiping = (abs(dXPercent) > 0 && abs(dXPercent) < 1) var swipeableView = itemView if (viewHolder is IDrawerSwipeableViewHolder) swipeableView = viewHolder.swipeableView - swipeableView.translationX = itemDx * swipeWidthPc + val swipeWidthPc = recyclerView.context.resources.displayMetrics.density * if (isLeftArea) swipeWidthLeftDp else swipeWidthRightDp + swipeableView.translationX = dXPercent * swipeWidthPc } else super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive) } @@ -168,10 +174,10 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: * Android default touch event mechanisms don't transmit these events to the sublayer : * any click on the exposed surface just swipes the item back to where it came */ - class RecyclerTouchTransmitter : View.OnTouchListener { + inner class RecyclerTouchTransmitter : View.OnTouchListener { override fun onTouch(v: View?, event: MotionEvent): Boolean { - if (null == v || v !is ViewGroup) return false + if (isSwiping || null == v || v !is ViewGroup) return false // Get the first visible View under the clicked coordinates val childView = v.getFirstVisibleViewByCoordinates(event.x, event.y) @@ -179,9 +185,11 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs: if (childView != null) when (event.actionMasked) { MotionEvent.ACTION_DOWN -> { + println("> action down") return childView.onTouchEvent(event) } MotionEvent.ACTION_UP -> { + println("> action up") return childView.onTouchEvent(event) } } From dcfda1a533b7fa90d06ab894cba37b0ae166e20b Mon Sep 17 00:00:00 2001 From: Robb Date: Sun, 29 Nov 2020 12:58:48 +0100 Subject: [PATCH 5/5] Fix merge conflict --- app/src/main/res/layout/swipeable_drawer_item.xml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/app/src/main/res/layout/swipeable_drawer_item.xml b/app/src/main/res/layout/swipeable_drawer_item.xml index 4ea8e25b3..c19a6cfa2 100644 --- a/app/src/main/res/layout/swipeable_drawer_item.xml +++ b/app/src/main/res/layout/swipeable_drawer_item.xml @@ -77,10 +77,11 @@ android:id="@+id/item_content" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@android:color/white" + android:background="?android:colorBackground" android:clickable="true" android:elevation="4dp" android:focusable="true" + android:foreground="?android:selectableItemBackground" android:gravity="center_vertical|start" android:orientation="vertical" android:paddingStart="@dimen/material_drawer_vertical_padding"