iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 4
0
Mobile Development

Android Kotlin開發 -小嫩雞的30篇精選筆記系列 第 4

Android x Kotlin : Recyclerview(三)-能上下滑又能左右滑的巢狀玩法

簡介

一個recyclerview裡可以再裝recyclerview,俄羅斯娃娃啦,懂?
今天要做的是一個縱向rview裡裝著一個橫向滑動的rview,縱向rview裝有大卡片及banner卡片兩種viewtype,橫向滑動rview則只裝有小卡片一種viewtype。我們會由adapter中的viewtype來判別要回傳哪一種viewholder。
以下會用rview或r_view來稱呼recyclerview。


圖片來源:CCC創作集

1. activity_main.xml

先放一個recyclerview,這是縱向(外層)rview

<androidx.recyclerview.widget.RecyclerView

        android:id="@+id/r_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

</androidx.recyclerview.widget.RecyclerView>

2. res->layout->

建一個xml檔,取名container_horiz,當作橫向(內層)rview的container(容器),然後裡面一樣放一個recyclerview。

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

</androidx.recyclerview.widget.RecyclerView>

3. 隨便建個class

寫一個dataModel,定義資料格式。

data class Item(
    val img:Int,
    val title:String,
    val content:String,
    val viewType:Int
)

//viewType:
// 1->小卡片
// 2->大卡片
// 3->banner卡片

val mainList = arrayListOf<Item>()
val horizList = arrayListOf<Item>()



//產生橫向(內層)rview資料
fun hDataGenerate(){
    for(i in 0..5){
        horizList.add(Item(R.drawable.work_smallcard,"滑動作品小卡片","文字介紹",1))
        horizList.add(Item(R.drawable.subject_smallcard,"滑動專題小卡片","文字介紹",1))
    }

}
//產生縱向(外層)rview的資料
fun dataGenerate(){

    for(i in 0..3){
        //banner卡片不需要顯示title跟content
        mainList.add(Item(R.drawable.bn_05,"","",3))
        mainList.add(Item(R.drawable.work_smallcard,"作品小卡片","文字介紹",1))
        mainList.add(Item(R.drawable.work_bigcard,"作品大卡片","文字介紹",2))        
    }
}

4. res->layout->

建一個layout檔,取名item_slidecard,裡面寫橫向rview的項目模板。

        <ImageView
            android:id="@+id/imgv_slidecard"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            />

        <TextView
            android:id="@+id/tv_title_slidecard"
            android:layout_width="0dp"
            android:layout_height="30dp"
            />

5. res->layout->

建一個layout檔,取名item_bigcard,裡面寫縱向rview的項目模板。(這裡就不把每種viewtype都寫一遍了,僅寫大卡片作為代表)

         <ImageView
            android:id="@+id/imgv_bigcard"
            android:layout_width="match_parent"
            android:layout_height="0dp"
            />

        <TextView

            android:id="@+id/tv_title_bigcard"
            android:layout_width="match_parent"
            android:layout_height="40dp"

        <TextView
            android:id="@+id/tv_content_bigcard"
            android:layout_width="match_parent"
            android:layout_height="40dp"
            
            />

6. 建一個adapter

幫橫向rview寫一個adapter,取名HorizontalAdapter,沒啥特別就是一個普通的adapter。

class HorizontalAdapter:RecyclerView.Adapter<HorizontalAdapter.mViewHolder>() {

    var localList = arrayListOf<Item>()

    inner class mViewHolder(itemView: View):RecyclerView.ViewHolder(itemView){
        val img = itemView.imgv_slidecard
        val title = itemView.tv_title_slidecard

        fun bind(item:Item){
            img.setImageResource(item.img)
            title.text = item.title
        }
    }

    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): mViewHolder {
                val inflater = LayoutInflater.from(parent.context)
                val itemView = inflater.inflate(R.layout.item_slidecard,parent,false)
                return mViewHolder(itemView)
    }

    override fun getItemCount() = localList.size

    override fun onBindViewHolder(holder: mViewHolder, position: Int) {
        holder.bind(localList[position])

    }

    //更新資料用
    fun updateList(list:ArrayList<Item>){
        localList = list
    }


}

7. 再建一個adapter

重點來了。幫縱向rview寫一個adapter,取名MainAdapter

class MainAdapter(val context:Context)
:RecyclerView.Adapter<RecyclerView.ViewHolder>() {

    var emptyList = arrayListOf<Item>()

    //把水平rview元件拉進來
    inner class horizontalViewHolder(itemView:View)
    :RecyclerView.ViewHolder(itemView){
        val rView = itemView.r_view_horizantal
    }

    //這裡跟原本一樣,把大卡片項目模板的元件們拉進來
    inner class bigCardViewHolder(itemView:View)
    :RecyclerView.ViewHolder(itemView){
        val img = itemView.imgv_bigcard
        val title = itemView.tv_title_bigcard
        val content = itemView.tv_content_bigcard

        fun bind(item:Item){
            img.setImageResource(item.img)
            title.text = item.title
            content.text = item.content
            
        }
    }
    
    override fun getItemViewType(position: Int): Int {
        return emptyList[position].viewType
    }

    override fun onCreateViewHolder(parent:ViewGroup,viewType: Int)
    : RecyclerView.ViewHolder {
        return when(viewType){
            0->{
            
            //這裡把container載入進來,做出巢狀recyclerview
                val inflater = LayoutInflater.from(parent.context)
                val itemView = inflater.inflate(R.layout.container_horiz,parent,false)

                //設定水平滑動的recyclerview
                //因為綁adapter跟layoutmanager這些是一次性的東西,如果寫在onBindViewHolder,就會在使用者每一次下滑捲動 系統更新項目資料的時候一直重複呼叫!
                    val adapter = HorizontalAdapter()
                    adapter.updateList(horizList)
                    val linearlayout = LinearLayoutManager(context)
                    linearlayout.orientation = RecyclerView.HORIZONTAL
                    HorizontalViewHolder(itemView).rView.layoutManager = linearlayout
                    HorizontalViewHolder(itemView).rView.adapter = adapter
                    HorizontalViewHolder(itemView)
            }
            1->{
                //載入大卡片的項目模板
                val inflater = LayoutInflater.from(parent.context)
                val itemView = inflater.inflate(R.layout.item_bigcard,parent,false)
                bigCardViewHolder(itemView)
            }
            2->{
            ........
            }
            else->{
            .......
            }

        }
    }

    override fun getItemCount() = emptyList.size

    override fun onBindViewHolder(
    holder:RecyclerView.ViewHolder,
    position: Int) {
        when(holder ){
            is horizontalViewHolder->{}
            is bigCardViewHolder -> holder.bind(emptyList[position])
        }
    }

    //更新資料用
    fun updateList(list:ArrayList<Item>){
        emptyList = list
    }


    


}

8. MainActivity.kt

回到MainActivity去設定縱向rview的adapter及layoutManager

class MainActivity : AppCompatActivity() {

    val adapter = MainAdapter(this)

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

        //產生資料
        dataGenerate()
        hDataGenerate()
        
        //設定縱向rview
        adapter.updateList(mainList)
        r_view.adapter = adapter
        r_view.layoutManager = LinearLayoutManager(this)
        adapter.notifyDataSetChanged()

    }

}

Done.


上一篇
Android x Kotlin : RecyclerView(二)-項目的拖曳換位及左右滑動刪除
下一篇
Android x Kotlin: BottomNavigationView底部導覽欄
系列文
Android Kotlin開發 -小嫩雞的30篇精選筆記30

尚未有邦友留言

立即登入留言