Android
Kotlin
AndroidStudio
android開発

RecyclerViewのヘッダ、フッタ、セクション

More than 1 year has passed since last update.


 ヘッダ、フッタ、セクション

RecyclerViewでは、ヘッダ、フッタ、セクションのViewを自前で用意し、設定してあげる必要がある。

ベースはこちら

RecyclerViewの実装


 ゴール

ヘッダ、フッタ、セクションをそれぞれカスタムビュー化し、別レイアウトとする。

ヘッダを列の頭に設定

フッタを列の後ろに設定

セクションは列の値の途中追加


実装

Adapterのデータクラス

RecyclerAdapterでのヘッダ、フッタ、セクション、ボディ部分の判定もtypeにて行う

今回はカスタムビューでも使用する


RecyclerState.kt

class RecyclerState(){

constructor(type: RecyclerType, text: String): this(){
this.type = type
this.text = text
}

// RcyclerAdapterにて追加するレコードのタイプ
var type: RecyclerType = RecyclerType.BODY
var text: String = ""
}



Type

RecyclerAdapterでの判定で使用するenum


RecyclerType.kt

enum class RecyclerType(val int: Int){

HEADER(0),
FOOTER(1),
SECTION(2),
BODY(3);

companion object {
// Intからenumへの変換
fun fromInt(int: Int): RecyclerType{
return values().firstOrNull { it.int == int }
?: RecyclerType.BODY
}
}
}



 Activity

ヘッダ、ボディ、ボディの間にセクション、フッタの順にテストデータが入るようにする


MainActivity.kt

class MainActivity : AppCompatActivity() {

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)

val states = arrayListOf<RecyclerState>()
// ヘッダ追加
val headerState = RecyclerState(RecyclerType.HEADER, "へっだ")
states.add(headerState)

var secCounter = 0
for(i in 1..5){

// 2 件目 と 3 件目 の上にセクションを追加
if(i == 2 || i == 3){
secCounter++
val sectionState = RecyclerState(RecyclerType.SECTION, "セクション(区切り) No. $secCounter")
states.add(sectionState)
}

val state = RecyclerState(RecyclerType.BODY, "$i 件目")
states.add(state)
}

// フッタ追加
val footerState = RecyclerState(RecyclerType.FOOTER, "ふった")
states.add(footerState)

val adapter = RecyclerAdapter(this, states)

val recycler = findViewById<RecyclerView>(R.id.mainRecycler)
recycler.adapter = adapter
}
}



Adapter

RecyclerViewのアダプタ

タイプを判定してデータ格納


RecyclerAdapter.kt

class RecyclerAdapter(

private val context: Context,
private val states: List<RecyclerState>) :
RecyclerView.Adapter<RecyclerView.ViewHolder>() {

override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
when(RecyclerType.fromInt(viewType)){
RecyclerType.HEADER -> {
val view = RecyclerItemHeaderView(context)
return RecyclerItemHeaderViewHolder(view)
}
RecyclerType.FOOTER -> {
val view = RecyclerItemFooterView(context)
return RecyclerItemFooterViewHolder(view)
}
RecyclerType.SECTION -> {
val view = RecyclerItemSectionView(context)
return RecyclerItemSectionViewHolder(view)
}
RecyclerType.BODY -> {
val view = RecyclerItemView(context)
return RecyclerItemViewHolder(view)
}
}
}

override fun onBindViewHolder(viewHolder: RecyclerView.ViewHolder, position: Int) {
when(viewHolder){
is RecyclerItemHeaderViewHolder ->{
viewHolder.update(states[position])
}
is RecyclerItemFooterViewHolder ->{
viewHolder.update(states[position])
}
is RecyclerItemSectionViewHolder ->{
viewHolder.update(states[position])
}
is RecyclerItemViewHolder ->{
viewHolder.update(states[position])
}
}
}

override fun getItemViewType(position: Int): Int {
return states[position].type.int
}

override fun getItemCount(): Int {
return states.count()
}
}



ViewHolder

レイアウトを操作するクラス


RecyclerItemViewHolder.kt

class RecyclerItemViewHolder(private val view: RecyclerItemView) : RecyclerView.ViewHolder(view) {

fun update(state: RecyclerState){
view.update(state)
}
}


Custom View

今回もヘッダ、フッタ、セクション、ボディ 全てをカスタムビューにしている

セクションだけ色つけたので載せておく


recycler_item_section_view.xml

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@android:color/darker_gray">

<TextView
android:id="@+id/recyclerItemSectionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="セクション"
android:textColor="@android:color/holo_blue_dark"
android:textSize="20sp"
android:textStyle="bold"/>

<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_gravity="bottom"
android:background="@android:color/darker_gray"/>

</FrameLayout>



RecyclerItemSectionView.kt

class RecyclerItemSectionView constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : FrameLayout(context, attrs, defStyleAttr){

private val textView: TextView

init {
val rootView = LayoutInflater.from(context).inflate(R.layout.recycler_item_section_view, this)
textView = rootView.findViewById(R.id.recyclerItemSectionText)
setOnClickListener {
// クリック処理
}
}

fun update(state: RecyclerState){
textView.text = state.text
}
}



できあがり

スクリーンショット 2017-08-31 19.41.10.png


注意点

getItemViewTypeをoverrideするのを忘れないようにずっと0で小パニックになります。


RecyclerAdapter.kt

  override fun getItemViewType(position: Int): Int {

return states[position].type.int
}

今回はstateにAdapterで種類を判定できるtype追加して判定を行ったけど、

Adapter自体にaddFooter()のようなメソッドを追加して、

これ以上データが無いかどうか判定してから、addFooter()を呼び出すのが実務に近いかも。


まとめ

カスタムビューとViewHolderをそれぞれ作るのに時間かかるのが難点

ヘッダ、フッタは1つずつなので、カスタムビュー作らない可能性も考慮してもいいかも。

セクションは使いまわす可能性高いのでカスタムビューが良いかと。