StickyListHeaders風のライブラリを自作する-7
StickyListHeaders風のライブラリを自作する-6 - 日々是好日の続き。
ヘッダーオブジェクトを自動で挿入するようにしてみた。
リファクタしてみた
旧PinningListViewライブラリは、ヘッダーとなるオブジェクトをユーザ側で挿入しなければならなかった。 たとえばこんな形。
val list: List<Any> = listOf( Header(...), Item(...), Item(...), Header(...), Item(...), Item(...), ... )
Header
はヘッダー用の、Item
はコンテンツ用のオブジェクト。
型を混在させる必要があるため、List<Any>
としてリストを作っていた。
こんなん使い物にならないのは明らかだったので、List<Item>
として渡せるように改良してみた。
どのように使いたいか
コンテンツのリストList<Item>
を渡すだけで、次のようにヘッダーを自動で表示する。
この例では日付の部分が自動で挿入されたヘッダーオブジェクト。
ヘッダー抽出クラス
新たにPinningListHeaderExtractor
インターフェースを定義し、ヘッダーに使用するプロパティを指定できるようにした。
// E : アイテムクラス // T : 参照するプロパティの型 // H : ヘッダークラス interface PinningHeaderExtractor<E, T, out H> { val referenceHeaderProperty: KProperty1<E, T> fun createElement(sectionTopElement: E): H } // たとえばアイテムクラスはこんな感じ data class Item( val id: Int, val index: String, val content: String ) // ヘッダークラスはこんな感じ data class Header( val header: String )
referenceHeaderProperty
では、ヘッダーを挿入したい箇所を検知するためのプロパティを設定する。
プロパティはリフレクション(Item::index
)により指定する。
ライブラリ側ではこのItem::index
を基にリストを走査し、Item::index
が切り替わるところでcreateElement
をコールする。
createElement
ではH
で指定したオブジェクトが返されるので、このオブジェクトをリストに挿入しヘッダーとして表示する。
実装
// Activity や Fragment など class Hoge { fun initializeView() { val list = listOf<Item>(...) val adapter = MyPinningListAdapter(list, R.layout.header) .also { // ヘッダーを抽出(Extract)するための Extractor をセット it.setExtractor(object : PinningListHeaderExtractor<Item, String, Header> { // ヘッダーの切り替わり検知用プロパティを指定 override val referenceHeaderProperty: KProperty1<Item, String> get() = Item::index // ヘッダーオブジェクトを生成する override fun createElement(sectionTopElement: Item): Header { return Header(sectionTopElement.index) } }) // リストを走査してヘッダーオブジェクトをセットする it.extractHeader() } ... } } // PinningListAdapter を継承したアダプタを作り、リストとヘッダー用レイアウトファイルを渡す class MyPinningListAdapter(list: List<Item>, @LayoutRes override val headerLayout: Int?) : PinningListAdapter<Item, String, Header, RecyclerView.ViewHolder>(list) { ... } // 指定したプロパティを読み取り、自動でヘッダーオブジェクトを挿入する // 今回は index プロパティを読み取る data class Item( val id: Int, val index: String, val content: String ) // ヘッダーには index を表示する data class Header( val header: String )
4行目のリストはこんな感じで作る。
"A", "B", "C"それぞれの境界のところにHeader("A")
等が挿入される。
val list = listOf( Item(id = 0, index = "A", content = "content1"), Item(1, "A", "content2"), Item(2, "B", "content3"), Item(3, "B", "content4"), Item(4, "C", "content5") )
ライブラリ公開先
Gradleでは次の記述で使えます。 JCenter申請中なので、とおればリポジトリの参照は不要となります。
// プロジェクトレベル build.gradle repositories { maven { url 'https://dl.bintray.com/hide-kc/maven'} } // モジュールレベル build.gradle dependencies { implementation 'work.kcs_labo:pinninglistview:1.0.1' }