Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix broken swipe distance when item width < recyclerView's width #951

Merged
merged 5 commits into from
Nov 30, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/src/main/res/layout/swipeable_drawer_item.xml
Original file line number Diff line number Diff line change
Expand Up @@ -76,11 +76,12 @@
<LinearLayout
android:id="@+id/item_content"
android:layout_width="match_parent"
android:layout_height="@dimen/material_drawer_item_primary"
android:background="@android:color/white"
android:layout_height="match_parent"
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"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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


/**
Expand All @@ -33,6 +34,9 @@ class SimpleSwipeDrawerCallback @JvmOverloads constructor(private val swipeDirs:
// Value = swiped direction (see {@link ItemTouchHelper})
private val swipedStates = HashMap<Int, Int>()

// True if a swiping gesture is currently being done
var isSwiping = false

interface ItemSwipeCallback {

/**
Expand Down Expand Up @@ -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
}
}

Expand Down Expand Up @@ -135,25 +140,31 @@ 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 dXPercent = dX / recyclerView.width

// If unswiped, fire event and update swiped state
if (0f == dX && swipedStates.containsKey(position)) {
itemSwipeCallback?.itemUnswiped(viewHolder.adapterPosition)
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 = dX * 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)
}

Expand All @@ -163,20 +174,22 @@ 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)
// Transmit the ACTION_DOWN and ACTION_UP events to this View
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)
}
}
Expand Down