Android开发:在RecyclerView适配器中判断GIF项是否完全可见及失焦
嘿,这个需求挺实用的,刚好我之前做过类似的功能,给你分享下具体实现思路和代码~
实现RecyclerView中GIF的可见性触发加载/停止
核心思路是通过RecyclerView滚动监听+LayoutManager的可见性判断方法,跟踪每个GIF item的可见状态,进而精准控制加载和停止逻辑。以下是具体步骤:
1. 自定义滚动监听器:实时跟踪完全可见的Item
我们需要一个OnScrollListener,在滚动过程中(包括滚动状态变化、滚动位移时),实时获取当前完全可见的Item范围,然后通知对应的ViewHolder启动/停止GIF加载。
class GifScrollListener( private val recyclerView: RecyclerView, private val adapter: GifAdapter ) : RecyclerView.OnScrollListener() { override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { super.onScrollStateChanged(recyclerView, newState) // 不管是滚动中、停止还是拖拽状态,都检查可见状态 updateGifLoadingState() } override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { super.onScrolled(recyclerView, dx, dy) updateGifLoadingState() } private fun updateGifLoadingState() { val layoutManager = recyclerView.layoutManager as LinearLayoutManager val firstFullVisiblePos = layoutManager.findFirstCompletelyVisibleItemPosition() val lastFullVisiblePos = layoutManager.findLastCompletelyVisibleItemPosition() // 没有完全可见的Item时直接返回 if (firstFullVisiblePos == -1 || lastFullVisiblePos == -1) return // 给所有完全可见的Item启动GIF加载 for (pos in firstFullVisiblePos..lastFullVisiblePos) { recyclerView.findViewHolderForAdapterPosition(pos)?.let { holder -> (holder as GifAdapter.GifViewHolder).startGifLoading() } } // 停止那些之前在加载但现在不再完全可见的Item val iterator = adapter.currentLoadingPositions.iterator() while (iterator.hasNext()) { val pos = iterator.next() if (pos < firstFullVisiblePos || pos > lastFullVisiblePos) { recyclerView.findViewHolderForAdapterPosition(pos)?.let { holder -> (holder as GifAdapter.GifViewHolder).stopGifLoading() } iterator.remove() } } } }
2. 适配器与ViewHolder:封装加载逻辑
在Adapter中维护一个集合记录当前正在加载的Item位置,同时在ViewHolder中实现启动/停止加载的方法,还要处理ViewHolder回收时的资源释放,避免内存泄漏。
class GifAdapter(private val gifUrls: List<String>) : RecyclerView.Adapter<GifAdapter.GifViewHolder>() { // 记录正在加载GIF的Item位置,避免重复操作 val currentLoadingPositions = mutableSetOf<Int>() inner class GifViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { private val gifImageView: ImageView = itemView.findViewById(R.id.gif_image) // 这里以Glide为例,替换成你实际使用的加载库即可 private var glideRequest: RequestBuilder<Drawable>? = null fun bind(position: Int) { // 初始设置占位图,不加载GIF gifImageView.setImageResource(R.drawable.gif_placeholder) } fun startGifLoading() { val position = adapterPosition if (currentLoadingPositions.contains(position)) return val gifUrl = gifUrls[position] // 启动GIF加载 glideRequest = Glide.with(itemView.context) .asGif() .load(gifUrl) .into(gifImageView) currentLoadingPositions.add(position) } fun stopGifLoading() { // 停止GIF加载并重置占位图 glideRequest?.clear(gifImageView) gifImageView.setImageResource(R.drawable.gif_placeholder) currentLoadingPositions.remove(adapterPosition) } // ViewHolder被回收时,必须停止加载 override fun onViewRecycled() { super.onViewRecycled() stopGifLoading() } } override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): GifViewHolder { val itemView = LayoutInflater.from(parent.context) .inflate(R.layout.item_gif, parent, false) return GifViewHolder(itemView) } override fun onBindViewHolder(holder: GifViewHolder, position: Int) { holder.bind(position) // 绑定的时候如果Item已经完全可见,直接启动加载 val layoutManager = (holder.itemView.parent as RecyclerView).layoutManager as LinearLayoutManager if (layoutManager.isViewCompletelyVisible(holder.itemView)) { holder.startGifLoading() } } override fun getItemCount() = gifUrls.size }
3. 初始化RecyclerView:绑定监听器
在你的Activity或Fragment中,给RecyclerView设置适配器和滚动监听器即可:
val recyclerView = findViewById<RecyclerView>(R.id.rv_gif_list) val gifUrls = listOf("你的GIF链接1", "你的GIF链接2", "...") val adapter = GifAdapter(gifUrls) recyclerView.apply { this.adapter = adapter layoutManager = LinearLayoutManager(this@MainActivity) addOnScrollListener(GifScrollListener(this, adapter)) }
额外注意事项
- 如果使用
GridLayoutManager,只需要把代码中的LinearLayoutManager替换成GridLayoutManager即可,它同样提供完全可见位置的判断方法。 - 如果你用的是其他GIF加载库(比如Picasso、Fresco),只需要替换
startGifLoading()和stopGifLoading()中的逻辑,确保库支持暂停/恢复加载。 - 若需要调整“触发加载”的可见度阈值(比如Item显示70%以上才加载),可以自定义可见区域占比的计算方法,替换默认的完全可见判断。
内容的提问来源于stack exchange,提问作者Hiren Shah
相关产品推荐
相关产品推荐

