Android RecyclerView & CardViewのonClickイベントにハマる


昨日、RecyclerViewのクリックイベントがどうしても取得できずハマってしまったのでメモしておきます。

RecyclerView onClickイベントの基本系

Layout

RecyclerViewの中のitemを以下のようなレイアウトにした場合、

<LinearLayout 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:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="wrap_content">

    <android.support.v7.widget.CardView
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:id="@+id/cardView">
    </android.support.v7.widget.CardView>
</LinearLayout>
onClickイベント

基本は RecyclerView.AdapterのonCreateViewHolder()の実装にonClickイベントを書くことになると思う。ネット上の情報もほぼこれ。

override fun onCreateViewHolder(parent: ViewGroup, p1: Int): ViewHolder {
    val itemView = LayoutInflater.from(parent.context).inflate(R.layoute.card_view_layout, parent,false)
    val holder = ViewHolder(itemView)
    //itemViewにリスナーを設定
    holder.itemView.setOnClickListener {
        //クリックされたViewHolderの位置が取得できる
        val position = holder.getAdapterPosition();
        doAction(piosition)
    }
    return holder
}

ただし、CardViewのクリック時に選択されたことを表現する為にandroid:clickable="true"を定義した場合、上記で言うitemViewのonClickイベントが取得できなくなります。

    <android.support.v7.widget.CardView
            android:clickable="true"
            android:foreground="?android:attr/selectableItemBackground"
            ...

CardView OnClickの定義

CardViewでリスナーの定義をするには単純に

override fun onCreateViewHolder(parent: ViewGroup, p1: Int): ViewHolder {
    val itemView = LayoutInflater.from(parent.context).inflate(R.layoute.card_view_layout, parent,false)
    val holder = ViewHolder(itemView)
    //CardViewにリスナーを設定
    holder.itemView.findViewById<CardView>(R.id.cardView).setOnClickListener {
        val position = holder.getAdapterPosition();
        doAction(piosition)
    }
    return holder
}

とすればよい。

蛇足 

上記のdoActionはAdapter内にベターっと書かずにActivityやFragment側で記述するのがシャレオツみたい。

interfaceを定義してうけとればよい。

Adapter

class FooAdapter( var mListener:onCardViewItemClick ): RecyclerView.Adapter<ViewHolder>(){
    interface onCardViewItemClick{
        fun onClick(position: Int)
    }
    override fun onCreateViewHolder(parent: ViewGroup, p1: Int): ViewHolder {
        ...中略
        holder.itemView.findViewById<CardView>(R.id.cardView).setOnClickListener {
            val position = holder.getAdapterPosition();
            mListener.onClick(piosition)
        }
        return holder
    }
}

Fragment側で実装を渡そう

class BarFragment : Fragment(),
    FooAdapter.onCardViewItemClick{
    override fun fun onClick(position: Int){
        //doAction
    }
    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        ...略
        recyclerView.adapter = FooAdapter(tihs)
    }
}