質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

1回答

3467閲覧

[Android] RecyclerViewでヘッダーフッターを付けたい

CAIOS

総合スコア24

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2020/02/28 10:55

編集2020/02/29 01:06

###問題

ヘッダーフッターを付けたいと思い、以下のクラスを作成しました。

使用する際はこのクラスを継承したAdapterを作成すれば使えるようにしたいです。ヘッダーフッターの追加には表示したいレイアウトをInflateしたものを引数にadd○○関数を使用します。ヘッダーフッターにもリスナーを付けたいのでHolderはAnyでonBindItemViewHolderを渡します。

作ったまではいいのですが、ヘッダーフッターがうまく表示されません。一応表示はされるのですがレイアウトの一部しか表示されなかったり、空白のスペースだけ表示されてしまったりと、想定した動作になりません。どのようにすればうまく表示されるでしょうか。

知恵袋とのマルチポストになります
https://detail.chiebukuro.yahoo.co.jp/qa/question_detail/q13220832973

[MainActivity.kt]

Kotlin

1val headerView = View.inflate(requireContext(), R.layout.view_header, null) 2val separateNavigationView = View.inflate(requireContext(), R.layout.view_separate_navigation, null) 3recyclerAdapter?.addHeaderView(headerView) 4recyclerAdapter?.addBetweenView(separateNavigationView, 5) 5...

[RecyclerAdapter(継承先のAdapter)]

Kotlin

1class MusicRecyclerHolder(itemView: View): RecyclerView.ViewHolder(itemView){ 2 val artwork = itemView.findViewById<ImageButton>(R.id.VSL_ArtImage) 3 val musicTitle = itemView.findViewById<TextView>(R.id.VSL_Title) 4 val musicText =itemView.findViewById<TextView>(R.id.VSL_TitleSec) 5 val menuIcon = itemView.findViewById<ImageButton>(R.id.VSL_Menu) 6 7 interface ListListener{ 8 fun onItemClick(tappedView: View?, viewHolder: MusicRecyclerHolder?, musicData: MusicData?) 9 } 10} 11 12class MusicRecyclerAdapter( 13 private val context: Context, 14 private val dataList: List<MusicData>, 15 private val isShowArtist: Boolean, 16 private val listener: MusicRecyclerHolder.ListListener 17): HFRecyclerAdapter<MusicRecyclerHolder>() { 18 19 override fun onCreateItemViewHolder(parent: ViewGroup, viewType: Int): MusicRecyclerHolder { 20 return MusicRecyclerHolder(LayoutInflater.from(parent.context).inflate(R.layout.view_song_list, parent, false)) 21 } 22 23 override fun onBindItemViewHolder(viewHolder: Any, position: Int, isHF: Boolean) { 24 if (!isHF && viewHolder is MusicRecyclerHolder) { 25 val data = dataList[position] 26 with(viewHolder) { 27 //リスナー付けたり色々処理 28 } 29 } else if(viewHolder is RecyclerView.ViewHolder){ 30 viewHolder.itemView.setOnClickListener { 31 listener.onItemClick(null, null, null) //ヘッダーフッター 32 } 33 } 34 } 35 36 override fun getNormalItemCount(): Int = dataList.size 37}

[HFRecyclerAdapter(今回問題の継承元Adapter)]

Kotlin

1abstract class HFRecyclerAdapter<ViewHolder: RecyclerView.ViewHolder>: RecyclerView.Adapter<RecyclerView.ViewHolder>(){ 2 3private var currentHV: View? = null 4private val headerViewList = mutableListOf<View>() 5private val hooterViewList = mutableListOf<View>() 6private val betweenViewMap = mutableMapOf<Int, View>() 7 8abstract fun onCreateItemViewHolder(parent: ViewGroup, viewType: Int): ViewHolder 9 10abstract fun onBindItemViewHolder(viewHolder: Any, position: Int, isHF: Boolean = false) 11 12abstract fun getNormalItemCount(): Int 13 14internal class HeaderFooterHolder(itemView: View?) : ViewHolder(itemView!!) 15 16override fun getItemViewType(position: Int): Int { 17if(headerViewList.isNotEmpty() && position < headerViewList.size) { 18currentHV = headerViewList[position] 19return ITEM_VIEW_TYPE_HEADER 20} 21 22if(betweenViewMap[position - headerViewList.size] != null){ 23currentHV = betweenViewMap[position - headerViewList.size]!! 24return ITEM_VIEW_TYPE_BETWEEN 25} 26 27if(position - headerViewList.size < getNormalItemCount()){ 28return ITEM_VIEW_TYPE_NORMAL 29} 30 31currentHV = hooterViewList[position - (getNormalItemCount() + headerViewList.size)] 32return ITEM_VIEW_TYPE_HOOTER 33} 34 35override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 36return when(viewType){ 37ITEM_VIEW_TYPE_HEADER, ITEM_VIEW_TYPE_HOOTER, ITEM_VIEW_TYPE_BETWEEN -> { 38if(currentHV != null) { 39HeaderFooterHolder(currentHV) 40} 41else { 42onCreateItemViewHolder(parent, viewType) 43} 44} 45else -> onCreateItemViewHolder(parent, viewType) 46} 47} 48 49override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 50val i = position - headerViewList.size 51onBindItemViewHolder(holder, i, (getItemViewType(position) != ITEM_VIEW_TYPE_NORMAL)) 52} 53 54override fun getItemCount(): Int = getNormalItemCount() + headerViewList.size + hooterViewList.size + betweenViewMap.size 55 56fun addHeaderView(layoutView: View){ 57headerViewList.add(layoutView) 58} 59 60fun addHooterView(layoutView: View){ 61hooterViewList.add(layoutView) 62} 63 64fun addBetweenView(layoutView: View, position: Int){ 65betweenViewMap.put(position, layoutView) 66} 67 68fun isHeader(position: Int) = (getItemViewType(position) == ITEM_VIEW_TYPE_HEADER) 69 70fun isHooter(position: Int) = (getItemViewType(position) == ITEM_VIEW_TYPE_HOOTER) 71 72fun isBetween(position: Int) = (getItemViewType(position) == ITEM_VIEW_TYPE_BETWEEN) 73 74companion object{ 75const val ITEM_VIEW_TYPE_HEADER = 872 76const val ITEM_VIEW_TYPE_HOOTER = 873 77const val ITEM_VIEW_TYPE_BETWEEN = 874 78const val ITEM_VIEW_TYPE_NORMAL = 875 79} 80}

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

hoshi-takanori

2020/02/28 19:25

HFRecyclerAdapter が abstract なんですけど、サブクラス作ってます?
CAIOS

2020/02/29 01:07

継承先のAdapterClassを追記しました。 ご確認お願いいたします。
hoshi-takanori

2020/02/29 09:15

HFRecyclerAdapter の currentHV ですが、getItemViewType が呼ばれた直後に、同じ position に対して onCreateViewHolder が呼ばれることを期待したコードになってるようですが、そんな保証はないと思います。また、onBindViewHolder が onBindItemViewHolder を呼ぶ時、position の値は betweenView のことも考慮する必要がありますね。
guest

回答1

0

ベストアンサー

なんとか動くようにしてみましたが、ちょっと無理があるかも。
headerView や betweenView に生の View を指定するのは RecyclerView の思想に合わないし、betweenView の位置を Int で指定するのは(HFRecyclerAdapter 側も辛いけど)データを管理する側もしんどいのではないかと思います。

Kotlin

1class MusicRecyclerAdapter( 2 private val context: Context, 3 private val dataList: List< MusicData>, 4 private val isShowArtist: Boolean, 5 private val listener: MusicRecyclerHolder.ListListener 6): HFRecyclerAdapter<MusicRecyclerHolder>() { 7 8 override fun onCreateItemViewHolder(parent: ViewGroup, viewType: Int): MusicRecyclerHolder { 9 return MusicRecyclerHolder(LayoutInflater.from(parent.context).inflate(R.layout.view_song_list, parent, false)) 10 } 11 12 override fun onBindItemViewHolder(viewHolder: MusicRecyclerHolder, position: Int) { 13 // NormalItem の場合しかここには来ません。 14 viewHolder.musicTitle.text = dataList[position].title 15 } 16 17 override fun getNormalItemCount(): Int = dataList.size 18}

Kotlin

1abstract class HFRecyclerAdapter<ViewHolder : RecyclerView.ViewHolder> : 2 RecyclerView.Adapter<RecyclerView.ViewHolder>() { 3 4 private val headerViewList = mutableListOf<View>() 5 private val hooterViewList = mutableListOf<View>() 6 private val betweenViewMap = mutableMapOf<Int, View>() 7 8 abstract fun onCreateItemViewHolder(parent: ViewGroup, viewType: Int): ViewHolder 9 10 abstract fun onBindItemViewHolder(viewHolder: ViewHolder, position: Int) 11 12 abstract fun getNormalItemCount(): Int 13 14 internal class HeaderFooterHolder(itemView: View?) : RecyclerView.ViewHolder(itemView!!) 15 16 override fun getItemViewType(position: Int): Int { 17 if (headerViewList.isNotEmpty() && position < headerViewList.size) { 18 return ITEM_VIEW_TYPE_HEADER 19 } 20 21 if (position >= maxNormalPosition()) { 22 return ITEM_VIEW_TYPE_HOOTER 23 } 24 25 if (betweenViewMap[position - headerViewList.size] != null) { 26 return ITEM_VIEW_TYPE_BETWEEN 27 } 28 29 return ITEM_VIEW_TYPE_NORMAL 30 } 31 32 override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { 33 return when (viewType) { 34 ITEM_VIEW_TYPE_HEADER, ITEM_VIEW_TYPE_HOOTER, ITEM_VIEW_TYPE_BETWEEN -> { 35 val inflater = LayoutInflater.from(parent.context) 36 HeaderFooterHolder(inflater.inflate(R.layout.view_frame, parent, false)) 37 } 38 else -> onCreateItemViewHolder(parent, viewType) 39 } 40 } 41 42 override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { 43 val viewType = getItemViewType(position) 44 if (viewType == ITEM_VIEW_TYPE_NORMAL) { 45 var pos = position - headerViewList.size 46 for (i in sortedBetweenKeys().reversed()) { 47 if (pos > i) { 48 pos-- 49 } 50 } 51 onBindItemViewHolder(holder as ViewHolder, pos) 52 return 53 } 54 val view = when (viewType) { 55 ITEM_VIEW_TYPE_HEADER -> headerViewList[position] 56 ITEM_VIEW_TYPE_BETWEEN -> betweenViewMap[position - headerViewList.size]!! 57 else -> hooterViewList[position - maxNormalPosition()] 58 } 59 (view.parent as? ViewGroup)?.removeView(view) 60 ((holder as HeaderFooterHolder).itemView as ViewGroup).addView(view) 61 } 62 63 override fun getItemCount(): Int = maxNormalPosition() + hooterViewList.size 64 65 private fun maxNormalPosition(): Int { 66 var count = getNormalItemCount() 67 for (i in sortedBetweenKeys()) { 68 if (i < count) { 69 count++ 70 } else { 71 break 72 } 73 } 74 return count + headerViewList.size 75 } 76 77 private fun sortedBetweenKeys(): List<Int> { 78 return betweenViewMap.keys.sorted() 79 } 80 81 fun addHeaderView(layoutView: View) { 82 headerViewList.add(layoutView) 83 } 84 85 fun addHooterView(layoutView: View) { 86 hooterViewList.add(layoutView) 87 } 88 89 fun addBetweenView(layoutView: View, position: Int) { 90 betweenViewMap.put(position, layoutView) 91 } 92 93 fun isHeader(position: Int) = (getItemViewType(position) == ITEM_VIEW_TYPE_HEADER) 94 95 fun isHooter(position: Int) = (getItemViewType(position) == ITEM_VIEW_TYPE_HOOTER) 96 97 fun isBetween(position: Int) = (getItemViewType(position) == ITEM_VIEW_TYPE_BETWEEN) 98 99 companion object { 100 const val ITEM_VIEW_TYPE_HEADER = 872 101 const val ITEM_VIEW_TYPE_HOOTER = 873 102 const val ITEM_VIEW_TYPE_BETWEEN = 874 103 const val ITEM_VIEW_TYPE_NORMAL = 875 104 } 105}

res/layout/view_frame.xml

XML

1<?xml version="1.0" encoding="utf-8"?> 2<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android" 3 android:layout_width="match_parent" 4 android:layout_height="wrap_content" />

投稿2020/03/03 21:22

hoshi-takanori

総合スコア7895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問