Home >> Blog >> Android 中的 RecyclerView 是什麼?

Android 中的 RecyclerView 是什麼?

RecyclerView是一個比 GridView 和 ListView 更靈活、更高級的小部件。它是一個用於顯示大型資料集的容器,可以通過維護有限數量的視圖來有效地滾動。當您擁有其元素在運行時更改取決於網絡事件或用戶操作的資料集合時,您可以使用 RecyclerView 小部件。

意見

Android 平台使用 View 和 ViewGroup 類在屏幕上繪製項目。這些類是抽象的,並被擴展到不同的實現以適應用例。例如,TextView 的目的很簡單,就是在屏幕上顯示文本內容。EditText 擴展自同一個 View 類,並添加了更多功能以使用戶能夠輸入資料。

可以創建我們自己的自定義視圖,以便在開髮用戶界面時獲得更大的靈活性。View 類提供了我們可以重寫以在屏幕上繪製的方法,以及一種傳遞參數的方法,例如寬度、高度和我們想要添加到 View 中的自定義屬性,以使其按我們希望的方式運行。

視圖組

ViewGroup 類是一種 View,但與簡單的 View 類不同,ViewGroup 的職責只是顯示,ViewGroup 使我們能夠將多個視圖放入一個視圖中,我們可以將其作為一個整體進行引用。在這種情況下,我們添加其他簡單視圖(我們也可以添加 viewGroups)到的頂層視圖稱為“父視圖”,而添加到內部的視圖是“子視圖”。

我們可以將 View 成像為 Array 並將 ViewGroup 成像為 Array 的 Array。鑑於數組數組本身就是數組,我們可以看到如何將 ViewGroup 視為視圖。

var arr1 = [1,2,3] //imagine a simple View as an Array
//we can imagine this as a NumberTextView which doesn't really exist
//but we could imagine there's one that makes it easy to use numbers
var arr2 = ["a","b","c"] // We can imagine this as another simple view

var nestedArr = [arr1,arr2] //in our anology, we can now group views
//together and the structure that would hold that would be what we call the ViewGroup

ViewGroup 還使我們能夠定義子視圖在視圖中的組織方式,例如,垂直或水平放置。我們可以在 View 內部有不同的交互規則。例如,TextView 彼此跟隨的距離應為 12dp,而 ImageViews 後跟 TextView 的距離應為 5dp。

如果我們從頭開始開發自己的 ViewGroup,就會出現這種情況。為了使這些配置更容易,Android 提供了一個名為 LayoutParams 的類,我們可以使用它來輸入這些配置。

Android 文檔提供了一些我們在配置自己的 ViewGroup 時會實現的默認參數。一些常見的參數是與寬度、高度和邊距有關的參數。默認情況下,這些配置具有結構 android:layout_height 用於高度,例如 android:layout_width 在這方面,當您創建 ViewGroup 時,您可以進一步創建特定於您希望 ViewGroup 行為方式的 LayoutParams。

Android 帶有默認的 Views 和 ViewGroups,我們可以用它來完成很多我們需要的常見任務。我們提到的一個例子是TextView。這是一個簡單的視圖,帶有高度、寬度、文本大小等可配置方面。如前所述,我們有一個 ImageView 用於顯示圖像和 EditText,等等。Android 也有自定義 ViewGroups,我們可以添加我們的 Views 並獲得預期的行為。

線性佈局

LinearLayout 允許我們在其中添加 View 項。LinearLayout 是一個方向屬性,它決定了它在屏幕上的佈局方式。它還有 LinearLayout.LayoutParams 規定了內部視圖的規則,例如,屬性 android:center_horizontal 將使視圖沿水平軸居中,而 `android:center_vertical 將使視圖的內容沿垂直軸居中。

這裡有一些圖像可以幫助您了解居中。我們將其視為 200 x 200 像素空間內的簡單 TextView,居中屬性將使其行為如下。

機器人:center_horizontal

Android 中的 RecyclerView 是什麼?

水平居中的內容

安卓:center_vertical

Android 中的 RecyclerView 是什麼?

垂直居中的內容

安卓:中心

Android 中的 RecyclerView 是什麼?

居中的內容

RecyclerView 的核心組件

Android 中的 RecyclerView 是什麼?

以下是 RecyclerView 的重要組件:

RecyclerView.Adapter

基礎 #1 – 適配器模式

適配器是將系統或設備的屬性轉換為其他不兼容設備或系統的屬性的設備。其中一些修改信號或電源屬性,而另一些則只是將一個連接器的物理形式調整到另一個連接器。

我們在現實生活中找到一個簡單的例子來解釋適配器,當我們需要將設備連接在一起時,它們的連接端口彼此不匹配。當您訪問他們使用不同類型套接字的不同國家時,可能會出現這種情況。如果您攜帶手機或筆記本電腦充電器,則無法將其連接到電源插座。但是,您不會放棄,只需在電源插座和充電器之間安裝一個適配器,即可進行充電。

當我們想要將兩個資料結構連接在一起以完成任務時,在編程中就是這種情況,但是它們的默認端口沒有相互通信的方式。

我們將使用設備和充電器的簡單示例。我們將有兩個充電器實例。一個美國人一個英國人

class AmericanCharger() {
var chargingPower = 10
}
class BritishCharger(){
var charginPower = 5
}

然後我們將創建兩個設備

class AmericanDevice()
class BritishDevice()

作為一個例子,我們可以創建一些設備的實例來一起玩。

var myAmericanPhone = new AmericanDevice()
var myBritishPhone = new BritishDevice()

然後,我們將通過在設備中添加一個名為 charge() 的方法來介紹為這兩種設備充電的概念。

該方法將其各自的充電器作為輸入並基於它進行充電。

sealed trait Device
class AmericanDevice : Device{
fun charge(charger:AmericanCharger){
//Do some American charging
}
}
class BritishDevice: Device{
fun charge(charger:BritishCharger){
//Do some British charging
}
}

在這種情況下,根據我們的類比,我們將出於某種原因在使用 AmericanDevice 時需要使用 BritishCharger,反之亦然。

在編程世界中,這通常是在將提供相同功能的庫混合在一起時(在我們的上下文中,我們的共享功能正在收費)。我們需要找到一種方法來實現這一點

如果我們按照這個類比,我們將需要去一家電子商店購買一個適配器,這樣我們就可以在 BritishChargers 的情況下為 AmericanDevices 充電。從編程的角度來看,我們將成為適配器的製造商。

我們將為一個匹配我們創建另一個所需的確切模式的適配器製作一個適配器。我們將它實現為一個類,如下所示。它不一定需要是一個類,也可以是一個突出適配器模式通常所做的功能的函數。我們將使用一個類,因為它與 Android 上的大多數用法相匹配。

class AmericanToBritishChargerAdapter(theAmericanCharger:AmericanCharger){
fun returnNewCharger(): BritishCharger{
//convert the American charger to a BritishCharger
//we would change the American charging functionality
//to British charging functionality to make sure the
//adapter doesn't destroy the device. The adapter could
//, for example, control the power output by dividing by 2
//our adapter could encompass this functionality in here

var charingPower:Int = charger.chargingPower / 2
var newBritishCharger = new BritishCharger()
newBritishCharger.chargingPower = theAmericanCharger.chargingPower/2

return newBritishCharger
}
}

在編程世界中,sockets 的不同類似於內部用於充電的方法的不同。使用不同方法的充電器會導致無法使用充電器。

var myBritishDevice = new BritishDevice()
var americanChargerIFound = new AmericanCharger()

嘗試使用 AmericanChargerIFound 調用 myBritishDevice 中的 charge() 方法將不起作用,因為 AmericanDevice 只接受 AmericanCharger

所以這是不可能的

var myBritishDevice = new BritishDevice()
var americanChargerIFound = new AmericanCharger()
myBritishDevice.charge(americanChargerIFound)

在這種情況下,我們創建的適配器

AmericanToBritishChargerAdapter 現在可以派上用場了。我們可以使用 returnNewCharger() 方法創建一個新的 BritishCharger,我們可以用它來充電。我們所需要做的就是創建一個適配器實例,並用我們擁有的 AmericanCharger 提供它,它將創建一個我們可以使用的 BritishCharger

var myBritishDevice = new BritishDevice()
var americanChargerIFound = new AmericanCharger()
//We create the adapter and feed it the americanCharger
var myAdapter = AmericanToBritishChargerAdapter(theAmericanCharger)
//calling returnNewCharger from myAdapter would return a BritishCharger
var britishChargerFromAdapter = myAdapter.returnNewCharger()
//and once we have the britishCharger we can now use it
myBritishDevice.charge(britishChargerFromAdapter)

RecyclerView.LayoutManager

在處理 ViewGroup 時,我們會將 Views 放置在其中。LayoutManager 的任務是描述視圖在內部的佈局方式

出於比較目的,在使用 Linearlayout ViewGroup 時,我們想要的用例是能夠垂直或水平放置項目。這很容易通過添加一個方向屬性來實現,它告訴我們線性佈局將如何放置在屏幕上。我們可以通過使用android:orientation=VERTICAL|HORIZONTAL屬性來做到這一點。

我們還有另一個叫做 GridLayout 的 ViewGroup,它的用例是當我們想要將視圖放置在一個矩形網格結構中時。這可能是因為我們提供給應用程序用戶的資料易於使用等原因。通過設計,GridLayout 可以通過配置來幫助您實現這一目標,我們可以在其中定義網格的尺寸,例如,我們可以有一個 4×4 網格,3 x 2 網格。

RecyclerView.ViewHolder

ViewHolder 是一個抽像類,我們也從 RecyclerView 擴展而來。ViewHolder 為我們提供了常用的方法來幫助我們引用我們放置在 RecyclerView 上的 View,即使在 RecyclerView 中的回收機制更改了我們不知道的各種引用之後。

大型列表

RecyclerViews 用於當我們想要向用戶呈現一組非常大的視圖時,同時又不會為創建的每個視圖實例耗盡我們設備上的 RAM。

如果我們以聯繫人列表為例,我們將大致了解一個聯繫人在列表中的外觀。然後我們要做的是創建一個模板佈局——它實際上是一個視圖——帶有插槽,我們的聯繫人列表中的各種資料將被填充。以下是解釋整個目的的偽代碼:

//OneContactView
< OneContact>
< TextView>{{PlaceHolderForName}}< /TextView>
< TextView>{{PlaceHolderForAddress}}< /TextView>
< ImageView>{{PlaceHolderForProfilePicture}}< /ImageView>
< TextView>{{PlaceHolderForPhoneNumber}}< /TextView>
< /OneContact>

然後我們將有一個這種性質的聯繫人列表

< ContactList>
< /ContactList>

如果是這種情況,我們對內容進行了硬編碼,我們就沒有編程方式在不重寫應用程序的情況下將新內容添加到列表中。對我們來說幸運。方法支持將視圖添加到視圖組addView(view:View)。

即使是這樣,RecyclerView 也不是如何將子視圖添加到其中的。

在我們的用例中,我們會有一長串聯繫人。對於列表中的每個聯繫人,我們需要創建 OneContactView 並在 View 中填充資料以匹配 Contact 類中的字段。然後,一旦我們有了視圖,我們就需要將它添加到 RecyclerView 以顯示列表。

data class Contact(var name:String, var address:String, var pic:String, var phoneNumber:Int)

var contact1 = Contact("Guru","Guru97", "SomePic1.jpg", 991)
var contact2 = Contact("Guru","Guru98", "SomePic2.jpg", 992)
var contact3 = Contact("Guru","Guru99", "SomePic3.jpg", 993)

var myContacts:ArrayList = arrayListOf< Contact>(contact1,contact2,contact3)

我們有一個名為 OneContactView 的聯繫人數組。它包含從 Contact 類獲取內容並顯示它們的插槽。在 RecyclerView 中,我們必須將視圖添加到其中,以便它可以幫助我們實現其回收能力。

RecyclerView 並不能真正讓我們添加視圖,但可以讓我們添加 ViewHolder。因此,在這種情況下,我們有兩件想要連接但不匹配的東西。這就是我們的適配器的用武之地。RecyclerView 為我們提供了一個適配器,很像我們 AmericanToBritishChargerAdapter() 之前的適配器,它使我們能夠將我們的 BritishDevice 無法使用的 AmericanCharger 轉換為可用的東西,類似於現實生活中的電源適配器。

在這種情況下,適配器將獲取我們的聯繫人數組和我們的視圖,並從那裡生成 RecyclerView 願意接受的 ViewHolders。

RecyclerView 提供了一個接口,我們可以通過 RecyclerView.Adapter 類擴展來創建我們的適配器。在這個適配器內部是一種創建 RecyclerView 想要使用的 ViewHolder 類的方法。所以,我們的情況和之前一樣,但是多了一個東西,那就是適配器。

我們有一個聯繫人數組,一個顯示一個聯繫人 OneContactView 的視圖。RecyclerView 是提供回收服務但只願意承擔 ViewHolders 的 View 列表

但是在這個場景中,我們現在有了 RecyclerView.Adapter 類,它有一個在裡面創建 ViewHolders 的方法。

fun createViewHolder(@NonNull parent: ViewGroup, viewType: Int): ViewHolder

RecyclerView.ViewHolder 是一個抽像類,它將我們的 View 作為參數並將其轉換為 ViewHolder。

它使用用於擴展類能力的包裝器模式。

基礎#2——包裝模式

我們將使用一個簡單的示例來演示如何讓動物說話。

密封特性 Animal{
fun sound():String
}

data class Cat(name:String):Animal{
fun sound(){
"Meow"
}
}
data class Dog(name:String):Animal{
fun sound(){
"Woof "
}
}

var cat1 = Cat("Tubby")
var dog1 = Dog("Scooby")
cat1.sound() //meow
dog1.sound() //woof

在上面的例子中,我們有兩隻動物。如果偶然,我們想添加一個方法來使說話,但是庫作者不好玩,我們仍然可以找到方法。我們需要的是 Animal 類的包裝器。我們將通過將 Animal 作為我們類的構造函數來做到這一點

類 SpeechPoweredAnimalByWrapper(var myAnimal:Animal){

fun sound(){
myAnimal.sound()
}

speak(){
println("你好,我的名字是 ${myAnimal.name}")
}
}

現在我們可以將一個動物實例傳遞給 SpeechPoweredAnimalByWrapper。調用它的 sound() 方法將調用傳入的動物 sound() 方法。我們還有一個額外的 speak() 方法,它算作我們為傳入的動物添加的新功能。我們可以按如下方式使用它:

var cat1 = Cat("Garfield")
cat1.sound()//"meow"
cat1.speak()// doesn't work as it isn't implemented
var talkingCat = new SpeechPoweredAnimalByWrapper(cat1)
talkingCat.sound() //"meow" the sound method calls the one defined for cat1
talkingCat.speak() //"Hello, my name is Garfield"

使用這種模式,我們可以獲取類並添加功能。我們只需要傳遞一個類實例和我們的包裝類定義的新方法。

在我們上面的例子中,我們使用了一個具體的類。也可以在 Abstract 類中實現相同的功能。我們需要做的是將 SpeechPoweredAnimalByWrapper 類更改為抽像類,我們就完成了。我們將類名更改為更短的名稱以使其更具可讀性。

抽像類 SpeechPowered(var myAnimal:Animal){

fun sound(){
myAnimal.sound()
}

speak(){
println("你好,我的名字是 ${myAnimal.name}")
}
}

它和以前一樣,但它意味著別的東西。在普通類中,我們可以像創建 cat1 和 dog1 一樣擁有一個類的實例。然而,抽像類並不意味著被實例化,而是意味著擴展其他類。那麼我們將如何使用新的 SpeechPowered(var myAnimal:Animal) 抽像類。我們可以通過創建將擴展它的新類來使用它,進而獲得它的功能。

在我們的示例中,我們將創建一個擴展類 SpeechPoweredAnimal 的類

類 SpeechPoweredAnimal(var myAnimal:Animal):SpeechPowered(myAnimal)

var cat1 = Cat("Tubby")
var speakKitty = SpeechPoweredAnimal(cat1) speakKitty.speak
() //"你好,我叫 Tubby"

這與 ViewHolder 中使用的模式相同。RecyclerView.ViewHolder 類是一個抽像類,它為 View 添加功能,就像我們為動物添加了 speak 方法一樣。添加的功能是它在處理 RecyclerView 時工作的原因。

這就是我們從 OneContactView 創建 OneContactViewHolder 的方式

//The View argument we pass is converted to a ViewHolder which uses the View to give it more abilities and in turn work with the RecyclerView
class OneContactViewHolder(ourContactView: View) : RecyclerView.ViewHolder(ourContactView)

RecyclerView 有一個適配器,它允許我們使用 RecyclerView 將 Contacts 數組連接到 ContactsView

添加視圖

ViewGroup 不會自動重繪 ViewGroup,而是遵循特定的時間表。您的設備可能會每 10 毫秒或 100 毫秒重繪一次,或者如果我們選擇一個荒謬的數字,比如 1 分鐘,當我們將視圖添加到視圖組時,您將在 1 分鐘後視圖組“刷新”時看到更改。

RecyclerView.Recycler

基礎#3。緩存

我們定期刷新的最佳示例之一是在瀏覽器中。例如,假設我們正在訪問的站點是靜態的並且沒有動態發送內容,我們需要不斷刷新才能看到變化。

對於這個例子,讓我們假設有問題的網站是 twitter。我們將列出一系列靜態推文,我們可以看到新推文的唯一方法是通過單擊刷新按鈕重新獲取內容。

重新粉刷整個屏幕顯然是一件代價高昂的事情。如果我們想像如果是這樣的話,我們的電話提供商的帶寬是有限的。而且我們的推文列表有很多圖片和視頻,每次刷新都重新下載頁面的所有內容會很昂貴。

我們需要一種方法來存儲已經加載的推文,並確保我們的下一個請求能夠說出它已經擁有的推文。因此,它不會重新下載所有內容,只會獲取它擁有的新推文,並檢查是否已在本地保存的某些推文不再存在,以便可以在本地將其刪除。我們所描述的稱為緩存。

我們向網站發送的關於我們擁有的內容的信息稱為元資料。所以實際上我們不僅僅是說“我們要加載你的網站”,我們說“我們要加載你的網站,這是我們上次加載時已經保存的一些內容,請用它來只向我們發送不存在的內容,因此我們不會使用大量帶寬,因為我們沒有很多資源。”

佈局調用——推文列表一定很瘋狂

佈局調用的一個示例是 scrollToPosition

這是一個常見的例子,出現在聊天應用程序中。如果聊天線程中的某人回復了之前的聊天氣泡,則某些聊天應用程序會包含回復和聊天氣泡的鏈接,單擊該鏈接可將您導航到原始消息所在的位置。

在這種情況下,我們在將 LayoutManager 添加到 RecyclerView 之前調用此方法,並且在我們擁有 RecyclerView.Adapter 之前,scrollToPosition(n:Int) 會被忽略。

RecyclerView 組件之間的通信

基礎#4。回調

RecyclerView 在工作時有很多活動部件。它必須處理 LayoutManager,它告訴我們如何以線性方式或在網格中組織視圖。它必須處理一個適配器,該適配器將我們的項目contactList轉換為Views OneContactView,然後轉換為RecyclerView願意在它為我們提供的方法內工作的ViewHolders OneContactViewHolder。

RecyclerView 的原材料是我們的視圖,例如 OneContactView 和資料源。

contactList:Array

我們使用一個簡單的場景作為起點來了解 RecyclerView 試圖實現的目標。

當我們想要向用戶展示一個包含 1000 個聯繫人的靜態數組時的基本情況很容易理解。

當列表不再是靜態的時,RecyclerView 機制真正開始發揮作用。

對於動態列表,當我們向列表中添加項目或從列表中刪除項目時,我們必須考慮屏幕上的視圖會發生什麼。

RecyclerView.LayoutManager

除了決定我們的視圖如何以線性或網格佈局。LayoutManager 在幫助 Recycler 知道何時進行 Recycling 方面做了很多工作。

它負責跟踪當前在屏幕上可見的視圖,並將此信息傳達給回收機制。當用戶向下滾動時,佈局管理器負責通知回收系統在頂部失去焦點的視圖,以便它們可以被重用,而不是保留在那裡並消耗內存,或者而不是銷毀它們並不得不創建新的。

這意味著 LayoutManager 需要在用戶滾動我們的列表時跟踪他們的位置。它通過一個以索引為基礎的位置列表來做到這一點,即第一項是從 0 開始並增加以匹配我們列表中的項目數。

如果我們可以在我們的列表中查看 100 個項目,比如 100,一開始,LayoutManager 知道它一直在焦點 view-0 到 View-9 當我們滾動時,LayoutManager 能夠計算出的視圖的焦點。

LayoutManager 能夠將這些視圖釋放到回收機制,以便它們可以被重用(可以將新資料綁定到它們,例如可以刪除視圖的聯繫人資料,並且來自下一個段的新聯繫人資料可以替換佔位符)。

如果我們擁有的列表是靜態的,這將是一個很好的案例,但是使用 RecyclerView 的最常見用例之一是使用動態列表,其中資料可以來自在線端點,甚至可能來自傳感器。不僅添加了資料,而且我們列表中的資料有時也會被刪除或更新。

我們資料的動態狀態可能使我們很難推斷 LayoutManager。出於這個原因,LayoutManager 維護一個關於項目和位置的自己的列表,該列表與回收組件使用的列表是分開的。這可以確保它正確地完成它的佈局工作。

同時,RecyclerView 的 LayoutManager 不想歪曲它擁有的資料。為了正確操作,LayoutManager 會以給定的時間間隔(60 毫秒)與 RecyclerView.Adapter 同步,共享有關我們的列表項的信息。即,添加、更新、刪除、從一個位置移動到另一個位置的項目)。LayoutManager 收到此信息後,會在必要時重新組織屏幕上的內容以匹配更改。

許多處理 RecylerView 的核心操作都圍繞著 RecyclerView.LayoutManager 和 RecyclerView.Adapter 之間的通信進行,後者存儲了我們有時是靜態的或有時是動態的資料列表。

更重要的是,RecyclerView 為我們提供了一些方法,當我們的 RecyclerView.Adapter 綁定來自 List 的內容時,我們可以使用這些方法來監聽諸如 onBindViewHolder 之類的事件,例如將聯繫人綁定到 ViewHolder,以便它現在習慣於在屏幕上顯示信息。

另一個是 onCreateViewHolder,它告訴我們 RecyclerView 何時。適配器採用 OneContactView 之類的常規 View 並將其轉換為 RecyclerView 可以使用的 ViewHolder 項。從我們的 ViewHolder 中供 RecyclerView 使用。onViewDetached

除了啟用回收的核心機制。RecyclerView 提供了在不影響回收的情況下自定義行為的方法。

視圖的重用使得我們很難做我們習慣用靜態視圖做的常見事情,比如對 onClick 事件做出反應。

正如我們所知道的那樣RecyclerView.LayoutManager,向用戶呈現視圖的 可能暫時具有與 RecyclerView.Adapter我們存儲在資料庫中或從源流中的列表不同的項目列表。將 OnClick 事件直接放在 Views 上可能會導致意外行為,例如刪除錯誤的聯繫人或更改聯繫人。

搖籃

如果我們想使用 RecyclerView,我們需要將它作為依賴添加到我們的 build .gradle 文件中。

在以下示例中,我們使用了實現“androidx.recyclerview:recyclerview:1.1.0”,這是本文中的最新版本。

將依賴添加到我們的 Gradle 文件後,Android Studio 會提示我們同步更改,

這就是我們的 Gradle 文件在使用默認值的空項目中添加 RecyclerView 後的樣子。

apply plugin: 'com.android.application'

apply plugin: 'kotlin-android'

apply plugin: 'kotlin-android-extensions'

android {
compileSdkVersion 29
buildToolsVersion "29.0.2"
defaultConfig {
applicationId "com.guru99.learnrecycler"
minSdkVersion 17
targetSdkVersion 29
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'),
'proguard-rules.pro'
}
}
}

dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
implementation"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.core:core-ktx:1.1.0'
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'

implementation "androidx.recyclerview:recyclerview:1.1.0"
}

我們目前只有一個佈局文件。我們將從一個簡單的示例開始,我們將使用 RecyclerView 在屏幕上顯示水果名稱列表。

物品清單

我們將導航到 MainActivity 文件並在設置期間生成的 onCreate() 方法之前創建一個包含水果名稱的數組。

package com.guru99.learnrecycler

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle

class MainActivity : AppCompatActivity() {

var fruitNames:Array< String> = arrayOf< String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
}

我們的下一個目標是使用 RecyclerView 在屏幕上顯示此列表。

為此,我們將導航到包含佈局的佈局目錄並創建一個負責顯示一個水果的視圖。

用於我們列表中每個項目的佈局

< ?xml version="1.0" encoding="utf-8"?>
< TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height ="match_parent"
android:id="@+id/fruitName"
/>
< /TextView>

在上面的 TextView 中,我們添加了一個 id 字段,用於標識 View。

默認情況下不生成。我們有我們的 TextView 的 id fruitName 來匹配將綁定到它的資料。

將 RecyclerView 添加到主佈局

在同一個活動中,默認為我們生成了 main_layout.xml 佈局文件。

如果我們選擇一個空項目。它會生成一個包含 ConstraintLayout的XML ,內部將是一個帶有“Hello”文本的 TextView。

我們將刪除所有內容並讓佈局僅包含 RecyclerView,如下所示:

< ?xml version="1.0" encoding="utf-8"?>
< androidx.recyclerview.widget.RecyclerView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app= "http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/fruitRecyclerView"
android:layout_width=" match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />

我們還為 RecyclerView 添加了一個 id 屬性,我們將使用它在代碼中引用它。

android:id="@+id/fruitRecyclerView"

然後我們將導航回我們的 MainActivity 文件。使用我們創建的 id,我們將能夠引用我們剛剛創建的視圖。

我們將從使用Android提供的 findViewById() 方法引用 RecyclerView 開始。我們將在 onCreate() 方法中執行此操作。

我們的 onCreate() 方法如下所示。

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}

創建一個 ViewHolder

接下來,我們將創建一個 RecyclerView.ViewHolder,它負責獲取我們的 View 並將其轉換為 ViewHolder,RecyclerView 使用它來顯示我們的項目。

我們將在有趣的 onCreate() 方法之後立即執行此操作。

package com.guru99.learnrecycler

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {
var fruitNames:Array< String> = arrayOf< String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}

class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

}

創建一個 RecyclerViewAdapter

接下來,我們將創建一個擴展 RecyclerView.Adapter 類的 FruitArrayAdapter 類。

我們創建的 FruitArrayAdapter 將負責執行以下操作。

它將從水果數組中獲取水果名稱。它將使用我們的視圖 one_fruit_view.xml 創建一個 ViewHolder,然後將水果綁定到一個 ViewHolder,並將內容動態綁定到我們創建的視圖 one_fruit_view.xml。

package com.guru99.learnrecycler

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {

var fruitNames:Array< String> = arrayOf< String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)
}

class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

class FruitArrayAdapter(var fruitArray: Array< String>) : RecyclerView.Adapter< FruitViewHolder>()
}

Android Studio 會在我們的 FruitArrayAdapter 上添加紅色波浪線,告訴我們需要實現一個 RecyclerView 可以用來將我們的數組連接到 RecyclerView 可以使用的 ViewHolder 的方法。

class FruitListAdapter(var fruitArray: Array< String>) : RecyclerView.Adapter< FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

}

override fun getItemCount(): Int {

}

override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

}
}


我們將從生成的代碼中最簡單的部分開始,即 getItemCount() 方法。我們知道如何通過調用 array.length 方法來獲取數組中的項目數。

class FruitListAdapter(var fruitArray: Array< String>) : RecyclerView.Adapter< FruitViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

}

override fun getItemCount(): Int {
return fruitArray.size
}

override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

}
}

然後我們將實現方法override fun onCreateViewHolder。

這就是 RecyclerView 要求我們幫助它為它構造一個 FruitHolder 的地方。

如果我們回想一下 FruitViewHolder 類的樣子:

lass FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

它需要我們創建為 xml 文件 one_fruit_view.xml 的fruitView

我們可以創建對此 xml 的引用並將其轉換為視圖,如下所示。

class FruitListAdapter(var fruitArray: Array< String>) : RecyclerView.Adapter< FruitViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)

var fruitViewHolder = FruitViewHolder(fruitView)

return fruitViewHolder

}

override fun getItemCount(): Int {
return fruitArray.size
}

override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

}
}


剩下的位是覆蓋

fun onBindViewHolder(holder: FruitViewHolder, position: Int)

RecyclerView.Adapter 詢問位置整數,我們將使用它從列表中獲取項目。它還為我們提供了一個持有者,以便我們可以將我們從fruitArray 獲得的項目綁定到被持有在視圖持有者內的視圖。

可以通過 ViewHolder.itemView 字段訪問 ViewHoder 中保存的視圖。一旦我們得到視圖,我們就可以使用我們之前創建的 id fruitName 來設置內容。

override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

var ourFruitTextView = holder.itemView.findViewById< TextView>(R.id.fruitName)

var aFruitName = fruitArray.get(position)

ourFruitTextView.setText(aFruitName)
}


這樣我們的 FruitArrayAdapter 就完成了,如下所示。

class FruitListAdapter(var fruitArray: Array< String>) : RecyclerView.Adapter() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)

var fruitViewHolder = FruitViewHolder(fruitView)

return fruitViewHolder
}

override fun getItemCount(): Int {
return fruitArray.size
}

override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

var ourFruitTextView = holder.itemView.findViewById< TextView>(R.id.fruitName)

var aFruitName = fruitArray.get(position)

ourFruitTextView.setText(aFruitName)
}
}

最後,我們準備連接 RecyclerView 的剩餘部分。其中正在創建一個 LayoutManager,它將告訴 RecyclerView 如何顯示列表的內容。是使用 LinearLayoutManager 以線性方式顯示,還是使用 GridLayoutManager 或 StaggeredGridLayoutManager 在網格中顯示。

創建佈局管理器

我們將回到我們的 onCreate 函數並添加 LayoutManager

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)

var fruitLinearLayout = LinearLayoutManager(this)

myFruitRecyclerView.layoutManager =fruitLinearLayout

}

將我們的適配器連接到項目並將其設置在 RecyclerView

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)

var fruitLinearLayout = LinearLayoutManager(this)

myFruitRecyclerView.layoutManager =fruitLinearLayout

var fruitListAdapter = FruitListAdapter(fruitNames)

myFruitRecyclerView.adapter =fruitListAdapter
}


我們還創建了一個fruitListAdapter 實例,並為其提供了水果名稱數組。

基本上,我們都完成了。

完整的 MainActivity.kt 文件如下所示。

Tpackage com.guru99.learnrecycler

import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView

class MainActivity : AppCompatActivity() {

var fruitNames:Array< String> = arrayOf< String>("Banana", "Mango", "Passion fruit", "Orange", "Grape")

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

var myFruitRecyclerView: RecyclerView = findViewById(R.id.fruitRecyclerView)

var fruitLinearLayout = LinearLayoutManager(this)

myFruitRecyclerView.layoutManager =fruitLinearLayout

var fruitListAdapter = FruitListAdapter(fruitNames)

myFruitRecyclerView.adapter =fruitListAdapter
}

class FruitViewHolder(fruitView: View): RecyclerView.ViewHolder(fruitView)

class FruitListAdapter(var fruitArray: Array< String>) : RecyclerView.Adapter< FruitViewHolder>() {

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FruitViewHolder {

var fruitView = LayoutInflater.from(parent.context).inflate(R.layout.one_fruit_view, parent, false)

var fruitViewHolder = FruitViewHolder(fruitView)

return fruitViewHolder
}

override fun getItemCount(): Int {
return fruitArray.size
}

override fun onBindViewHolder(holder: FruitViewHolder, position: Int) {

var ourFruitTextView = holder.itemView.findViewById< TextView>(R.id.fruitName)

var aFruitName = fruitArray.get(position)

ourFruitTextView.setText(aFruitName)
}
}
}

android 十全大補

android 十全大補

with

add

mphotoalbum

十全大補

your

實作

實作

實作

實作

實作