横向RecyclerView:选中项移出视口时切换左右边缘项背景
横向RecyclerView滚动提示实现方案
核心问题解答
findLastCompletelyVisibleItemPosition()完全可以用来获取最右侧完全可见的条目位置,对应的findFirstCompletelyVisibleItemPosition()则用于获取最左侧完全可见的条目位置。注意这两个方法仅适用于LinearLayoutManager(需配置为横向模式),其他LayoutManager需调整逻辑。
获取最左/最右侧条目位置的两种场景
- 完全可见条目:使用
linearLayoutManager.findFirstCompletelyVisibleItemPosition()(最左)、linearLayoutManager.findLastCompletelyVisibleItemPosition()(最右),仅返回完全显示在视口内的条目位置。 - 包含部分可见的条目:如果需要把仅露出一部分的条目算作边缘,改用
findFirstVisibleItemPosition()和findLastVisibleItemPosition(),这两个方法会返回视口中第一个/最后一个哪怕只显示1px的条目位置。
完整实现步骤
- 保存选中项位置:用变量记录当前选中的条目索引,比如
var selectedPosition = 0。 - 添加滚动监听:给RecyclerView注册
OnScrollListener,在滚动时实时判断选中项是否在视口外,并更新边缘条目的背景。 - Adapter中处理背景切换:通过Adapter的方法控制指定条目的背景样式,避免全局刷新影响性能。
代码示例
滚动监听部分
// 初始化横向LinearLayoutManager val linearLayoutManager = LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false) recyclerView.layoutManager = linearLayoutManager var selectedPosition = 0 // 初始选中位置 recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) val firstFullVisible = linearLayoutManager.findFirstCompletelyVisibleItemPosition() val lastFullVisible = linearLayoutManager.findLastCompletelyVisibleItemPosition() // 先重置之前的提示背景 (recyclerView.adapter as ItemAdapter).resetHintBackground() // 判断选中项位置并设置提示 if (selectedPosition > lastFullVisible && lastFullVisible != RecyclerView.NO_POSITION) { // 选中项在右侧,给最右完全可见条目加提示背景 (recyclerView.adapter as ItemAdapter).setEdgeHint(lastFullVisible, isRight = true) } else if (selectedPosition < firstFullVisible && firstFullVisible != RecyclerView.NO_POSITION) { // 选中项在左侧,给最左完全可见条目加提示背景 (recyclerView.adapter as ItemAdapter).setEdgeHint(firstFullVisible, isRight = false) } } })
Adapter部分
class ItemAdapter(private val data: List<String>) : RecyclerView.Adapter<ItemAdapter.ViewHolder>() { private var hintPosition = -1 private var isRightHint = false // 重置提示背景 fun resetHintBackground() { val oldPosition = hintPosition hintPosition = -1 if (oldPosition != -1) notifyItemChanged(oldPosition) } // 设置边缘提示 fun setEdgeHint(position: Int, isRight: Boolean) { hintPosition = position isRightHint = isRight notifyItemChanged(position) } override fun onBindViewHolder(holder: ViewHolder, position: Int) { holder.tvContent.text = data[position] // 根据提示状态设置背景 holder.itemView.setBackgroundColor( if (position == hintPosition) { // 区分左右提示的颜色,比如右侧用浅蓝,左侧用浅黄 if (isRightHint) Color.parseColor("#E3F2FD") else Color.parseColor("#FFF3E0") } else { Color.WHITE // 默认背景 } ) } inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { val tvContent: TextView = itemView.findViewById(R.id.tv_content) } override fun getItemCount() = data.size override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { val view = LayoutInflater.from(parent.context).inflate(R.layout.item_layout, parent, false) return ViewHolder(view) } }
注意事项
- 如果条目宽度不固定,部分可见的边缘条目也需要触发提示,可以把
findFirstCompletelyVisibleItemPosition()替换成findFirstVisibleItemPosition(),同理最右侧用findLastVisibleItemPosition()。 - 滚动监听中优先使用
onScrolled()方法,它会在每次滚动时触发,保证提示的实时性;onScrollStateChanged()仅在滚动状态改变时触发,无法实时更新。 - 避免使用
notifyDataSetChanged()刷新全部条目,用notifyItemChanged()只更新需要改变背景的条目,提升性能。
内容的提问来源于stack exchange,提问作者astro.zombi
相关产品推荐
相关产品推荐

