Android RecyclerView启用SLIDER_VIEW时ITEM_VIEW项丢失冲突问题
问题现象
- 开启轮播滑块开关(
Config.ENABLE_RECENT_SLIDER=true),将SLIDER_VIEW放置在列表0号位置时,SLIDER_VIEW与ITEM_VIEW出现位置合并问题:ITEM_VIEW第一项无法正常展示,列表最后一项丢失。
- 关闭轮播滑块开关时,
ITEM_VIEW第一项可正常展示。
实现目标
列表顶部正常展示SLIDER_VIEW轮播组件,同时缺失的ITEM_VIEW首项正常渲染,所有列表项(广告项、轮播项、壁纸项)无丢失。
故障原因
核心错误集中在位置计算逻辑,共3处:
getItemCount()未将开启轮播时新增的SLIDER_VIEW计入总项数,总长度少1,直接导致列表最后一项被截断。getItemViewType()判断广告位、普通项位置时,未将轮播项占用的位置纳入偏移计算,位置0被轮播占用后,后续广告位判断、普通项索引全部错位,导致第一项壁纸项被覆盖。onBindViewHolder()中计算普通壁纸项对应mData索引时,未扣除轮播占用的位置,且使用Math.round(position / Config.NATIVE_ADS_PER_ITEM)计算广告偏移量逻辑错误,整数除法直接取整即可,四舍五入会导致广告位附近索引计算偏差。
修复代码
仅需修改3个核心方法,其余业务逻辑无需调整:
1. 修正总项数计算逻辑
@Override public int getItemCount() { if (mData == null) return 0; // 整数除法直接计算广告数量,无需四舍五入 int adCount = mData.size() / Config.NATIVE_ADS_PER_ITEM; int total = mData.size() + adCount; // 开启轮播时总长度+1,计入顶部轮播项 if (Config.ENABLE_RECENT_SLIDER) { total += 1; } return total; }
2. 修正项类型判断逻辑
@Override public int getItemViewType(int position) { // 开启轮播时0号位置固定为轮播项 if (position == 0 && Config.ENABLE_RECENT_SLIDER) { return SLIDER_VIEW; } // 扣除轮播占用的位置偏移后,再判断广告位 int adjustPos = Config.ENABLE_RECENT_SLIDER ? position - 1 : position; if ((adjustPos + 1) % Config.NATIVE_ADS_PER_ITEM == 0) { return AD_VIEW; } return ITEM_VIEW; }
3. 修正绑定阶段的数据源索引计算
@Override public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) { sharedPreferences = PreferenceManager.getDefaultSharedPreferences(mContext); status = sharedPreferences.getString("status", "free"); StaggeredGridLayoutManager.LayoutParams layoutParams = (StaggeredGridLayoutManager.LayoutParams) holder.itemView.getLayoutParams(); if (holder.getItemViewType() == ITEM_VIEW) { // 第一步:扣除轮播项的位置偏移 int adjustPos = Config.ENABLE_RECENT_SLIDER ? position - 1 : position; // 第二步:扣除前面已加载的广告项数量,得到壁纸数据在mData中的真实索引 int dataPos = adjustPos - (adjustPos / Config.NATIVE_ADS_PER_ITEM); ((MainViewHolder) holder).bindData(mData.get(dataPos), listener, position); } else if (holder.getItemViewType() == AD_VIEW) { // 原代码用&是按位与,这里改为逻辑与&& if (Config.ENABLE_ADS && status.equals("free")) { ((AdViewHolder) holder).bindAdData(); layoutParams.setFullSpan(true); } } else if (holder.getItemViewType() == SLIDER_VIEW) { layoutParams.setFullSpan(true); ((SliderViewHolder) holder).bindSliderData(); } }
可选优化
- 当前
SliderViewHolder每次绑定都会新建图片列表、重复添加页面监听、重复发起网络请求,上下滑动轮播项会重复加载,建议将轮播数据初始化、网络请求逻辑移至Adapter构造函数执行,bindSliderData()仅做视图绑定,避免重复请求和内存泄漏。 - 轮播使用的
Timer需要在RecyclerView回收、关联Activity/Fragment销毁时调用cancel()方法释放,避免内存泄漏。
内容的提问来源于stack exchange,提问作者Taiyab
相关产品推荐
相关产品推荐

