【问题标题】:Show Download progress in a Recyclerview in Android在 Android 的 Recyclerview 中显示下载进度
【发布时间】:2018-08-07 03:56:09
【问题描述】:

我目前正在开发可以下载视频文件的应用程序。我正在使用下载管理器下载文件,一切正常,因为文件正在正确下载,我可以在通知中看到下载进度。现在我想在另一个类(包含recyclerview)中显示那些下载文件的进度。那么我应该怎么做才能实现这个模块呢?

  String urlDownload = mUrl;
        String filename = mUrl.substring(mUrl.lastIndexOf('/') + 1);
        Log.i("onLoadResource()", "url = " + mUrl + "\n" + filename);
        DownloadManager.Request request = new DownloadManager.Request(Uri.parse(urlDownload));

    request.setDescription("Testando");
    request.setTitle("Download");
    request.allowScanningByMediaScanner();
    request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED);
    request.setDestinationInExternalPublicDir(Environment.DIRECTORY_DOWNLOADS, filename);

    final DownloadManager manager = (DownloadManager) getSystemService(Context.DOWNLOAD_SERVICE);

    final long downloadId = manager.enqueue(request);

    final ProgressBar mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);

    new Thread(new Runnable() {

        @Override
        public void run() {

            boolean downloading = true;

            while (downloading) {

                DownloadManager.Query q = new DownloadManager.Query();
                q.setFilterById(downloadId);

                Cursor cursor = manager.query(q);
                cursor.moveToFirst();
                int bytes_downloaded = cursor.getInt(cursor
                        .getColumnIndex(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR));
                int bytes_total = cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_TOTAL_SIZE_BYTES));

                if (cursor.getInt(cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)) == DownloadManager.STATUS_SUCCESSFUL) {
                    downloading = false;
                }

                final int dl_progress = (int) ((double)bytes_downloaded / (double)bytes_total * 100f);

                runOnUiThread(new Runnable() {

                    @Override
                    public void run() {

                        mProgressBar.setProgress((int) dl_progress);

                    }
                });


                cursor.close();
            }

        }
    }).start();
}

【问题讨论】:

  • 只是将下载百分比广播到该类..并更新该活动中回收者的进度条
  • 好的,我明白了,谢谢,但是如何在 recyclerview 适配器中添加多个文件请给我一些指导
  • 如何在该课程中设置广播下载百分比请指导我
  • 我有这个问题超过 1.5 年。 StackOverflow 中没有人回答这个话题。甚至,谷歌也帮不上忙
  • 我遇到了同样的问题。你解决了吗?

标签: android android-recyclerview android-asynctask android-download-manager


【解决方案1】:

在您为每个 RecyclerViewItem 使用的数据模型中维护一个变量用于进度。当您收到有关进度的更新时,请更新您正在使用的数据集,然后调用 notifyItemChanged(mPos)notifyItemRangeChanged() 以通知适配器。 notifyItemChanged(mPos) 触发 onBindVieHolder 对应的位置,即使它当前不可见。

【讨论】:

    【解决方案2】:

    以下是一些一般性建议。

    您可以通过调用notifyItemRangeChanged(int positionStart, int itemCount, Object payload) 来更新RecyclerView 行。这将在您的适配器中调用onBindViewHolder(FeedBaseViewHolder holder, int position, List<Object> payloads),您可以在其中使用进度条更新一行。

    在有效负载中,您可以将所需值的角度与您的要求一起传递,例如开始进度操作、更新进度、停止进度。

    希望对你有帮助。

    【讨论】:

      【解决方案3】:

      之前建议使用notifyItemChanged() 的答案给了我三个大问题:

      1. 它不必要地更新整个 ViewHolder 和所有子视图,
      2. 当您在显示更新的同时滚动列表时,它会产生无穷无尽的烦人动画
      3. 它显示随机(不值得/不正确的)ViewHolders 的进度更新

      为我解决所有这些问题的方法是使用回调来更新与进度相关的特定视图。我的解决方案是在 Kotlin 中,因为 A) 这就是我所做的,B) 它是为了让这些问题变得简单,C) OP 没有将 Java 指定为语言。

      PRDownload 库通过在每个响应/更新时调用的内置回调系统使这更容易一些 - 这是DownloadManager 所没有的。要使用DownloadManager,您只需创建一个计时器(可能是WorkManager)来查询DownloadManager 数据库并定期获取下载进度,并根据进度调用回调。


      PRDownload 的依赖:

      implementation "com.mindorks.android:prdownloader:0.6.0"
      

      适配器

      import android.os.Handler
      import android.os.Looper
      import android.util.Log
      import android.view.LayoutInflater
      import android.view.View
      import android.view.ViewGroup
      import android.widget.TextView
      import androidx.recyclerview.widget.RecyclerView
      import com.downloader.Error
      import com.downloader.OnDownloadListener
      import com.downloader.OnProgressListener
      import com.downloader.PRDownloader
      import com.downloader.Progress
      
      val mutableMapOfIDToProgressListenerCallback: MutableMap<Int, MyDownloadProgressListener> =
          mutableMapOf()
      
      class RecyclerViewAdapter(private val dataSet: List<ListItem>) :
          RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder>() {
      
          inner class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
              lateinit var listItem: ListItem
              val percentDownloaded = itemView.findViewById<TextView?>(R.id.percent_downloaded)
              fun setUpdateViewLambda() {
                  mutableMapOfIDToProgressListenerCallback[listItem.id]?.setLabmda {
                      percentDownloaded?.text = toPercent(it).toString()
                  }
              }
      
              fun removeUpdateViewLambda() {
                  mutableMapOfIDToProgressListenerCallback[listItem.id]?.setLabmda {}
              }
      
              init {
                  view.setOnClickListener { //to demonstrate downloads
                      Thread { //use coroutines instead
                              downloadItem(
                                  listItem,
                                  adapterPosition,
                                  this@RecyclerViewAdapter,
                                  "https://downloads.gradle-dn.com/distributions/gradle-6.9.1-bin.zip", //insert your download here
                                  view.context.getExternalFilesDir(null)!!.absolutePath,
                                  "a${listItem.id}.zip"
                              )
                      }.start()
                  }
              }
          }
      
          override fun onViewAttachedToWindow(holder: ViewHolder) {
              holder.setUpdateViewLambda()
              super.onViewAttachedToWindow(holder)
          }
      
          override fun onViewDetachedFromWindow(holder: ViewHolder) {
              holder.removeUpdateViewLambda() //remove callback so that recycled viewholder doesn't undeservingly get updates
              super.onViewDetachedFromWindow(holder)
          }
      
          override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
              return ViewHolder(
                  LayoutInflater
                      .from(parent.context)
                      .inflate(R.layout.list_item, parent, false)
              )
          }
      
          override fun onBindViewHolder(holder: ViewHolder, position: Int) {
              val listItem = dataSet[position]
      
              //perform your binding
      
              holder.listItem = listItem
              holder.percentDownloaded?.text = listItem.percentDownloaded.toString()
              holder.setUpdateViewLambda()
          }
      
          override fun getItemCount(): Int {
              return dataSet.size
          }
      }
      
      data class ListItem(
          val id: Int, //a value that uniquely represents this list item
          var percentDownloaded: Byte //can only ever be between 0-100, so no need for Int
      )
      
      class MyDownloadProgressListener : OnProgressListener {
          lateinit var listItem: ListItem
          private var updateView: (progress: Progress) -> Unit = {}
          fun setLabmda(lambda: (progress: Progress) -> Unit) {
              updateView = lambda
          }
      
          override fun onProgress(progress: Progress?) {
              Log.d("Adapter", "Progress object: $progress")
              if (progress != null) {
                  val toPercent = toPercent(progress)
                  Log.d("Adapter", "Percent received: $toPercent")
                  listItem.percentDownloaded = toPercent
                  updateView(progress)
              }
          }
      }
      
      fun downloadItem(
          listItem: ListItem,
          adapterPosition: Int,
          adapter: RecyclerViewAdapter,
          url: String,
          dirPath: String,
          filename: String
      ) {
          val listener = MyDownloadProgressListener()
          mutableMapOfIDToProgressListenerCallback[listItem.id] = listener
          listener.listItem = listItem
          Handler(Looper.getMainLooper()).post { adapter.notifyItemChanged(adapterPosition)/*bind listener to ViewHolder*/ }
      
          PRDownloader
              .download(url, dirPath, filename)
              .build()
              .setOnProgressListener(listener)
              .setOnCancelListener {
                      mutableMapOfIDToProgressListenerCallback.remove(listItem.id)
              }
              .start(object : OnDownloadListener {
                  override fun onDownloadComplete() {
                      mutableMapOfIDToProgressListenerCallback.remove(listItem.id)
                  }
      
                  override fun onError(error: Error?) {
                      mutableMapOfIDToProgressListenerCallback.remove(listItem.id)
                  }
              }
          )
      }
      
      fun toPercent(progress: Progress) =
          (100 * (progress.currentBytes / progress.totalBytes.toDouble())).toInt().toByte()
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多