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

如何在RecyclerView中实现仿通知栏的点击及上下滑动展开收起效果

Hey there! Let me share a practical approach to implement the expand/collapse functionality for your RecyclerView notification items via vertical swipe and click—since you mentioned the previous solution didn’t meet your expectations.

Implementation Steps

1. Update Your Data Model

First, add a state flag to your notification data class to track whether an item is expanded or collapsed:

data class NotificationItem(
    val id: String,
    val title: String,
    val fullContent: String,
    var isExpanded: Boolean = false // Tracks expansion state
)

2. Handle Vertical Swipe with ItemTouchHelper

Use ItemTouchHelper to intercept vertical swipe gestures and trigger state changes once a swipe threshold is met:

class VerticalSwipeHandler(private val swipeCallback: SwipeCallback) : ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.UP or ItemTouchHelper.DOWN) {

    interface SwipeCallback {
        fun onItemSwiped(position: Int)
    }

    override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
        return false // We don't need drag-and-drop sorting here
    }

    override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
        // Trigger state toggle regardless of swipe direction (up/down)
        swipeCallback.onItemSwiped(viewHolder.adapterPosition)
    }

    override fun getSwipeThreshold(viewHolder: RecyclerView.ViewHolder): Float {
        // Set a threshold (e.g., 30% of item height) to trigger the swipe action
        return 0.3f
    }
}

3. Build the Adapter with State Logic

In your RecyclerView Adapter, bind the state to the UI and handle both click and swipe-triggered state changes:

class NotificationAdapter(private val notifications: MutableList<NotificationItem>) : RecyclerView.Adapter<NotificationAdapter.NotificationViewHolder>() {

    inner class NotificationViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
        val titleTv: TextView = itemView.findViewById(R.id.tv_notification_title)
        val contentTv: TextView = itemView.findViewById(R.id.tv_notification_content)
        val rootLayout: ConstraintLayout = itemView.findViewById(R.id.root_layout)
    }

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

    override fun onBindViewHolder(holder: NotificationViewHolder, position: Int) {
        val item = notifications[position]
        holder.titleTv.text = item.title
        holder.contentTv.text = item.fullContent

        // Update UI based on expansion state
        if (item.isExpanded) {
            holder.contentTv.visibility = View.VISIBLE
            // Use wrap content to show full content
            holder.rootLayout.layoutParams = RecyclerView.LayoutParams(
                RecyclerView.LayoutParams.MATCH_PARENT,
                RecyclerView.LayoutParams.WRAP_CONTENT
            )
        } else {
            holder.contentTv.visibility = View.GONE
            // Set fixed height to only show the title section
            val collapsedHeight = holder.titleTv.measuredHeight + holder.itemView.paddingTop + holder.itemView.paddingBottom
            holder.rootLayout.layoutParams = RecyclerView.LayoutParams(
                RecyclerView.LayoutParams.MATCH_PARENT,
                collapsedHeight
            )
        }

        // Toggle state on item click
        holder.itemView.setOnClickListener {
            toggleItemState(position)
        }
    }

    private fun toggleItemState(position: Int) {
        notifications[position].isExpanded = !notifications[position].isExpanded
        // Use local refresh to avoid full RecyclerView flicker
        notifyItemChanged(position)
    }

    // Expose method for swipe handler to trigger state toggle
    fun handleSwipe(position: Int) {
        toggleItemState(position)
    }

    override fun getItemCount() = notifications.size
}

4. Connect Everything in Your Activity/Fragment

Initialize the RecyclerView, Adapter, and bind the swipe handler:

val recyclerView = findViewById<RecyclerView>(R.id.recycler_notifications)
val notificationList = mutableListOf<NotificationItem>(
    // Populate your notification data here
)
val adapter = NotificationAdapter(notificationList)

recyclerView.adapter = adapter
recyclerView.layoutManager = LinearLayoutManager(this)

// Attach the vertical swipe handler
val swipeHandler = VerticalSwipeHandler(object : VerticalSwipeHandler.SwipeCallback {
    override fun onItemSwiped(position: Int) {
        adapter.handleSwipe(position)
    }
})
ItemTouchHelper(swipeHandler).attachToRecyclerView(recyclerView)

5. Optional Enhancements

  • Add Animations: Use ViewPropertyAnimator to add smooth fade/height transitions when expanding/collapsing items.
  • Resolve Swipe Conflicts: If your item contains scrollable views, override onInterceptTouchEvent in a custom RecyclerView to prioritize vertical swipe gestures.
  • Persist State: Store expansion states in a HashMap<String, Boolean> (mapping notification IDs to states) to retain state across configuration changes or pagination.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.09 17:52:33