You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android:无需第三方库实现RecyclerView类Tinder滑动卡片及回调

Android RecyclerView实现无第三方库的Tinder式滑动卡片(带动画)

核心思路

利用RecyclerView自带的ItemTouchHelper处理滑动手势,自定义ItemTouchHelper.Callback实现Tinder风格的滑动动画(倾斜+位移),同时通过自定义接口回调传递卡片出现/消失的位置、滑动方向,滑动完成后自动移除数据并刷新列表。

具体实现步骤

1. 定义滑动回调接口

用于传递卡片状态信息:

interface CardSwipeCallback {
    // 返回即将显示的卡片位置(滑动后露出的下一张)
    fun onCardAppear(position: Int)
    // 返回被滑走的卡片位置
    fun onCardDisappear(position: Int)
    // 返回当前滑动方向:ItemTouchHelper.LEFT / ItemTouchHelper.RIGHT
    fun onCardSwipeDirection(direction: Int)
}

2. 自定义ItemTouchHelper.Callback

处理滑动逻辑、动画及回调触发:

class TinderSwipeCallback(
    private val adapter: RecyclerView.Adapter<*>,
    private val callback: CardSwipeCallback
) : ItemTouchHelper.Callback() {

    override fun getMovementFlags(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder
    ): Int {
        // 仅允许左右滑动
        val swipeFlags = ItemTouchHelper.LEFT or ItemTouchHelper.RIGHT
        return makeMovementFlags(0, swipeFlags)
    }

    override fun onMove(
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        target: RecyclerView.ViewHolder
    ): Boolean = false // 不需要拖拽移动

    override fun onChildDraw(
        c: Canvas,
        recyclerView: RecyclerView,
        viewHolder: RecyclerView.ViewHolder,
        dX: Float,
        dY: Float,
        actionState: Int,
        isCurrentlyActive: Boolean
    ) {
        // 添加滑动倾斜动画,模拟Tinder卡片的倾斜效果
        val itemView = viewHolder.itemView
        val rotation = dX * 0.1f // 倾斜角度随滑动距离线性变化
        itemView.rotation = rotation

        // 保留系统默认的滑动位移逻辑
        super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive)
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        val position = viewHolder.adapterPosition
        // 触发消失卡片的回调
        callback.onCardDisappear(position)
        // 触发滑动方向回调
        callback.onCardSwipeDirection(direction)

        // 移除数据并刷新列表
        (adapter as CardAdapter).removeItem(position)

        // 若还有剩余卡片,触发下一张卡片的出现回调
        if (adapter.itemCount > 0) {
            callback.onCardAppear(0) // 堆叠布局下,下一张卡片始终在position 0
        }
    }

    override fun clearView(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder) {
        super.clearView(recyclerView, viewHolder)
        // 滑动结束后重置卡片旋转角度
        viewHolder.itemView.rotation = 0f
    }
}

3. 实现RecyclerView Adapter

处理数据绑定与移除逻辑:

class CardAdapter(private val dataList: MutableList<String>) : RecyclerView.Adapter<CardAdapter.CardViewHolder>() {

    inner class CardViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val cardContent: TextView = itemView.findViewById(R.id.tv_card_content)
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): CardViewHolder {
        val view = LayoutInflater.from(parent.context)
            .inflate(R.layout.item_card, parent, false)
        return CardViewHolder(view)
    }

    override fun onBindViewHolder(holder: CardViewHolder, position: Int) {
        holder.cardContent.text = dataList[position]
    }

    override fun getItemCount(): Int = dataList.size

    fun removeItem(position: Int) {
        dataList.removeAt(position)
        notifyItemRemoved(position)
    }
}

4. 界面配置与回调实现

在Activity/Fragment中完成RecyclerView初始化、绑定ItemTouchHelper,并实现回调接口:

class MainActivity : AppCompatActivity(), CardSwipeCallback {

    private lateinit var recyclerView: RecyclerView
    private lateinit var adapter: CardAdapter
    private val dataList = mutableListOf("卡片1", "卡片2", "卡片3", "卡片4", "卡片5")

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        recyclerView = findViewById(R.id.recycler_view)
        // 自定义LayoutManager,禁止垂直滑动,模拟堆叠效果
        recyclerView.layoutManager = object : LinearLayoutManager(this) {
            override fun canScrollVertically(): Boolean = false
        }
        adapter = CardAdapter(dataList)
        recyclerView.adapter = adapter

        // 绑定ItemTouchHelper
        val swipeCallback = TinderSwipeCallback(adapter, this)
        ItemTouchHelper(swipeCallback).attachToRecyclerView(recyclerView)

        // 初始触发第一张卡片的出现回调
        if (dataList.isNotEmpty()) {
            onCardAppear(0)
        }
    }

    override fun onCardAppear(position: Int) {
        // 这里可添加卡片出现的业务逻辑,比如日志、状态更新
        println("出现的卡片位置:$position")
    }

    override fun onCardDisappear(position: Int) {
        // 这里可添加卡片消失的业务逻辑
        println("消失的卡片位置:$position")
    }

    override fun onCardSwipeDirection(direction: Int) {
        val directionStr = if (direction == ItemTouchHelper.LEFT) "左滑" else "右滑"
        println("当前卡片滑动方向:$directionStr")
    }
}

5. 布局优化(堆叠效果)

在item_card.xml中给CardView设置负top margin,让下一张卡片露出顶部边缘,模拟堆叠:

<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginLeft="16dp"
    android:layout_marginRight="16dp"
    android:layout_marginTop="-20dp" <!-- 负margin实现堆叠 -->
    android:layout_marginBottom="16dp"
    android:elevation="8dp">

    <TextView
        android:id="@+id/tv_card_content"
        android:layout_width="match_parent"
        android:layout_height="300dp"
        android:gravity="center"
        android:textSize="24sp"/>

</androidx.cardview.widget.CardView>

注意事项

  • 若需要更细腻的动画,可在onChildDraw中添加透明度变化(itemView.alpha = 1 - abs(dX) / recyclerView.width)
  • 可通过修改rotation的系数调整卡片倾斜程度,适配不同设计需求
  • 若卡片数量较多,建议优化RecyclerView的回收机制,避免内存泄漏

内容的提问来源于stack exchange,提问作者Hassan Raza

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.15 14:15:39