# ヘッダ、フッタ、セクション
RecyclerViewでは、ヘッダ、フッタ、セクションのViewを自前で用意し、設定してあげる必要がある。
ベースはこちら
RecyclerViewの実装
# ゴール
ヘッダ、フッタ、セクションをそれぞれカスタムビュー化し、別レイアウトとする。
ヘッダを列の頭に設定
フッタを列の後ろに設定
セクションは列の値の途中追加
実装
Adapterのデータクラス
RecyclerAdapterでのヘッダ、フッタ、セクション、ボディ部分の判定もtypeにて行う
今回はカスタムビューでも使用する
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
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
ヘッダ、ボディ、ボディの間にセクション、フッタの順にテストデータが入るようにする
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のアダプタ
タイプを判定してデータ格納
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
レイアウトを操作するクラス
class RecyclerItemViewHolder(private val view: RecyclerItemView) : RecyclerView.ViewHolder(view) {
fun update(state: RecyclerState){
view.update(state)
}
}
##Custom View
今回もヘッダ、フッタ、セクション、ボディ 全てをカスタムビューにしている
セクションだけ色つけたので載せておく
<?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>
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
}
}
できあがり
注意点
getItemViewTypeをoverrideするのを忘れないようにずっと0で小パニックになります。
override fun getItemViewType(position: Int): Int {
return states[position].type.int
}
今回はstateにAdapterで種類を判定できるtype追加して判定を行ったけど、
Adapter自体にaddFooter()のようなメソッドを追加して、
これ以上データが無いかどうか判定してから、addFooter()を呼び出すのが実務に近いかも。
まとめ
カスタムビューとViewHolderをそれぞれ作るのに時間かかるのが難点
ヘッダ、フッタは1つずつなので、カスタムビュー作らない可能性も考慮してもいいかも。
セクションは使いまわす可能性高いのでカスタムビューが良いかと。