iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 20
0
自我挑戰組

Kotlin Everyday:新手寫程式踩的坑系列 第 20

Day 20 ─用 Kotlin 做待辦清單 Todolist(4) 給RecyclerView Item添加點擊事件

  • 分享至 

  • xImage
  •  

完成基本 Todolist 之後,要為它新增一個「編輯」功能,在每筆資料後面都有一個編輯圖案,點選之後會跳到編輯模式,可以修改字串,看似不難的練習卻花了好幾天才看懂,注意練習重點:

  • 新增模式和編輯模式的差異
  • 編輯模式的 EditText 上面會顯示要修改的字串
  • 兩者的標題文字不同
  • 修改後要怎麼儲存,才會改變到 SharedPreferences 的值

細節很多,讓我們一一寫清楚
首先,使用 RecyclerView 可以做出簡潔好用的布局,但是要實現裡面 Item 的點擊事件卻很麻煩、沒有直接方法可以調用,網路上的做法也是看得眼花撩亂,因為這裡資訊量頗多,筆記一下其中一種做法來提醒自己。

最初,我是在 onBindViewHolder() 方法中直接實現點選事件,寫法雖然可行、卻被建議不要這樣寫,最好是把所有用來實現功能的 code 都寫在 MainActivity、而非 Adapter,Adapter是用來連結資料和畫面的。
這個練習,是參考之前在好想工作室的活動 Kotlin EveryWhere 第四週實作的程式碼完成,同時看 android 開發:給 RecyclerView 的 item 新增點選事件稍微了解每個步驟在幹嘛,有興趣的可以先看一遍再跟著做:

一、做編輯按鈕

1. 新增每筆資料的編輯圖示
Day 8 ─做一個ImageList (上) 裡面有寫到每筆資料的素材、怎麼設 Adapter 範例畫面,這次是要在每筆資料中新增一個按鈕,所以打開放畫面樣式的 xml 檔,在 TextView 旁新添加一個鉛筆圖案,要用作編輯內容的點擊按鈕

2. 在 ViewHolder 裡面綁定編輯圖示
回到 MyAdapter.kt 中,到 ViewHolder 去綁 itemView,同時讓這個 img_edit 可以被點擊(尚未設定點擊之後觸發的事)

inner class ViewHolder(v: View):RecyclerView.ViewHolder(v){
    val tv_todo = v.findViewById<TextView>(R.id.tv_todo)
    val img_edit = v.findViewById<ImageView>(R.id.img_edit)
           //綁畫面元件
    fun bind(Thing: Thing){
        tv_todo.setText(Thing.todo)
        img_edit.setOnClickListener{
           //為它設一個 setOnClickListener,之後放點擊事件
        }
    }
}

二、為 RecyclerView Item 添加點擊事件

interface 介面/接口
為 RecyclerView Item 添加點擊,先要認識 interface
介面是一個相關方法的集合,通常能告訴對象要執行的操作有哪些,以及默認情況下如何執行此操作,參考:

  1. 我們定義一個叫做 ItemClickListener 介面,裡面包含一個抽象方法 toEdit(),至於方法裡面的參數待會再來填它
    (P.S interface 裡可以定義屬性,但也必須是抽象的)
interface ItemClickListener{   //自定義一個 ItemClickListener 介面
    fun toEdit()
}
  1. 需要連結監聽事件,因為這裡沒有任何可以供監聽的選項,所以我們也要自定義一個監聽介面 itemClickListener ,這個監聽介面繼承自定義的 ItemClickListener
private var itemClickListener : ItemClickListener? = null
  1. 把這個監聽器 itemClickListener 放進 img_edit 的點擊事件,並且調用 ItemClickListener 裡的抽象方法 toEdit(),這樣一來,可以在點選後轉移到自定義的介面上,如此一來就可以傳到外面的呼叫者(重要!!!)
fun bind(Thing: Thing){
    tv_todo.setText(Thing.todo)
    img_edit.setOnClickListener{
        itemClickListener?.toEdit(Thing) 
    }
}
  1. 把按鈕點擊事件和接口連結後,現在就是讓其他 Activity 可以從外面呼叫它,於是就寫一個 set 方法,set 方法是可以供 Activity 或 Fragment 呼叫
fun setToEditClickListener(listener : ItemClickListener){  //用以呼叫的
    itemClickListener = listener
}     //set方法,可以供Activity或Fragment呼叫

補充:以上可參考 RecyclerView給Item新增點選事件

三、RecyclerView Item 點擊功能實現

現在來到 MainActivity修改 RecyclerView 程式碼,在裡頭實現點選事件:

  • Myadapter.setToEditClickListener() 呼叫 Adapter

  • 會被要求填入參數 listener

  • 用 object 實現接口,參考 Kotlin 里那些「不是那么写的」了解 object 其實可以拿來繼承或實現,這時候會被要求實作 interface 裡面的抽象方法,一定要 override

  • 也就是說 interface 裡面的 toEdit() 要被實作出來,讓它執行函式 EditEvent()

Myadapter.setToEditClickListener(object : MyAdapter.ItemClickListener{
    override fun toEdit(edit: Thing) {     //override
        EditEvent(edit)                    //執行函式 EditEvent()
    }

寫一個函式 EditEvent()
切換頁面並且夾帶資料過去(使用 Gson)
Gson 是一種強大的物件轉換,明天會比較仔細講這一部分,簡單來說因為 Thing 是我們自定義的資料格式,雖然目前只有 String 這一項資料型態,但之後可能會增加更多,不可能一項一項putString、putBoolean的傳送,將資料用序列化成 Json 字串

fun EditEvent(Event: Thing){
    val intent = Intent(this, Main2Activity::class.java)
    val eventString = Gson().toJson(Event)
    intent.putExtra("event", eventString)
    startActivityForResult(intent, 1)
}

現在 RecycleView Item 就可以被點擊,實現切換頁面的效果囉!當然,還有很多地方要調整,像是跳到編輯模式時 EditText 要顯示想要修改的文字、區分新增模式和編輯模式等等,明天再繼續講解囉~

後記
這篇 RecyclerView 點擊事件寫得很卡,因為不夠了解語法,花了整整兩天查資料,寫出這篇文章,後來跟 Noya 討論為什麼需要用 interface 來做,而不是直接在 onBindViewHolder 寫出來時,他給我的想法是考慮到「資料取得及層級」:
這次練習的,資料不是存在 Sharepreference、就是從另一個 Activity傳送返回,和以前直接寫在 Adapter 的陣列不一樣,在 Adapter 裡面取不到這些東西,只有在 MainActivity 才能,所以才要透過 interface 或一些方法把點擊事件呼叫過來。


上一篇
Day 19 ─用 Kotlin 做待辦清單 Todolist(3) SharedPreferences 下篇
下一篇
Day21 ─Stetho 超級好用的工具:如何查看 SQLite 和 sharepreference 內資料
系列文
Kotlin Everyday:新手寫程式踩的坑30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言