iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 9
0
Mobile Development

大一之 Android Kotlin 自習心路歷程系列 第 9

[Day 9] Android in Kotlin: 簡單的 Recycler View

在 android 中看到像是底下這種可以上下滑的列表類資料,可以用 recycler view 呈現,是一個使用非常非常頻繁的元件。

如果資料超出畫面的話

可以上下滑顯示所有資料

起初都會做的手忙腳亂,多寫幾次就不會了

先從 build.gradle(Module:app) 中加入依賴

dependencies {
    implementation "androidx.recyclerview:recyclerview:1.1.0"
}

這裡採用的是 androidx 而版本是 1.1.0

XML

在要用來顯示列表的主要畫面加上 recycler view 的 widget
這裡裝一個滿版的 recycler view

 <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

還要一個 layout 用來表示一個 item 的畫面。這裡就簡單寫個 text view 跟 button

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:background="@color/colorPrimary"
    android:layout_margin="20dp"
    android:padding="10dp"
    android:orientation="horizontal"
    android:gravity="center_vertical">

    <TextView
        android:id="@+id/textId"
        android:text="id"
        android:textSize="20sp"
        android:textColor="@android:color/white"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>
    
    <Button
        android:id="@+id/btnClick"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="click"
        android:textAllCaps="false"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintBottom_toBottomOf="parent"/>

</androidx.constraintlayout.widget.ConstraintLayout>

現在我們有了畫面,再來呢,還需要一個 adapter 控制每個 item 的內容

Adapter

建立一個 class 並使其繼承於 RecyclerView.Adapter<>()
除此之外,我還想要用 constructor 接收 context 跟 data。context 是用來

class RecyclerViewAdapter(
    private val context: Context, 
    private val data: List<String>
    ): RecyclerView.Adapter<>() {
    
}

也可以傳入 view model 給 adapter 取得資料

如果在這階段直接對class的紅底線按 alt+Enter 實現父類別方法的話,會有 ??? 的產生

class RecyclerViewAdapter(
    private val context: Context, 
    private val data: List<String>
    ): RecyclerView.Adapter<>() {
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ??? {
        TODO("Not yet implemented")
    }

    override fun onBindViewHolder(holder: ???, position: Int) {
        TODO("Not yet implemented")
    }

    override fun getItemCount(): Int {
        TODO("Not yet implemented")
    }
}

是因為他不知道要放什麼東西,所以要建立一個內部類別叫做 view holder,並放進角括號中做泛型

class RecyclerViewAdapter(
    private val context: Context, 
    private val data: List<String>
    ): RecyclerView.Adapter<RecyclerViewAdapter.MyViewHolder>() {
    class MyViewHolder(itemView: View): RecyclerView.ViewHolder(itemView){
        val textId: TextView= itemView.findViewById(R.id.textId)
        val btnClick: Button= itemView.findViewById(R.id.btnClick)
    }
}

item 中的各個元件是在這裡面宣告,可以在這時候先寫好。完成後再去實現,就不會有 ??? 了

實現完後多出的三個方法

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    TODO("Not yet implemented")
}

override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    TODO("Not yet implemented")
}

override fun getItemCount(): Int {
    TODO("Not yet implemented")
}

三個方法的功能分別為

onCreateViewHolder()

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MyViewHolder {
    return MyViewHolder(LayoutInflater.from(context).inflate(R.layout.item_recycler_view, parent, false))
}

中文翻譯就是創立 view holder,並且要將其傳回給 item 用。中間塞入 item 的 layout。

onBindViewHolder

第二個是每一個 item 在建立的時候會跑的方法,也就是你的資料有多少筆,他就會跑幾次。

override fun onBindViewHolder(holder: MyViewHolder, position: Int) {
    holder.apply{
        textId.text= data[position]
        btnClick.setOnClickListener {
            Toast.makeText(context, "按下第 ${position+ 1} 個", Toast.LENGTH_SHORT).show()
            // position 從零開始的
        }
    }
}

這個方法負責每一個 item 要顯示的東西或監聽事件。剛剛寫的 MyViewHolder 會從上面那個方法傳進來,就可以更改其內容。

getItemCount()

最後這個最簡單,就只要回傳總共要顯示多少筆資料而已

override fun getItemCount(): Int = data.size

因為我們有從建構元中拿到資料,回傳其 size 即可
完成後就可以回到 activity 做最後準備了

最後

一個 recycler view 除了需要剛剛做的 adapter 以外,還要有 layout manager,其中當然不只有 LinearLayoutManager能夠做使用,還可以用其他的 manager 做出不同的變化。用 setter 設置 adapter 跟 layout manager

val recyclerView: RecyclerView = findViewById(R.id.recyclerView)
recyclerView.apply {
    adapter= RecyclerViewAdapter(context, listOf("第一個", "第二個", "第三個", "第四個", "第五個"))
    layoutManager= LinearLayoutManager(context)
}

完成了以後就來看看成果吧。


點選按鈕的時候,會跳出 Toast 顯示是第幾個


上一篇
[Day 8] Android in Kotlin: Button 點擊監聽器
下一篇
[Day 10] Android in Kotlin: Fragment 換頁使用分享
系列文
大一之 Android Kotlin 自習心路歷程30

尚未有邦友留言

立即登入留言