日々是好日

プログラミングについてのあれこれ、ムダ知識など

StickyListHeaders風のライブラリを自作する-4

StickyListHeaders風のライブラリを自作する-3 - 日々是好日の続き。

override が必要なメソッドの整理。

ユーザ側でオーバーライドするメソッド

  • PinningListView
    • RecyclerView を拡張した View
    • setOnHeaderTouchListener を搭載し、ヘッダー部分をタッチしたときのリスナーをセット可としている
    • オーバーライドは特に必要なし
  • PinningListAdapter (abstract)
    • PinningListView 用の抽象アダプタ
    • onCreateViewHolder
    • onBindViewHolder
    • getItemViewType
    • bindHeaderData
    • 内部クラスとして ViewHolder を要実装
  • PinningItemDecoration
    • ヘッダー描画用の ItemDecoration クラス
    • インスタンス化して PinningListView に突っ込めば OK(引数にアダプタを渡すこと)
    • PinningListListener インターフェースをもつ
  • PinningHeaderTouchListener
    • PinningListView#setOnHeaderTouchListener でヘッダータッチイベントを定義するためのインターフェース
    • onHeaderTouch を実装する

つまるところ、PinningListAdapter を継承したアダプタ のみ実装が必要となる。 ヘッダーにタッチイベントを付けたい場合は PinningHeaderTouchListener を使って実装してやる。

使用例

f:id:kcpoipoi:20190718230448g:plain

こんな感じになる。ヘッダーの色変化はあえてやってるので、あまり気にせず…。 ヘッダーをタップし、ヘッダーの位置にスクロールしたいときの実装は以下のとおり。 adapterPosition の取得がちょっとめんどくさい。

Kotlin では object 式で匿名クラスを実装してやる。

val decor = PinningItemDecoration(adapter)

pinningListView.setOnHeaderTouchListener(object: PinningHeaderTouchListener {
  override fun onTouchHeader(rv: RecyclerView, e: MotionEvent): Boolean {
    val headerHeight = decor.getHeaderHeight()

    if (headerHeight != null && e.y <= headerHeight && rv.scrollState == RecyclerView.SCROLL_STATE_IDLE){
      val listener = rv.adapter as PinningListDecoration.PinningListListener
      val topView = rv.getChildAt(0)
      val adapterPosition = rv.getChildAdapterPosition(topView)
      val headerPosition = listener.getCurrentHeaderPosition(adapterPosition)
      if (headerPosition != null) {
        rv.layoutManager?.scrollToPosition(headerPosition)
        return true
      }
    }
    return false
  }
})

リポジトリ

github.com