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

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

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

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

Android

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

Q&A

解決済

1回答

261閲覧

AndroidのRecyclerViewで可変アイテムの高さが正確に計算されない

croakubi

総合スコア1

Java

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

Android

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

0グッド

0クリップ

投稿2024/10/12 23:41

実現したいこと

  • スクロールビュー内のRecyclerViewがスクロールなしで全アイテムを3列でにグリッド上に表示できること

(RecyclerView)にこだわりはないので別のアプローチでも大丈夫です!

  • リスト可変アイテムの高さを持つRecyclerViewで、正確に全体の高さを計算する方法はありますか?
  • リストあるいは、他にアプローチとして考えられる解決策があれば教えてください。

前提

現在、AndroidのRecyclerViewを使ってスクロール無しの横3列で
グリッド上にアイテムを表示しようと思っています。

表示する各アイテムはアイテム内の要素をGONEにしたり、VISIBLEにしているので高さが異なるため、
行ごとに一番高いアイテムの高さに合わせてRecyclerViewの高さを動的に計算しようとしています。

しかし、RecyclerViewの高さを正確に計算できず、特定の行が見切れる、
または高さが合わない問題が発生しています。

発生している問題

  • リストスクロールをしないようにしたRecyclerViewの高さが正確に計算されず、一部の行が見切れる。
  • 1行ごとのアイテムの高さが異なるため、行ごとの最大高さを正しく反映させたいが、うまくいかない。

該当のソースコード

  1. レイアウトファイル (ScrollView + RecyclerView)

xml

1<ScrollView 2 android:id="@+id/main_scroll_view" 3 android:layout_width="match_parent" 4 android:layout_height="match_parent"> 5 6 <LinearLayout 7 android:layout_width="match_parent" 8 android:layout_height="wrap_content" 9 android:orientation="vertical"> 10 11 <!-- グリッド表示のRecyclerView --> 12 <androidx.recyclerview.widget.RecyclerView 13 android:id="@+id/articleRecyclerView" 14 android:layout_width="match_parent" 15 android:layout_height="wrap_content" 16 android:visibility="visible" 17 android:nestedScrollingEnabled="false" /> 18 <!-- 以下にその他項目(省略) --> 19 </LinearLayout> 20</ScrollView> 21
  1. アイテムのレイアウト (item_article.xml)

xml

1<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 2 android:layout_width="wrap_content" 3 android:layout_height="wrap_content" 4 android:paddingBottom="8dp" 5 android:paddingLeft="8dp" 6 android:paddingRight="8dp"> 7 8 <!-- 固定表示される記事のサムネイル --> 9 <ImageView 10 android:id="@+id/articleImage" 11 android:layout_width="104dp" 12 android:layout_height="104dp" 13 android:layout_alignParentTop="true" 14 android:layout_centerHorizontal="true" /> 15 16 <!-- Newアイコン(表示/非表示) --> 17 <TextView 18 android:id="@+id/articleNewIcon" 19 android:layout_width="wrap_content" 20 android:layout_height="wrap_content" 21 android:layout_below="@id/articleImage" 22 android:layout_centerHorizontal="true" 23 android:layout_marginTop="4dp" 24 android:text="NEW!" 25 android:visibility="gone" /> 26 27 <!-- 投稿日(nullの場合非表示) --> 28 <TextView 29 android:id="@+id/publishDate" 30 android:layout_width="wrap_content" 31 android:layout_height="wrap_content" 32 android:layout_below="@id/articleNewIcon" 33 android:layout_centerHorizontal="true" 34 android:visibility="gone" /> 35 36 <!-- 常に表示されるタイトル --> 37 <TextView 38 android:id="@+id/articleTitle" 39 android:layout_width="wrap_content" 40 android:layout_height="wrap_content" 41 android:layout_below="@id/publishDate" 42 android:layout_centerHorizontal="true" 43 android:text="Article Title" /> 44 45 <!-- ブログ名(nullの場合非表示) --> 46 <TextView 47 android:id="@+id/blogName" 48 android:layout_width="wrap_content" 49 android:layout_height="wrap_content" 50 android:layout_below="@id/articleTitle" 51 android:layout_centerHorizontal="true" 52 android:visibility="gone" /> 53</RelativeLayout> 54
  1. アダプタ (ArticleListAdapter.java)

java

1public class ArticleListAdapter extends RecyclerView.Adapter<ArticleListAdapter.ArticleViewHolder> { 2 private List<Article> dataList; 3 4 public ArticleListAdapter(List<Article> dataList) { 5 this.dataList = dataList; 6 } 7 8 @NonNull 9 @Override 10 public ArticleViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { 11 View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_article, parent, false); 12 return new ArticleViewHolder(itemView); 13 } 14 15 @Override 16 public void onBindViewHolder(@NonNull ArticleViewHolder holder, int position) { 17 Article data = dataList.get(position); 18 // サムネイル 19 holder.articleImage.setImageResource(data.getImageResourceId()); 20 // タイトル(常に表示) 21 holder.titleView.setText(data.getTitle()); 22 // Newアイコン 23 holder.newIcon.setVisibility(data.isNew() ? View.VISIBLE : View.GONE); 24 // 投稿日(nullの場合非表示) 25 if (data.getPublishDate() != null) { 26 holder.publishDate.setText(data.getPublishDate()); 27 holder.publishDate.setVisibility(View.VISIBLE); 28 } else { 29 holder.publishDate.setVisibility(View.GONE); 30 } 31 // ブログ名(nullの場合非表示) 32 if (data.getBlogName() != null) { 33 holder.blogName.setText(data.getBlogName()); 34 holder.blogName.setVisibility(View.VISIBLE); 35 } else { 36 holder.blogName.setVisibility(View.GONE); 37 } 38 } 39 40 @Override 41 public int getItemCount() { 42 return dataList.size(); 43 } 44 45 public static class ArticleViewHolder extends RecyclerView.ViewHolder { 46 TextView titleView, newIcon, publishDate, blogName; 47 ImageView articleImage; 48 public ArticleViewHolder(@NonNull View itemView) { 49 super(itemView); 50 titleView = itemView.findViewById(R.id.articleTitle); 51 newIcon = itemView.findViewById(R.id.articleNewIcon); 52 publishDate = itemView.findViewById(R.id.publishDate); 53 blogName = itemView.findViewById(R.id.blogName); 54 articleImage = itemView.findViewById(R.id.articleImage); 55 } 56 } 57} 58
  1. フラグメント (MyFragment.java)

java

1public class MyFragment extends Fragment { 2 3 @Override 4 public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 5 View view = inflater.inflate(R.layout.fragment_layout, container, false); 6 7 RecyclerView recyclerView = view.findViewById(R.id.articleRecyclerView); 8 recyclerView.setLayoutManager(new GridLayoutManager(getContext(), 3)); 9 recyclerView.setAdapter(new ArticleListAdapter(getDummyData())); 10 11 // 高さを動的に設定 12 adjustRecyclerViewHeight(recyclerView, 3); 13 return view; 14 } 15 16 // RecyclerViewの高さを動的に調整する 17 private void adjustRecyclerViewHeight(RecyclerView recyclerView, int columns) { 18 RecyclerView.Adapter adapter = recyclerView.getAdapter(); 19 if (adapter == null) { 20 return; 21 } 22 23 int totalHeight = 0; // 最終的な RecyclerView の高さ 24 int itemCount = adapter.getItemCount(); // アイテム数 25 26 // 1行の高さを記録するための変数 27 List<Integer> rowHeights = new ArrayList<>(); // 各行の高さを格納するリスト 28 29 // 各行の高さを計算 30 for (int rowIndex = 0; rowIndex < Math.ceil((double) itemCount / columns); rowIndex++) { 31 int maxHeightInRow = 0; 32 33 // 各行内の各アイテムの高さを計測し、最大値を取る 34 for (int columnIndex = 0; columnIndex < columns; columnIndex++) { 35 int itemIndex = rowIndex * columns + columnIndex; 36 37 // アイテムが存在する場合のみ計測 38 if (itemIndex < itemCount) { 39 // アイテムの ViewHolder を取得 40 RecyclerView.ViewHolder holder = adapter.createViewHolder(recyclerView, adapter.getItemViewType(itemIndex)); 41 View itemView = holder.itemView; 42 43 // アイテムの高さを測定 44 itemView.measure( 45 View.MeasureSpec.makeMeasureSpec(recyclerView.getWidth() / columns, View.MeasureSpec.EXACTLY), 46 View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) 47 ); 48 49 // アイテムの高さを取得 50 int itemHeight = itemView.getMeasuredHeight(); 51 52 // アイテムのマージンを考慮 53 ViewGroup.MarginLayoutParams layoutParams = (ViewGroup.MarginLayoutParams) itemView.getLayoutParams(); 54 int itemVerticalMargin = layoutParams.topMargin + layoutParams.bottomMargin; 55 itemHeight += itemVerticalMargin; 56 57 // この行の最大高さを更新 58 if (itemHeight > maxHeightInRow) { 59 maxHeightInRow = itemHeight; 60 } 61 } 62 } 63 64 // 行の最大高さをリストに追加 65 rowHeights.add(maxHeightInRow); 66 } 67 68 // 各行の高さを合計して最終的な RecyclerView の高さを計算 69 for (int rowHeight : rowHeights) { 70 totalHeight += rowHeight; 71 } 72 73 // RecyclerView のパディングを考慮 74 totalHeight += recyclerView.getPaddingTop() + recyclerView.getPaddingBottom(); 75 76 // 高さを設定 77 ViewGroup.LayoutParams params = recyclerView.getLayoutParams(); 78 params.height = totalHeight; 79 recyclerView.setLayoutParams(params); 80 } 81}

試したこと

  • GridViewを使用したGrid表示(各行のアイテムの高さが揃わず断念しRecyclerViewに移行)
  • ViewHolderを使ってアイテムの高さを取得し、行ごとの最大高さを計算
  • ViewTreeObserver.OnGlobalLayoutListener でRecyclerViewが描画された後に高さを計算
  • スクロールを無効にして全アイテムが表示されるように設定(nestedScrollingEnabled=false)

補足情報(FW/ツールのバージョンなど)

  • Java
  • minSdkVersion 21
  • targetSdkVersion 33
  • Android Studio

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

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

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

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

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

jimbe

2024/10/13 01:09

質問本文の最大文字数に引っかかったのかもしれませんが、幾つかの重要な部分(ArticleクラスやgetDammyDataメソッド)が省略されていて、状況の確認が出来ません。 もしまだ最大文字数まで余裕がありましたら、本文で説明されている状況が『こちらでコード・データを補完せずに』確認できる十分なコード・データをご提示ください。 もし書ききれないようでしたら、 GitHub 等外部のストレージ(質問本文はずっと残りますので少なくとも短期間では消えない所)をご利用になって、その URL を載せて頂く等で対応をお願いします。
croakubi

2024/10/13 02:13

ありがとうございます! こちらがArticleクラスやgetDammyDataメソッドを保管したコードとなります。 プロジェクト全体は大変申し訳ありませんがちょっとセキュリティの兼ね合いがありまして必要最低限のコードの展開となります。 https://gist.github.com/c-akubi/0ff915397e7a3e3754544499c1323cfb
jimbe

2024/10/13 04:27

ありがとうございます。 テストデータですが、drawable に置く R.drawable.article_image_1~3を 高さ100px/200px/300px で作成しておく等で良いのでしょうか。
croakubi

2024/10/13 07:14

ありがとうございます。 R.drawable.article_imageのサイズは100px固定で newIconやarticleImageをVISIBLEやGONEなどにして各アイテムごとに高さを変える感じですね。
croakubi

2024/10/13 07:27

すみません、補足としてデータが30件くらいある場合にリストにアイテムが表示し切れなくなったので高さの計算処理を実装したという経緯があります。
guest

回答1

0

ベストアンサー

minSDK が 21 で出来るか分かりませんが、手元の 30 でざっくりやってみた感じですと、

・フラグメントのレイアウトで RecyclerView を含む ScrollView は、 androidx.core.widget.NestedScrollView を用いる。
→ でも試したら ScrollView でも大丈夫かも?
→ でもでも、やっぱり ScrollView だと RecyclerView の表示が崩れてるかもしれない。

・ RecyclerView の高さの計算・設定は しない

でそれっぽくなったような感じがします。

投稿2024/10/13 05:16

編集2024/10/13 11:18
jimbe

総合スコア13108

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

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

croakubi

2024/10/13 07:19

おお? adjustRecyclerViewHeight()を使用せずただ表示するだけって言うことでしょうか?
jimbe

2024/10/13 11:04

フラグメントのレイアウトを ScrollView をトップレベルにして、後はテストデータをコピペで16個用意して表示した所、高さを計算しなくても スクロールしないRecyclerView(全6行)+TextView が ScrollView 内でスクロールする状態となりました。
croakubi

2024/10/15 00:11

ありがとうございます! androidx.core.widget.NestedScrollView を使用することで解決しました!
jimbe

2024/10/15 03:17

良かったです。お疲れさまでした。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.39%

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

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

質問する

関連した質問