RecyclerView中CountDownTimer切换ViewType后闪烁问题解决咨询
解决CountDownTimer切换ViewType时的闪烁问题
问题根源
闪烁的核心原因有两个:
- RecyclerView复用ViewHolder时,旧的CountDownTimer未被取消,新Timer和旧Timer同时更新同一个TextView,导致数值跳变。
- 每次绑定ViewHolder时,没有先设置当前实时的倒计时数值,而是等Timer第一次
onTick触发(延迟1秒),期间会显示之前的旧值,造成闪烁。
具体修复步骤
1. 给ViewHolder添加Timer引用并管理生命周期
在ExamListV2ActiveItemViewHolder里添加成员变量保存当前Timer,绑定前先取消旧Timer:
public class ExamListV2ActiveItemViewHolder extends RecyclerView.ViewHolder { public final ItemBinding binding; public CountDownTimer currentTimer; // 保存当前运行的Timer public ExamListV2ActiveItemViewHolder(ItemBinding binding) { super(binding.getRoot()); this.binding = binding; } }
2. 绑定ViewHolder时先取消旧Timer,再设置实时数值
修改绑定逻辑,先取消旧Timer,计算当前剩余时间并直接设置文本,最后启动新Timer:
// 先取消旧Timer,避免多个Timer同时更新UI if (holder.currentTimer != null) { holder.currentTimer.cancel(); holder.currentTimer = null; } long currentTimeMillis = System.currentTimeMillis(); long timeUntilStart = startDate.getTime() - currentTimeMillis; long timeUntilEnd = endDate.getTime() - currentTimeMillis; if (timeUntilStart > 0) { // 先设置当前实时的倒计时数值,避免等待onTick的1秒延迟 long seconds = (timeUntilStart / 1000) % 60; long minutes = (timeUntilStart / (1000 * 60)) % 60; long hours = (timeUntilStart / (1000 * 60 * 60)); holder.binding.tvRemainingTime.setText(String.format("%02d Hour: %02d min :%02d sec", hours, minutes, seconds)); // 创建并启动新Timer,同时保存到ViewHolder holder.currentTimer = new CountDownTimer(timeUntilStart, 1000) { @Override public void onTick(long millisUntilFinished) { long sec = (millisUntilFinished / 1000) % 60; long min = (millisUntilFinished / (1000 * 60)) % 60; long hr = (millisUntilFinished / (1000 * 60 * 60)); holder.binding.tvRemainingTime.setText(String.format("%02d Hour: %02d min :%02d sec", hr, min, sec)); } @Override public void onFinish() { holder.binding.btnJoin.setVisibility(View.VISIBLE); holder.binding.tvStarts.setText(context.getResources().getString(R.string.exam_starts_in)); // 先设置结束阶段的实时数值 long secEnd = (timeUntilEnd / 1000) % 60; long minEnd = (timeUntilEnd / (1000 * 60)) % 60; long hrEnd = (timeUntilEnd / (1000 * 60 * 60)); holder.binding.tvRemainingTime.setText(String.format("%02d Hour %02d Min %02d Sec", hrEnd, minEnd, secEnd)); // 创建结束阶段的Timer,保存到ViewHolder holder.currentTimer = new CountDownTimer(timeUntilEnd, 1000) { @Override public void onTick(long millisUntilFinished) { long sec = (millisUntilFinished / 1000) % 60; long min = (millisUntilFinished / (1000 * 60)) % 60; long hr = (millisUntilFinished / (1000 * 60 * 60)); holder.binding.tvRemainingTime.setText(String.format("%02d Hour %02d Min %02d Sec", hr, min, sec)); } @Override public void onFinish() { holder.binding.tvRemainingTime.setText(context.getResources().getString(R.string.exam_ended)); holder.currentTimer = null; // 结束后清空引用 } }; holder.currentTimer.start(); } }; holder.currentTimer.start(); }
3. 回收ViewHolder时取消Timer
在RecyclerView.Adapter里重写onViewRecycled方法,确保ViewHolder被回收时取消Timer:
@Override public void onViewRecycled(@NonNull RecyclerView.ViewHolder holder) { super.onViewRecycled(holder); if (holder instanceof ExamListV2ActiveItemViewHolder) { ExamListV2ActiveItemViewHolder activeHolder = (ExamListV2ActiveItemViewHolder) holder; if (activeHolder.currentTimer != null) { activeHolder.currentTimer.cancel(); activeHolder.currentTimer = null; } } }
额外注意点
- 直接用
System.currentTimeMillis()获取当前时间,比创建Date对象更高效。 - 确保
startDate和endDate是有效时间戳,避免计算出负数导致Timer异常。
内容的提问来源于stack exchange,提问作者Shahriar
相关产品推荐
相关产品推荐

