解决RecyclerView滚动时TextView与ImageView颜色显示异常问题
解决RecyclerView滚动时TextView和图标颜色错乱的问题
你遇到的问题是RecyclerViewViewHolder复用机制导致的状态错乱,加上当前的状态存储方式(单独的toggledChoices数组)不够可靠,再加上重复创建Drawable的操作可能引发的状态残留,最终导致滚动时颜色显示错误。下面是具体的解决方案:
核心问题分析
- ViewHolder复用残留:RecyclerView会回收滑出屏幕的ViewHolder并重新绑定给新的数据,如果没有在每次绑定的时候完全重置所有UI状态,就会保留之前的颜色设置。
- 状态存储不可靠:单独的
toggledChoices数组和displayedBuses数据列表可能不同步(比如列表过滤、更新时数组长度没跟上),导致状态判断错误。 - Drawable重复创建与共享:每次绑定都重新获取Drawable并设置颜色滤镜,没有调用
mutate()会导致Drawable实例共享,颜色设置互相干扰。
具体修复步骤
1. 将状态与数据实体绑定(替代单独数组)
给BusFromToResponse添加一个isNear字段,把状态直接存在数据项里,避免数组和数据不同步的问题:
// 假设你的BusFromToResponse类,添加以下字段和方法 private boolean isNear; public boolean isNear() { return isNear; } public void setNear(boolean near) { isNear = near; }
2. 优化时间计算逻辑(避免过时API与错误)
原来的时间计算使用了过时的getHours()、getMinutes()方法,改用Calendar的标准API,同时简化时间差计算:
@Override public void onBindViewHolder(View_Holder holder, int position) { BusFromToResponse bus = displayedBuses.get(position); Calendar currentCal = Calendar.getInstance(TimeZone.getTimeZone("Asia/Tehran")); int currentHour = currentCal.get(Calendar.HOUR_OF_DAY); int currentMinute = currentCal.get(Calendar.MINUTE); // 解析出发时间 String departureTimeStr = bus.getDepartureDate().substring( bus.getDepartureDate().indexOf("T") + 1, bus.getDepartureDate().indexOf("Z") - 3 ); String[] depHourMin = departureTimeStr.split(":"); int depHour = Integer.parseInt(depHourMin[0]); int depMin = Integer.parseInt(depHourMin[1]); // 计算30分钟后的基准时间(处理进位) int baseMin = depMin + 30; int baseHour = depHour; if (baseMin >= 60) { baseMin -= 60; baseHour += 1; } if (baseHour >= 24) { baseHour -= 24; } // 调用工具类判断时间差,注意要确保DateUtil.IsBigger30Min逻辑正确(考虑跨天) boolean isNear = DateUtil.IsBigger30Min( String.format("%02d:%02d", baseHour, baseMin), String.format("%02d:%02d", currentHour, currentMinute), str_date ); // 将状态绑定到数据实体 bus.setNear(isNear); // 设置显示时间 holder.time.setText(String.format("%02d : %02d", baseHour, baseMin)); // 绑定其他UI状态 holder.onBind(bus, position); // 设置行背景色 holder.rootView.setBackgroundColor(context.getResources().getColor( position % 2 == 0 ? R.color.ticket_row1 : R.color.ticket_row2 )); }
3. 缓存Drawable实例,避免重复创建与共享
在Adapter中提前初始化两种颜色的时钟图标,调用mutate()确保每个Drawable是独立实例:
public class BusTicketAdapter extends RecyclerView.Adapter<BusTicketAdapter.View_Holder> { // 添加成员变量缓存Drawable private Drawable yellowClockIcon; private Drawable whiteClockIcon; // ...其他成员变量 public BusTicketAdapter(List<BusFromToResponse> availableBuses, Context context, String str_date, TicketListener listener) { // ...原有初始化代码 // 初始化缓存的Drawable yellowClockIcon = ContextCompat.getDrawable(context, R.drawable.clock).mutate(); yellowClockIcon.setColorFilter(ContextCompat.getColor(context, R.color.yellow), PorterDuff.Mode.SRC_IN); whiteClockIcon = ContextCompat.getDrawable(context, R.drawable.clock).mutate(); whiteClockIcon.setColorFilter(ContextCompat.getColor(context, R.color.white), PorterDuff.Mode.SRC_IN); } // ...其他方法 }
4. 完善ViewHolder的onBind方法,确保完全重置UI状态
在onBind中直接使用数据实体的isNear状态,避免依赖不可靠的数组:
void onBind(BusFromToResponse bus, int position) { if (!bus.isNear()) { time.setTextColor(ContextCompat.getColor(context, R.color.yellow)); clockIc.setImageDrawable(yellowClockIcon); } else { time.setTextColor(ContextCompat.getColor(context, R.color.white)); clockIc.setImageDrawable(whiteClockIcon); } }
额外注意事项
- 检查
DateUtil.IsBigger30Min的实现:确保它能正确处理跨天的时间差(比如基准时间是次日00:10,当前时间是23:50,此时时间差是20分钟,应该返回false,即显示黄色)。 - 避免使用过时API:
getHours()、getMinutes()已经被废弃,统一使用Calendar或LocalDateTime(API 26+)来处理时间。
内容的提问来源于stack exchange,提问作者BeginnerProgrammer
相关产品推荐
相关产品推荐

