You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android RecyclerView列表适配器移动动画卡顿问题咨询

问题描述

当列表项视图被同步更新时,RecyclerView的移动动画出现卡顿,请问这是什么原因?

相关代码

观察文件列表并更新Adapter的代码:

fileViewModel.getAllfiles().observe(this, new Observer<List<File>>() {
   @Override
   public void onChanged(List<File> files) {
      adapter.submitList(files);
   }
});

下载进度更新逻辑(修改数据库触发上述Observer):

executor = Executors.newFixedThreadPool(1);
executor.execute(() -> {
   fileRecycler.post(new Runnable() {
      @Override
      public void run () {
         fileViewModel.setDownloadProgress(file.getFileId(), percentage);
      }
   });
});

现象细节

  • 仅正在更新下载进度的列表项出现移动动画卡顿
  • 进度更新间隔较大时动画缓慢,更新频繁时动画完全停滞,直到进度更新结束
  • 移除ViewHolder中的代码holder.view.determinateBar.setProgress(file.getDownloadProgress(), true);后,移动动画不再卡顿

问题原因与解决方案

核心原因

  1. 全量刷新抢占主线程资源:每次更新进度都会触发submitList(files),该方法会执行DiffUtil全量对比计算——哪怕只有一个Item的进度变化。频繁的Diff计算会占用主线程大量资源,导致RecyclerView的移动动画帧被阻塞。
  2. 双重动画资源冲突:determinateBar.setProgress(..., true)会触发进度条自身的属性动画,这个动画同样在主线程执行,和RecyclerView的Item移动动画抢占同一线程资源,双重消耗直接导致卡顿甚至停滞。

解决方案

方案1:局部更新单个Item,避免全量刷新

放弃每次进度变化都调用submitList的方式,改为直接定位目标Item并局部刷新:

  • 在ViewModel中新增进度更新的LiveData,只传递变化的文件ID和进度值
  • UI层监听该LiveData,找到对应Item在列表中的位置后,调用带payload的局部刷新方法

示例代码:

// ViewModel部分
private MutableLiveData<ProgressUpdate> _progressUpdateLiveData = new MutableLiveData<>();
public LiveData<ProgressUpdate> getProgressUpdateLiveData() {
    return _progressUpdateLiveData;
}

public void setDownloadProgress(String fileId, int percentage) {
    // 数据库更新逻辑...
    _progressUpdateLiveData.postValue(new ProgressUpdate(fileId, percentage));
}

// 自定义进度更新实体类
public static class ProgressUpdate {
    public String fileId;
    public int percentage;
    public ProgressUpdate(String fileId, int percentage) {
        this.fileId = fileId;
        this.percentage = percentage;
    }
}

// UI层监听
fileViewModel.getProgressUpdateLiveData().observe(this, update -> {
    int position = findFilePositionById(update.fileId); // 实现根据ID找列表位置的逻辑
    if (position != -1) {
        // 只更新进度相关内容,不重绘整个Item
        adapter.notifyItemChanged(position, "PROGRESS_UPDATE");
    }
});

// Adapter重写局部刷新逻辑
@Override
public void onBindViewHolder(FileViewHolder holder, int position, List<Object> payloads) {
    if (!payloads.isEmpty() && "PROGRESS_UPDATE".equals(payloads.get(0))) {
        File file = getItem(position);
        holder.view.determinateBar.setProgress(file.getDownloadProgress(), true);
        return;
    }
    // 正常绑定整个Item的逻辑
    super.onBindViewHolder(holder, position, payloads);
}

方案2:优化进度条动画执行逻辑

  • 关闭进度条的自带动画,调用setProgress(percentage, false)直接设置进度值,减少主线程动画计算
  • 或者使用postOnAnimation延迟进度条动画,避开RecyclerView移动动画的执行时机

方案3:控制进度更新频率

设置更新阈值,比如进度变化超过1%或间隔500ms才触发一次更新,减少不必要的刷新操作。


内容的提问来源于stack exchange,提问作者esseestpercipi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.07 14:25:12