Infinito Nirone 7

白羽の矢を刺すスタイル

RecyclerView の item に focus が当たったことを検知する

やりたいこと

旧来の AdapterView (ListView や GridView など)でいうところの OnItemSelectedListener と同じように、RecyclerView がもつアイテムにフォーカスが当たったときにその情報をコールバックする方法です。

方法

RecyclerView には OnChildAttachStateChangeListener というインタフェースが定義されています。このインタフェースのメソッドで、Adapter の生成する View が RecyclerView に attach されたタイミングが分かるので、そこで attach された View に OnFocusChangeListener をセットしてフォーカスが当たったことを検知します。

class FocusListeningRecyclerView(context: Context?, attrs: AttributeSet?, defStyle: Int)
     : RecyclerView(context, attrs, defStyle), RecyclerView.OnChildAttachStateChangeListener {
  var focusListener: OnItemFocusedListener? = null

  constructor(context: Context?): this(context, null)
  constructor(context: Context?, attrs: AttributeSet?): this(context, attrs, 0)

  override fun onAttachedToWindow() {
    super.onAttachedToWindow()
    addOnChildAttachStateChangeListener(this)
  }

  override fun onDetachedFromWindow() {
    removeOnChildAttachStateChangeListener(this)
    super.onDetachedFromWindow()
  }

  override fun onChildViewAttachedToWindow(view: View?) {
    view?.setOnFocusChangeListener { child, hasFocus ->
      if (!hasFocus)
        return@setOnFocusChangeListener
      focusListener?.onItemFocused(getChildAdapterPosition(child))
    }
  }

  override fun onChildViewDetachedFromWindow(view: View?) {}

  interface OnItemFocusedListener {
    fun onItemFocused(position: Int)
  }
}