Improve UI/UX and use actually intended swipe not drag

You now have to click the delete icon. This was a UX choice so you don't inadvertently swipe and create unexpected popups
This commit is contained in:
Luna712 2024-06-12 18:38:00 -06:00 committed by GitHub
parent b8a696b11a
commit 81ae8b6591
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -14,6 +14,7 @@ import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.text.format.Formatter.formatShortFileSize import android.text.format.Formatter.formatShortFileSize
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
@ -299,9 +300,6 @@ class DownloadFragment : Fragment() {
} }
class SwipeToDeleteCallback(private val adapter: DownloadHeaderAdapter) : ItemTouchHelper.Callback() { class SwipeToDeleteCallback(private val adapter: DownloadHeaderAdapter) : ItemTouchHelper.Callback() {
private var deleteInitiated = false
override fun getMovementFlags( override fun getMovementFlags(
recyclerView: RecyclerView, recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder viewHolder: RecyclerView.ViewHolder
@ -309,12 +307,12 @@ class SwipeToDeleteCallback(private val adapter: DownloadHeaderAdapter) : ItemTo
val position = viewHolder.bindingAdapterPosition val position = viewHolder.bindingAdapterPosition
val item = adapter.cardList[position] val item = adapter.cardList[position]
if (item.data.type.isEpisodeBased()) return 0 if (item.data.type.isEpisodeBased()) return 0
return makeMovementFlags(ItemTouchHelper.LEFT, 0) return makeMovementFlags(0, ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT)
} }
override fun isLongPressDragEnabled(): Boolean = true override fun isLongPressDragEnabled(): Boolean = false
override fun isItemViewSwipeEnabled(): Boolean = false override fun isItemViewSwipeEnabled(): Boolean = true
override fun onMove( override fun onMove(
recyclerView: RecyclerView, recyclerView: RecyclerView,
@ -328,9 +326,6 @@ class SwipeToDeleteCallback(private val adapter: DownloadHeaderAdapter) : ItemTo
) {} ) {}
private fun handleDelete(viewHolder: RecyclerView.ViewHolder) { private fun handleDelete(viewHolder: RecyclerView.ViewHolder) {
if (deleteInitiated) return
deleteInitiated = true
val position = viewHolder.bindingAdapterPosition val position = viewHolder.bindingAdapterPosition
val item = adapter.cardList[position] val item = adapter.cardList[position]
@ -346,6 +341,7 @@ class SwipeToDeleteCallback(private val adapter: DownloadHeaderAdapter) : ItemTo
} }
} }
@SuppressLint("ClickableViewAccessibility")
override fun onChildDraw( override fun onChildDraw(
c: Canvas, c: Canvas,
recyclerView: RecyclerView, recyclerView: RecyclerView,
@ -355,65 +351,84 @@ class SwipeToDeleteCallback(private val adapter: DownloadHeaderAdapter) : ItemTo
actionState: Int, actionState: Int,
isCurrentlyActive: Boolean isCurrentlyActive: Boolean
) { ) {
if (dX == 0f) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
return
}
val deleteIcon: Drawable = ContextCompat.getDrawable( val deleteIcon: Drawable = ContextCompat.getDrawable(
recyclerView.context, recyclerView.context,
R.drawable.ic_baseline_delete_outline_24 R.drawable.ic_baseline_delete_outline_24
) ?: return ) ?: return
val background = ColorDrawable(Color.RED) val background = ColorDrawable(Color.RED)
background.alpha = 160
val itemView = viewHolder.itemView val itemView = viewHolder.itemView
val scaleFactor = 1.5f val scaleFactor = 1.25f
val iconWidth = (deleteIcon.intrinsicWidth * scaleFactor).toInt() val iconWidth = (deleteIcon.intrinsicWidth * scaleFactor).toInt()
val iconHeight = (deleteIcon.intrinsicHeight * scaleFactor).toInt() val iconHeight = (deleteIcon.intrinsicHeight * scaleFactor).toInt()
val iconMargin = (itemView.height - iconHeight) / 2
val iconTop = itemView.top + (itemView.height - iconHeight) / 2 val iconTop = itemView.top + (itemView.height - iconHeight) / 2
val iconBottom = iconTop + iconHeight val iconBottom = iconTop + iconHeight
val swipeDistance = itemView.width / 4 val maxSwipeDistance = 230f
val limitedDX = if (dX < -swipeDistance) -swipeDistance.toFloat() else dX val minSwipeDistance = itemView.width / 4.5f
val swipeDistance = if (minSwipeDistance <= maxSwipeDistance) {
minSwipeDistance
} else maxSwipeDistance
val limitedDX = if (dX < -swipeDistance) -swipeDistance else if (dX >= 0) 0f else dX
if (limitedDX < 0) { // Swiping to the left if (limitedDX < 0) { // Swiping to the left
val iconLeft = itemView.right - iconMargin - iconWidth val backgroundLeft = itemView.right + limitedDX.toInt()
val iconRight = itemView.right - iconMargin val backgroundRight = itemView.right
val backgroundTop = itemView.top
val backgroundBottom = itemView.bottom
val iconLeft = backgroundLeft + (backgroundRight - backgroundLeft - iconWidth) / 2
val iconRight = iconLeft + iconWidth
deleteIcon.setBounds(iconLeft, iconTop, iconRight, iconBottom) deleteIcon.setBounds(iconLeft, iconTop, iconRight, iconBottom)
val path = Path() val path = Path()
val rectF = RectF( val rectF = RectF(
itemView.right + limitedDX, backgroundLeft.toFloat(),
itemView.top.toFloat(), backgroundTop.toFloat(),
itemView.right.toFloat(), backgroundRight.toFloat(),
itemView.bottom.toFloat() backgroundBottom.toFloat()
) )
val radii = floatArrayOf(0f, 0f, 20f, 20f, 20f, 20f, 0f, 0f) val radii = floatArrayOf(0f, 0f, 20f, 20f, 20f, 20f, 0f, 0f)
path.addRoundRect(rectF, radii, Path.Direction.CW) path.addRoundRect(rectF, radii, Path.Direction.CW)
c.clipPath(path) c.clipPath(path)
background.setBounds( background.setBounds(backgroundLeft, backgroundTop, backgroundRight, backgroundBottom)
itemView.right + limitedDX.toInt(),
itemView.top,
itemView.right,
itemView.bottom
)
} else background.setBounds(0, 0, 0, 0) } else background.setBounds(0, 0, 0, 0)
background.draw(c) background.draw(c)
deleteIcon.draw(c) deleteIcon.draw(c)
if (dX <= -swipeDistance && !isCurrentlyActive) { if (dX <= -swipeDistance && !isCurrentlyActive) {
handleDelete(viewHolder) recyclerView.setOnTouchListener { _, event ->
} else super.onChildDraw(c, recyclerView, viewHolder, limitedDX, dY, actionState, isCurrentlyActive) if (event.action == MotionEvent.ACTION_DOWN) {
} val x = event.x.toInt()
val y = event.y.toInt()
override fun clearView( val backgroundLeft = itemView.right + limitedDX.toInt()
recyclerView: RecyclerView, val backgroundRight = itemView.right
viewHolder: RecyclerView.ViewHolder val backgroundTop = itemView.top
) { val backgroundBottom = itemView.bottom
super.clearView(recyclerView, viewHolder)
deleteInitiated = false if (x in backgroundLeft..backgroundRight && y >= backgroundTop && y <= backgroundBottom) {
handleDelete(viewHolder)
true
} else false
} else false
}
} else {
recyclerView.setOnTouchListener(null)
super.onChildDraw(c, recyclerView, viewHolder, limitedDX, dY, actionState, isCurrentlyActive)
}
} }
} }