Android開發——RecyclerView實現下載列表

来源:https://www.cnblogs.com/kexing/archive/2019/10/15/11676374.html
-Advertisement-
Play Games

本篇記錄的是使用Jsoup框架爬取網頁內容,結合Android的RecyclerView,從而實現批量下載小說的功能(也是我的APP "星之小說下載器Android版" 的核心功能), 思路僅供參考 本文使用了AsyncTask來實現下載功能,不懂使用的可以參考一下我的文章 "Android開發—— ...


本篇記錄的是使用Jsoup框架爬取網頁內容,結合Android的RecyclerView,從而實現批量下載小說的功能(也是我的APP星之小說下載器Android版的核心功能),思路僅供參考

本文使用了AsyncTask來實現下載功能,不懂使用的可以參考一下我的文章Android開發——實現子線程更新UI

RecyclerView的使用這裡也略過了,詳情請看Android ListView與RecycleView的對比使用

思路分析

RecyclerView相關概念

RecyclerView的使用大家都熟悉了,我們主要繼承適配器,實現了適配器中的三個方法

主要流程:

適配器獲得我們寫的Item.xml佈局,之後根據此佈局,創建了一個ViewHolder,然後,就把數據源(List存儲的實體類)逐一地設置到我們寫的Item.xml佈局文件中(找到某個控制項的實例,之後進行setText等操作)

Item進度條更新

思路:
我們的item中包含有進度條,想要實現進度條更新效果,按照之前的常理,得找到這個進度條的實例對象,然後設置進度條的進度。

問題來了——

1.如何找到進度條這個實例對象呢?

View類中提供了一個方法findViewById,通過此方法就可以找到某個實例對象,所以我們要獲得進度條所在的那個root View對象(也就是itemView)

2.如何獲得itemView?

RecyclerView中,提供了一個方法findViewHolderForAdapterPosition用來找到某個位置的ViewHolder,找到ViewHolder,之後就可以由此ViewHolder找到itemView

//找到特定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

這部分更新的UI的代碼,要在AsyncTask的onProgressUpdate方法中執行(子進程中更新UI)

暫停功能

思路:
在Item的那個佈局中,添加一個TextView,並設置visibility屬性為gone,此TextView就是一個暫停的標記,預設text屬性為1,就是不暫停。

小說下載器是是按章下載的,在開始下載某一章節的時候,檢測此TextView的值是否為0,不為0則下載,為0則進入到一個死迴圈

當點擊暫停按鈕的時候,修改狀態TextView的text為0即可

總結

從以上的思路分析,可以總結出這樣的思路:

我們通過itemView去達到更新UI功能(上述只是簡單說需要更新進度條當然,實際情況,不只更新進度條,還要更新其他的控制項,具體情況,具體分析),所以需要一個List或HashMap存放itemView。

這裡實際項目我選用了HashMap(名字為itemViewMap),然後HashMap的key為Int(變數名為itemPosition)表示是當前任務列表的第幾個任務(從0開始),value則是該任務對應的itemView

由於我們是使用findViewHolderForAdapterPosition方法得到的ViewHolder,再由ViewHolder獲得itemView對象,所以需要一個position

這裡,如果考慮到任務完成之後的情況,position可能會改變,因為任務完成之後,RecyclerView會將item移出

上圖中的第3個任務(即是RecyclerView中position為2的那個任務),之後RecyclerView會將該item移出列表,後面的item的position就會發生改變,原本itemPosition=3對應的position也是3,之後position發生了改變,itemPosition=3的item對應的position變為了2

由上面分析,我們應該使用一個HashMap(名字為itemPositonMap)來保存itemPosition和對應的positionitemPosition作為key,position作為value),在任務完成之後需要重新計算itemPositonMap中的映射關係(也就是在AsyncTask中的onPostExecute方法中)

由上圖得到的規律:

某個任務完成了,index>該任務的index,position=position-1

每添加一個任務,新的任務的itemPosition=itemPositonMap.size,對應的position=dataList.size

itemPositionMap的長度,即是記錄了當前是第幾個任務

dataList即是new一個適配器傳到適配器中數據源,之後任務完成需要根據position移出某個數據

實現

註意點

  1. ViewHolder需要在RecyclerView填充完item之後才能獲取到,否則為空
  2. 暫停功能的那個TextView也是需要在RecyclerView填充完item之後才能獲取到,否則為空

代碼

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[itemPosition] = dataList.size
    }

    override fun doInBackground(vararg params: String?): DownloadedItem {

        val tool = NovelDownloadTool(params[0].toString(), itemPosition)
        val messageItem = tool.getMessage()
        publishProgress(messageItem)
        for (i in 0 until tool.chacterMap.size) {
            //下載每章節,並更新
            val item = tool.downloadChacter([email protected], i)
            publishProgress(item)

            //tvStatus控制項可能為空(因為RecyclerView的itemView未初始化成功)
            while (tvStatus?.text.toString() != "1") {
            }
            // if (tvStatus != null) while (tvStatus!!.text.toString() != "1"){}
        }
        //合併文件,並返回一個數據類(DownloadedItem),之後添加到另外的RecyclerView中
        return tool.mergeFile([email protected])
    }

    override fun onProgressUpdate(vararg values: DownloadingItem?) {
        //recyclerView Item更新
        if (isFirst) {
            values[0]?.let { dataList.add(it) }

            adapter?.notifyDataSetChanged()
            isFirst = false

        } else {
            if (tvStatus == null) {
                val itemView = rv_downloading.findViewHolderForAdapterPosition(itemPositonMap[itemPosition] as Int).itemView
                tvStatus = itemView.findViewById(R.id.tv_status) as TextView?
                //存入itemView
                itemViewMap[values.last()?.itemPosition] = itemView
            }
            updateItem(values.last())
        }
    }

    override fun onPostExecute(result: DownloadedItem?) {
        showToast("下載成功")

        //移出adapter中的數據
        val position = itemPositonMap[result?.itemPosition] as Int

        adapter?.notifyItemRemoved(position)
        dataList.removeAt(position)
        //下載完成,重新計算itemPostion對應的position
        for (i in position + 1 until itemPositonMap.size) {
            itemPositonMap[i] = itemPositonMap[i] as Int - 1
        }

        val mainactivity = [email protected] as MainActivity
        mainactivity.addItemToHistory(result)
    }
}

缺點

  1. itemPositonMap和itemViewMap在任務列表存在過多任務,占用的記憶體會過大(可以考慮在任務列表任務全部完成之後進行一次清空操作)
  2. 暫停功能使用的是while死迴圈,可能會產生bug

您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 現需要限定特定的用戶只能查看並訪問特定的資料庫,防止多個用戶對資料庫操作時一些誤操作。 參考i6first的如何讓用戶只能訪問特定的資料庫(MSSQL)博文 1.新建登錄用戶 以管理員身份登陸資料庫(許可權最高的身份如sa),點擊安全性->登錄名,右鍵新建登錄名,輸入登錄名和密碼,取消強制實施密碼策略 ...
  • 1、去官網查找最新(你需要的)安裝包版本 # https://dev.mysql.com/downloads/repo/yum/ 2、下載MySQL安裝包 # wget http://dev.mysql.com/get/mysql80-community-release-el7-3.noarch.r ...
  • 概述 數據完整性指資料庫中數據的 正確性、相容性和一致性 。包括現實世界中的應用需求的完整性。數據的完整性由完整性規則來定義。 關係模型的完整性規則是對關係的某種約束,提供一種手段來保證用戶對資料庫的修改時不會破壞資料庫中數據的完整性。保證數據是有意義的。 關係模型分三類約束:實體完整性約束、參照完 ...
  • 在SQL Server中重建索引(Rebuild Index)與重組索引(Reorganize Index)會觸發統計信息更新嗎? 那麼我們先來測試、驗證一下: 我們以AdventureWorks2014為測試環境,如下所示: Person.Person表的統計信息最後一次更新為2014-07-17... ...
  • 本文基於 Android 9.0 , 代碼倉庫地址 : "android_9.0.0_r45" 文中源碼鏈接: "SystemServer.java" "ActivityManagerService.java" "Process.java" "ZygoteProcess.java" 對 和 啟動流程 ...
  • 1. 在對象內部讀取數據時,應該直接通過實例變數來讀,而寫入數據時,則應通過屬性來寫。 2. 在初始化方法及dealloc方法中,總是應該直接通過實例變數來讀寫數據。 3. 使用Lazy Initialization配置的數據,應該通過屬性來讀取數據。 4. 不要在setter/g... ...
  • 1. 可以用@property語法來定義對象中所封裝的數據。 2. 通過“修飾詞”來指定存儲數據所需的正確語義。 3. 在設置屬性所對應的實例變數時,一定要遵從該屬性所聲明的語義。 4. 開發iOS程式時應該使用nonatomic屬性,因為atomic(同步鎖)屬性嚴重影響性能。 ...
  • 1. 應該用枚舉表示狀態機的狀態、傳遞給方法的選項以及狀態碼等值,給這些值起個易懂的名字,就像監聽網路狀態的枚舉。 2. 如果把傳遞給某個方法的選項表示為枚舉類型,而多個選項又可同時使用,那麼就將各選項定義為2的冪,以便通過按位或操作將其組合起來。 3. 用 NS_ENUM 與 NS_O... ...
一周排行
    -Advertisement-
    Play Games
  • 前言 在我們開發過程中基本上不可或缺的用到一些敏感機密數據,比如SQL伺服器的連接串或者是OAuth2的Secret等,這些敏感數據在代碼中是不太安全的,我們不應該在源代碼中存儲密碼和其他的敏感數據,一種推薦的方式是通過Asp.Net Core的機密管理器。 機密管理器 在 ASP.NET Core ...
  • 新改進提供的Taurus Rpc 功能,可以簡化微服務間的調用,同時可以不用再手動輸出模塊名稱,或調用路徑,包括負載均衡,這一切,由框架實現並提供了。新的Taurus Rpc 功能,將使得服務間的調用,更加輕鬆、簡約、高效。 ...
  • 順序棧的介面程式 目錄順序棧的介面程式頭文件創建順序棧入棧出棧利用棧將10進位轉16進位數驗證 頭文件 #include <stdio.h> #include <stdbool.h> #include <stdlib.h> 創建順序棧 // 指的是順序棧中的元素的數據類型,用戶可以根據需要進行修改 ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • C總結與剖析:關鍵字篇 -- <<C語言深度解剖>> 目錄C總結與剖析:關鍵字篇 -- <<C語言深度解剖>>程式的本質:二進位文件變數1.變數:記憶體上的某個位置開闢的空間2.變數的初始化3.為什麼要有變數4.局部變數與全局變數5.變數的大小由類型決定6.任何一個變數,記憶體賦值都是從低地址開始往高地 ...
  • 如果讓你來做一個有狀態流式應用的故障恢復,你會如何來做呢? 單機和多機會遇到什麼不同的問題? Flink Checkpoint 是做什麼用的?原理是什麼? ...
  • C++ 多級繼承 多級繼承是一種面向對象編程(OOP)特性,允許一個類從多個基類繼承屬性和方法。它使代碼更易於組織和維護,並促進代碼重用。 多級繼承的語法 在 C++ 中,使用 : 符號來指定繼承關係。多級繼承的語法如下: class DerivedClass : public BaseClass1 ...
  • 前言 什麼是SpringCloud? Spring Cloud 是一系列框架的有序集合,它利用 Spring Boot 的開發便利性簡化了分散式系統的開發,比如服務註冊、服務發現、網關、路由、鏈路追蹤等。Spring Cloud 並不是重覆造輪子,而是將市面上開發得比較好的模塊集成進去,進行封裝,從 ...
  • class_template 類模板和函數模板的定義和使用類似,我們已經進行了介紹。有時,有兩個或多個類,其功能是相同的,僅僅是數據類型不同。類模板用於實現類所需數據的類型參數化 template<class NameType, class AgeType> class Person { publi ...
  • 目錄system v IPC簡介共用記憶體需要用到的函數介面shmget函數--獲取對象IDshmat函數--獲得映射空間shmctl函數--釋放資源共用記憶體實現思路註意 system v IPC簡介 消息隊列、共用記憶體和信號量統稱為system v IPC(進程間通信機制),V是羅馬數字5,是UNI ...