在 Android 的 Recyclerview 中显示下载进度
Posted
技术标签:
【中文标题】在 Android 的 Recyclerview 中显示下载进度【英文标题】:Show Download progress in a Recyclerview in Android 【发布时间】: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 年。 *** 中没有人回答这个话题。甚至,谷歌也帮不上忙 我遇到了同样的问题。你解决了吗? 【参考方案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()
的答案给了我三个大问题:
-
它不必要地更新整个 ViewHolder 和所有子视图,
当您在显示更新的同时滚动列表时,它会产生无穷无尽的烦人动画
它显示随机(不值得/不正确的)ViewHolders 的进度更新
为我解决所有这些问题的方法是使用回调来更新与进度相关的特定视图。我的解决方案是在 Kotlin 中,因为 A) 这就是我所做的,B) 它是为了让这些问题变得简单,C) OP 没有将 Java 指定为语言。
PRDownload 库通过在每个响应/更新时调用的内置回调系统使这更容易一些 - 这是DownloadManager
所没有的。要使用DownloadManager
,您只需创建一个计时器(可能是WorkManager
)来查询DownloadManager
数据库并定期获取下载进度,并根据进度调用回调。
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()
【讨论】:
以上是关于在 Android 的 Recyclerview 中显示下载进度的主要内容,如果未能解决你的问题,请参考以下文章
android: ScrollView 内的 RecyclerView
Android:在 Recyclerview 中更改图像旋转