android - 如何使用 ItemTouchHelper 在 recyclerview 的每一侧显示 2 个单独的按钮
问题描述
因此,我试图在我的回收站视图中创建一个可滑动列表,如果我向右滑动,左侧会显示撤消按钮,如果我向左滑动,则会在右侧显示删除按钮。我能够为左右滑动显示一个按钮。如果我先向左滑动,即使我向右滑动也会显示删除按钮。当我向右滑动时,也会发生同样的事情。我认为这与绘图功能的工作方式有关,但我不确定出了什么问题。我想要做的是让左右滑动绘制2个彼此独立的不同按钮
这是我在 kotlin 中的 ItemTouchHelper 类
import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.view.MotionEvent
import android.view.View
import androidx.annotation.ColorRes
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.RecyclerView
import java.util.*
import kotlin.math.abs
import kotlin.math.max
abstract class SwipeHelper(
private val recyclerView: RecyclerView
) : ItemTouchHelper.SimpleCallback(
ItemTouchHelper.ACTION_STATE_IDLE,
ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
) {
private var swipedPosition = -1
private val buttonsBuffer: MutableMap<Int, List<UnderlayButton>> = mutableMapOf()
private val recoverQueue = object : LinkedList<Int>() {
override fun add(element: Int): Boolean {
if (contains(element)) return false
return super.add(element)
}
}
@SuppressLint("ClickableViewAccessibility")
private val touchListener = View.OnTouchListener { _, event ->
if (swipedPosition < 0) return@OnTouchListener false
buttonsBuffer[swipedPosition]?.forEach { it.handle(event) }
recoverQueue.add(swipedPosition)
swipedPosition = -1
recoverSwipedItem()
true
}
init {
recyclerView.setOnTouchListener(touchListener)
}
private fun recoverSwipedItem() {
while (!recoverQueue.isEmpty()) {
val position = recoverQueue.poll() ?: return
recyclerView.adapter?.notifyItemChanged(position)
}
}
private fun drawButtons(
canvas: Canvas,
buttons: List<UnderlayButton>,
itemView: View,
dX: Float
) {
var right = itemView.right
buttons.forEach { button ->
val width = button.intrinsicWidth / buttons.intrinsicWidth() * abs(dX)
val left = right - width
button.draw(
canvas,
RectF(left, itemView.top.toFloat(), right.toFloat(), itemView.bottom.toFloat())
)
right = left.toInt()
}
}
private fun drawButtonsRight(
canvas: Canvas,
buttons: List<UnderlayButton>,
itemView: View,
dX: Float
) {
var left = itemView.left
buttons.forEach { button ->
val width = button.intrinsicWidth / buttons.intrinsicWidthRight() * abs(dX)
val right = width
button.draw(
canvas,
RectF(left.toFloat(), itemView.top.toFloat(), right, itemView.bottom.toFloat())
)
left = right.toInt()
}
}
override fun onChildDraw(
c: Canvas,
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
dX: Float,
dY: Float,
actionState: Int,
isCurrentlyActive: Boolean
) {
val position = viewHolder.adapterPosition
var maxDX = dX
val itemView = viewHolder.itemView
if (actionState == ItemTouchHelper.ACTION_STATE_SWIPE) {
if (dX < 0) {
if (!buttonsBuffer.containsKey(position)) {
buttonsBuffer[0] = instantiateUnderlayButton(position)
}
val buttons = buttonsBuffer[position] ?: return
if (buttons.isEmpty()) return
maxDX = max(-buttons.intrinsicWidth(), dX)
drawButtons(c, buttons, itemView, maxDX)
}
if(dX > 0) {
if (!buttonsBuffer.containsKey(position)) {
buttonsBuffer[1] = instantiateUnderlayButtonRight(position)
}
val buttons = buttonsBuffer[position] ?: return
if (buttons.isEmpty()) return
maxDX = buttons.intrinsicWidthRight()
drawButtonsRight(c, buttons, itemView, maxDX)
}
}
super.onChildDraw(
c,
recyclerView,
viewHolder,
maxDX,
dY,
actionState,
isCurrentlyActive
)
}
override fun onMove(
recyclerView: RecyclerView,
viewHolder: RecyclerView.ViewHolder,
target: RecyclerView.ViewHolder
): Boolean {
return false
}
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
val position = viewHolder.adapterPosition
if (swipedPosition != position) recoverQueue.add(swipedPosition)
swipedPosition = position
recoverSwipedItem()
}
abstract fun instantiateUnderlayButton(position: Int): List<UnderlayButton>
abstract fun instantiateUnderlayButtonRight(position: Int): List<UnderlayButton>
//region UnderlayButton
interface UnderlayButtonClickListener {
fun onClick()
}
class UnderlayButton(
private val context: Context,
textSize: Float,
private val bitmap: Bitmap,
@ColorRes private val colorRes: Int,
private val clickListener: UnderlayButtonClickListener
) {
private var clickableRegion: RectF? = null
private val textSizeInPixel: Float = textSize * context.resources.displayMetrics.density // dp to px
private val horizontalPadding = 100.0f
val intrinsicWidth: Float
init {
val titleBounds = Rect()
intrinsicWidth = titleBounds.width() + 2 * horizontalPadding
}
fun draw(canvas: Canvas, rect: RectF) {
val paint = Paint()
// Draw background
paint.color = ContextCompat.getColor(context, colorRes)
canvas.drawRect(rect, paint)
// Draw title
paint.color = ContextCompat.getColor(context, android.R.color.white)
paint.textSize = textSizeInPixel
paint.typeface = Typeface.DEFAULT_BOLD
paint.textAlign = Paint.Align.LEFT
val spaceHeight = 10f // change to whatever you deem looks better
val bounds = Rect()
val combinedHeight = bitmap.height + spaceHeight + bounds.height()
canvas.drawBitmap(
bitmap,
rect.centerX() - bitmap.width / 2,
rect.centerY() - combinedHeight / 2,
null
)
clickableRegion = rect
}
fun handle(event: MotionEvent) {
clickableRegion?.let {
if (it.contains(event.x, event.y)) {
clickListener.onClick()
}
}
}
}
//endregion
}
private fun List<SwipeHelper.UnderlayButton>.intrinsicWidth(): Float {
if (isEmpty()) return 0.0f
return map { it.intrinsicWidth }.reduce { acc, fl -> acc + fl }
}
private fun List<SwipeHelper.UnderlayButton>.intrinsicWidthRight(): Float {
if (isEmpty()) return 0.0f
return map { it.intrinsicWidth }.reduce { acc, fl -> acc + fl }
}
我觉得我很接近,但不确定我哪里出错了。任何帮助将不胜感激谢谢
解决方案
推荐阅读
- c++ - “变量”声明为“内联”字段
- c++ - 从范围内的数组中删除元素
- javascript - 如何自动分页到for循环?
- react-native - Passing Local Image To a Class Component - React Native
- c++ - In gtkmm, How to imply function which can create a new window when detach a notebook tab?
- .net-core - One to many with optional parent
- dbt - yml 文件的 DBT 问题:profiles.yml 中的配置文件 my-bigquery-db 为空
- python - Synchronising writes into AmazonDB using Python
- java - 加入 JPA 查询——spring boot
- rust - 指定 Rust 闭包的生命周期