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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...