Android开发——RecyclerView实现下载列表
<p>本篇记录的是使用Jsoup框架爬取网页内容,结合Android的RecyclerView,从而实现批量下载小说的功能(也是我的APP星之小说下载器Android版的核心功能),<strong>思路仅供参考</strong></p><p>本文使用了AsyncTask来实现下载功能,不懂使用的可以参考一下我的文章Android开发——实现子线程更新UI</p>
<p>RecyclerView的使用这里也略过了,详情请看Android ListView与RecycleView的对比使用</p>
<h2 id="思路分析">思路分析</h2>
<h3 id="recyclerview相关概念">RecyclerView相关概念</h3>
<p>RecyclerView的使用大家都熟悉了,我们主要继承适配器,实现了适配器中的三个方法</p>
<p><strong>主要流程:</strong></p>
<p>适配器获得我们写的Item.xml布局,之后根据此布局,创建了一个ViewHolder,然后,就把数据源(List存储的实体类)逐一地设置到我们写的Item.xml布局文件中(找到某个控件的实例,之后进行setText等操作)</p>
<h3 id="item进度条更新">Item进度条更新</h3>
<p><strong>思路:</strong><br>
我们的item中包含有进度条,想要实现进度条更新效果,按照之前的常理,得找到这个进度条的实例对象,然后设置进度条的进度。</p>
<p>问题来了——</p>
<p><strong>1.如何找到进度条这个实例对象呢?</strong></p>
<p>View类中提供了一个方法<code>findViewById</code>,通过此方法就可以找到某个实例对象,所以我们要获得进度条所在的那个root View对象(也就是itemView)</p>
<p><strong>2.如何获得itemView?</strong></p>
<p>RecyclerView中,提供了一个方法<code>findViewHolderForAdapterPosition</code>用来找到某个位置的ViewHolder,找到ViewHolder,之后就可以由此ViewHolder找到itemView</p>
<pre><code>//找到特定position对应的ItemView
val itemView = rv_downloading.findViewHolderForAdapterPosition(position).itemView
val progressbar = itemView.findViewById(R.id.progress)
//kotlin中特有的自动转型功能,设置进度条进度为20
if (progressbar is ProgressBar) progressbar.progress = 20
</code></pre>
<p>这部分更新的UI的代码,要在AsyncTask的onProgressUpdate方法中执行(子进程中更新UI)</p>
<h3 id="暂停功能">暂停功能</h3>
<p><strong>思路:</strong><br>
在Item的那个布局中,添加一个TextView,并设置visibility属性为gone,此TextView就是一个暂停的标记,默认text属性为1,就是不暂停。</p>
<p>小说下载器是是按章下载的,在开始下载某一章节的时候,检测此TextView的值是否为0,不为0则下载,为0则进入到一个死循环</p>
<p>当点击暂停按钮的时候,修改状态TextView的text为0即可</p>
<h3 id="总结">总结</h3>
<p>从以上的思路分析,可以总结出这样的思路:</p>
<p>我们通过itemView去达到更新UI功能(上述只是简单说需要更新进度条当然,实际情况,不只更新进度条,还要更新其他的控件,具体情况,具体分析),所以需要一个List或HashMap存放itemView。</p>
<p>这里实际项目我选用了HashMap(<strong>名字为itemViewMap</strong>),然后HashMap的key为Int(<strong>变量名为itemPosition</strong>)表示是当前任务列表的第几个任务(从0开始),value则是该任务对应的itemView</p>
<p>由于我们是使用<code>findViewHolderForAdapterPosition</code>方法得到的ViewHolder,再由ViewHolder获得itemView对象,所以需要一个position</p>
<p>这里,如果考虑到任务完成之后的情况,position可能会改变,<strong>因为任务完成之后,RecyclerView会将item移出</strong></p>
<p><img src="https://img2018.cnblogs.com/blog/1210268/201910/1210268-20191015103829445-1417628524.png" alt="" loading="lazy"></p>
<p>上图中的第3个任务(即是RecyclerView中position为2的那个任务),之后RecyclerView会将该item移出列表,后面的item的position就会发生改变,原本itemPosition=3对应的position也是3,之后position发生了改变,itemPosition=3的item对应的position变为了2</p>
<p>由上面分析,我们应该使用一个HashMap(<strong>名字为itemPositonMap</strong>)来保存<code>itemPosition</code>和对应的<code>position</code>(<strong>itemPosition作为key,position作为value</strong>),在任务完成之后需要重新计算<code>itemPositonMap</code>中的映射关系(也就是在AsyncTask中的<code>onPostExecute</code>方法中)</p>
<p><strong>由上图得到的规律:</strong></p>
<p>某个任务完成了,index>该任务的index,<code>position=position-1</code></p>
<p>每添加一个任务,新的任务的<code>itemPosition=itemPositonMap.size</code>,对应的<code>position=dataList.size</code></p>
<p>itemPositionMap的长度,即是记录了当前是第几个任务</p>
<p>dataList即是new一个适配器传到适配器中数据源,之后任务完成需要根据position移出某个数据</p>
<h2 id="实现">实现</h2>
<h3 id="注意点">注意点</h3>
<ol>
<li>ViewHolder需要在RecyclerView填充完item之后才能获取到,否则为空</li>
<li>暂停功能的那个TextView也是需要在RecyclerView填充完item之后才能获取到,否则为空</li>
</ol>
<h3 id="代码">代码</h3>
<pre><code>private val dataList = arrayListOf<DownloadingItem>()
private val itemViewMap = hashMapOf<Int?, View>()
//itemPostion(data) - > position(recyclerview)
private val itemPositonMap = hashMapOf<Int, Int>()
internal inner class DownloadingTask : AsyncTask<String, DownloadingItem, DownloadedItem>() {
var isFirst = true
var itemPosition = 0
var tvStatus: TextView? = null
override fun onPreExecute() {
//一些初始化操作
itemPosition = itemPositonMap.size
//保存对应的item索引和位置
itemPositonMap = dataList.size
}
override fun doInBackground(vararg params: String?): DownloadedItem {
val tool = NovelDownloadTool(params.toString(), itemPosition)
val messageItem = tool.getMessage()
publishProgress(messageItem)
for (i in 0 until tool.chacterMap.size) {
//下载每章节,并更新
val item = tool.downloadChacter(this@DownloadingFragment.activity, i)
publishProgress(item)
//tvStatus控件可能为空(因为RecyclerView的itemView未初始化成功)
while (tvStatus?.text.toString() != "1") {
}
// if (tvStatus != null) while (tvStatus!!.text.toString() != "1"){}
}
//合并文件,并返回一个数据类(DownloadedItem),之后添加到另外的RecyclerView中
return tool.mergeFile(this@DownloadingFragment.activity)
}
override fun onProgressUpdate(vararg values: DownloadingItem?) {
//recyclerView Item更新
if (isFirst) {
values?.let { dataList.add(it) }
adapter?.notifyDataSetChanged()
isFirst = false
} else {
if (tvStatus == null) {
val itemView = rv_downloading.findViewHolderForAdapterPosition(itemPositonMap as Int).itemView
tvStatus = itemView.findViewById(R.id.tv_status) as TextView?
//存入itemView
itemViewMap = itemView
}
updateItem(values.last())
}
}
override fun onPostExecute(result: DownloadedItem?) {
showToast("下载成功")
//移出adapter中的数据
val position = itemPositonMap as Int
adapter?.notifyItemRemoved(position)
dataList.removeAt(position)
//下载完成,重新计算itemPostion对应的position
for (i in position + 1 until itemPositonMap.size) {
itemPositonMap = itemPositonMap as Int - 1
}
val mainactivity = this@DownloadingFragment.activity as MainActivity
mainactivity.addItemToHistory(result)
}
}
</code></pre>
<h3 id="缺点">缺点</h3>
<ol>
<li>itemPositonMap和itemViewMap在任务列表存在过多任务,占用的内存会过大(可以考虑在任务列表任务全部完成之后进行一次清空操作)</li>
<li>暂停功能使用的是while死循环,可能会产生bug</li>
</ol>
</div>
<div id="MySignature" role="contentinfo">
<hr>
<span>提问之前,请先看</span>提问须知
<span>点击右侧图标发起提问</span>
<img border="0" src="http://wpa.qq.com/pa?p=2:1053894518:52" alt="联系我" title="联系我">
<span>或者加入QQ群一起学习</span>
<img border="0" src="//pub.idqqimg.com/wpa/images/group.png" alt="Stars-One安卓学习交流群" title="Stars-One安卓学习交流群">
TornadoFx学习交流群:1071184701
<img src="https://img2020.cnblogs.com/blog/1210268/202003/1210268-20200316120825333-1551152974.png" width="1000" height="auto">
<img src="https://img2018.cnblogs.com/blog/1210268/201905/1210268-20190508151523126-971809604.gif" width="1000" height="auto">
<!--<img src="https://img2020.cnblogs.com/blog/1210268/202004/1210268-20200413161422035-1188549898.gif" width="1000" height="auto">--><br><br>
来源:https://www.cnblogs.com/stars-one/p/11676374.html
頁:
[1]