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

Android中为RecyclerView每个Item添加进度条的实现方案咨询

解决RecyclerView复用下的下载进度条显示问题

作为Android开发新手遇到这个问题太正常了,RecyclerView的视图复用机制确实会让进度条这类和状态绑定的UI出现错乱,核心思路是把下载状态和进度数据绑定到你的Track模型类中,而不是只依赖ViewHolder里的视图控件,这样无论视图怎么复用,都能从数据源获取正确的状态来更新UI。下面是具体的实现步骤:

1. 改造Track数据类,添加下载状态字段

首先在你的Track类里增加几个字段,用来记录当前下载的核心状态:

public class Track {
    // 你的原有字段:name、artist、artUrl、downloadUrl等
    private boolean isDownloading; // 是否正在下载
    private int downloadProgress; // 当前下载进度(0-100)
    private boolean isDownloaded; // 是否已下载完成

    // 对应的getter和setter方法
    public boolean isDownloading() {
        return isDownloading;
    }

    public void setDownloading(boolean downloading) {
        isDownloading = downloading;
    }

    public int getDownloadProgress() {
        return downloadProgress;
    }

    public void setDownloadProgress(int downloadProgress) {
        this.downloadProgress = downloadProgress;
    }

    public boolean isDownloaded() {
        return isDownloaded;
    }

    public void setDownloaded(boolean downloaded) {
        isDownloaded = downloaded;
    }

    // 你的其他原有方法
}

2. 更新Adapter的onBindViewHolder方法,根据数据状态更新UI

在onBindViewHolder里,必须根据当前Track的状态来决定显示下载按钮还是进度条,以及设置进度值,这样复用ViewHolder时才会显示正确的状态:

@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    Track currentTrack = this.trackList.get(position);
    TrackListView trackListView = (TrackListView) holder;
    
    // 原有UI设置逻辑
    trackListView.title.setText(currentTrack.getName());
    trackListView.track.setText(currentTrack.getArtist());
    Picasso.get().load(currentTrack.getArtUrl())
            .resize(100,100)
            .into(trackListView.imageView);

    // 根据Track的状态更新下载相关UI
    if (currentTrack.isDownloaded()) {
        // 已下载:隐藏下载按钮和进度条,显示"已下载"提示
        trackListView.download.setVisibility(View.GONE);
        trackListView.progressBar.setVisibility(View.GONE);
        trackListView.progressText.setText("已下载");
    } else if (currentTrack.isDownloading()) {
        // 正在下载:隐藏下载按钮,显示进度条并设置进度
        trackListView.download.setVisibility(View.GONE);
        trackListView.progressBar.setVisibility(View.VISIBLE);
        trackListView.progressBar.setProgress(currentTrack.getDownloadProgress());
        trackListView.progressText.setText(currentTrack.getDownloadProgress() + "%");
    } else {
        // 未下载:显示下载按钮,隐藏进度条和提示文本
        trackListView.download.setVisibility(View.VISIBLE);
        trackListView.progressBar.setVisibility(View.GONE);
        trackListView.progressText.setText("");
    }
}

3. 在MainActivity的下载逻辑中更新Track状态并通知Adapter刷新

使用AndroidNetworking下载时,在进度回调里更新对应Track的进度值,然后调用notifyItemChanged(position)来刷新对应的Item(别用notifyDataSetChanged(),会全量刷新影响性能):

private void downloadMusic(Track selectedTrack, View cell) {
    int position = tracks.indexOf(selectedTrack);
    if (position == -1) return;

    // 先标记该Track为正在下载,立即刷新UI
    selectedTrack.setDownloading(true);
    trackListAdapter.notifyItemChanged(position);

    // 使用AndroidNetworking执行下载
    AndroidNetworking.download(selectedTrack.getDownloadUrl(), /* 你的文件保存路径 */)
            .build()
            .setDownloadProgressListener(new DownloadProgressListener() {
                @Override
                public void onProgress(long bytesDownloaded, long totalBytes) {
                    // 计算进度百分比
                    int progress = (int) ((bytesDownloaded * 100) / totalBytes);
                    // 更新Track的进度数据
                    selectedTrack.setDownloadProgress(progress);
                    // 只刷新当前下载的Item
                    trackListAdapter.notifyItemChanged(position);
                }
            })
            .startDownload(new DownloadListener() {
                @Override
                public void onDownloadComplete() {
                    // 下载完成,更新状态并刷新
                    selectedTrack.setDownloading(false);
                    selectedTrack.setDownloaded(true);
                    trackListAdapter.notifyItemChanged(position);
                    Toast.makeText(MainActivity.this, "下载完成", Toast.LENGTH_SHORT).show();
                }

                @Override
                public void onError(ANError error) {
                    // 下载失败,重置状态并刷新
                    selectedTrack.setDownloading(false);
                    trackListAdapter.notifyItemChanged(position);
                    Toast.makeText(MainActivity.this, "下载失败:" + error.getMessage(), Toast.LENGTH_SHORT).show();
                }
            });
}

4. 额外优化提示

  • 避免在异步回调里使用getAdapterPosition():因为ViewHolder可能已经被复用,位置会失效,建议像上面代码一样,在点击时就获取Item的位置或者直接操作对应的Track对象。
  • 确保视图状态重置:如果遇到极端的复用问题,可以在onBindViewHolder开头先统一隐藏下载相关控件,再根据状态显示,防止残留旧状态。

这样修改后,RecyclerView复用视图时,每个Item都会根据对应Track的真实状态来显示进度条或下载按钮,就不会出现错乱的问题了。

内容的提问来源于stack exchange,提问作者Manikandan V. Nair

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.28 07:31:31