iT邦幫忙

2018 iT 邦幫忙鐵人賽
DAY 6
1
Software Development

Kotlin 30 天,通過每天一個小 demo 學習 Android 開發系列 第 6

Kotlin 開發第 6 天 ImageList (RecyclerView)

Android ImageList
今天做一個這樣的練習,可以通過畫面滾動的方式來瀏覽 15 張圖片以及對應的描述。

類似 iOS 開發中會用到的 UITableView / UICollectionView。

  • 自定義每一個 item (cell) 的樣式,上方為圖片,下方為描述。
  • 手勢滑動主畫面時可以看到不同的內容。

RecyclerView

類似於 UICollectionView,只是使用起來不太一樣。
我們需要為 RecyclerView 提供一個 Adapter,並且給他設定一個 layoutManager。

在 MainActivity 中,看起來就是下面這樣而已,而像是多少數量、將資料呈現在 View 上等動作,都是交給 Adapter。
這裡的 Adapter 有點像 UICollectionView 的 Datasource & Delegate 方法提供者。

imageListRecyclerView.layoutManager = LinearLayoutManager(this)
 
val adapter = ImageListAdapter(imageList)
imageListRecyclerView.adapter = adapter

Adapter

我們建立了一個 ImageListAdapter ,定義 ImageListAdapter 可以:

  • 接受 List
  • 返回 RecyclerView.Adapter<ImageListAdapter.ViewHolder>

在 Android 中,各種資源的都會有一個對應的 ID 來進行存取,比如 R.drawable.img_1 這不像 iOS 中可以直接根據圖片名稱來進行存取。

下面在 Adapter 中,我們通過圖片名稱轉換成對應的 Resource ID (這樣做是因為一開始沒注意到 R.drawable.img_1 是 Int)

class ImageListAdapter(val feedModelItems: List<ImageModel>) : RecyclerView.Adapter<ImageListAdapter.ViewHolder>() {

    // 入口
    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
        // 指定了 layout
        val view = LayoutInflater.from(parent.context).inflate(R.layout.layout_image_list_item, parent, false)
        return ViewHolder(view)
    }

    // 綁定資料
    override fun onBindViewHolder(holder: ViewHolder?, position: Int) {
        holder?.bindImageModel( feedModelItems[position] )
    }

    // 返回數目
    override fun getItemCount(): Int {
        return feedModelItems.size
    }

    // view
    inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
        fun bindImageModel(imageModel: ImageModel){
            // set description
            itemView.descriptionTextView.text = imageModel.description

            // set image
            when(imageModel.imageName){
                "img_1" -> itemView.imageView.setImageResource(R.drawable.img_1)
                "img_2" -> itemView.imageView.setImageResource(R.drawable.img_2)
                "img_3" -> itemView.imageView.setImageResource(R.drawable.img_3)
                "img_4" -> itemView.imageView.setImageResource(R.drawable.img_4)
                "img_5" -> itemView.imageView.setImageResource(R.drawable.img_5)
                "img_6" -> itemView.imageView.setImageResource(R.drawable.img_6)
                "img_7" -> itemView.imageView.setImageResource(R.drawable.img_7)
                "img_8" -> itemView.imageView.setImageResource(R.drawable.img_8)
                "img_9" -> itemView.imageView.setImageResource(R.drawable.img_9)
                "img_10" -> itemView.imageView.setImageResource(R.drawable.img_10)
                "img_11" -> itemView.imageView.setImageResource(R.drawable.img_11)
                "img_12" -> itemView.imageView.setImageResource(R.drawable.img_12)
                "img_13" -> itemView.imageView.setImageResource(R.drawable.img_13)
                "img_14" -> itemView.imageView.setImageResource(R.drawable.img_14)
                "img_15" -> itemView.imageView.setImageResource(R.drawable.img_15)
            }

        }
    }
}

建立Layout

就像 iOS 的 UICollectionViewCell 一樣,我們想要為 RecyclerView 的 Item 定義自己想要的 Layout。

比如說,我們想要每一個 item 上面是一張長條的圖片,下面是一些描述。

https://ithelp.ithome.com.tw/upload/images/20171209/20107329tW4LJiG4zU.png

我們在 Layout 中建立一個 layout_image_list_item.xml 的檔案,用來定義上面的 Layout。

https://ithelp.ithome.com.tw/upload/images/20171209/20107329BZOJbvPMZr.png

這裡和 iOS 中的 Storyboard 很不一樣的是,
雖然我們定義了一個 item 設定圖片高度 220dp 描述背景 40dp ,合起來260dp。
但實際上在 Design 介面中看到的還是一個完整的手機樣式,不像 iOS 的 Storyboard 只會顯示 cell 的大小。

我一開始以為這樣就可以了,但結果到手機上跑的時候,一開始只有看到一個 Item,
結果發現一個 Item 就像 Design 介面一樣占滿了整個畫面。

後來就去 Text 下手動修改了高度。

<android.support.constraint.ConstraintLayout
    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:layout_width="match_parent"
    android:layout_height="160dp">

   // ...

</android.support.constraint.ConstraintLayout>

建立 Model

這次我想要為資料建立兩個 Model。

ImageModel 用來定義一張圖片資料會有圖片名、描述兩個字段。
ImageListModel 用來定義一個 list 當中存的會是 ImageModel類型的資料。

建立 Model 資料夾

在 Android Studio 中,在 Project 中建立 Folder 會有分類,我這裡為 Model 建立了一個叫做 Package 的 Folder

在 Android Studio 中,在 Project 中建立 Folder 會有分類,我這裡為 Model 建立了一個叫做 Package 的 Folder

https://ithelp.ithome.com.tw/upload/images/20171209/20107329ZlvviSVivO.png

定義 Model

在 iOS 開發過程中,我通常用 Struct 來定義 Model,但 Github 上逛了一下,看到通常用 data class 來定義

ImageModel

data class ImageModel ( val imageName: String, val description: String)

ImageListModel

data class ImageListModel(val data: List<ImageModel>)

通過 model 封裝數據後傳值

fun bindImageModel(imageModel: ImageModel){
    itemView.descriptionTextView.text = imageModel.description
}

IDE 自動補全方法

這邊用 Adapter 舉一個例子,這邊定義了 ListAdapter() 並且會回傳一個 ViewHolder。

class ListAdapter(): RecyclerView.Adapter<ImageListAdapter.ViewHolder>() { ... }

然後我們在 ListAdapter() 這邊按下,Option + Enter(Mac 系統),就會看到 Implement Members 的選項。

https://ithelp.ithome.com.tw/upload/images/20171209/20107329wEoX50Cxf2.png

接著就可以選擇想要補全的方法。

https://ithelp.ithome.com.tw/upload/images/20171209/20107329lD9QF85UOj.png

Android Studio 就會幫我們都補上了。

class ListAdapter(): RecyclerView.Adapter<ImageListAdapter.ViewHolder>() {
    override fun onBindViewHolder(holder: ImageListAdapter.ViewHolder?, position: Int) {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ImageListAdapter.ViewHolder {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }

    override fun getItemCount(): Int {
        TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
    }
}

參考


上一篇
Kotlin 開發第 5 天 Webview Search ( WebView + Keyboard )
下一篇
Kotlin 開發第 7 天 MyLocation (GoogleMap)
系列文
Kotlin 30 天,通過每天一個小 demo 學習 Android 開發30

尚未有邦友留言

立即登入留言