前回記事で、GridViewのサムネイルが画面に収まらない問題が発生。そこで、スクロールバーを付けて、
- 「日付」「カテゴリ(とサブカテゴリ)」はスクロールされずに固定で画面上部に表示される
- 表示内容が多いとき「コメント」と「サムネイル」はスクロールされる
みたいな感じにしたいと考え、コメントとサムネイルをScrollViewで囲めばいいんじゃん?と安易に考えていた。が、その方法だとScrollviewの内側にある筈のTextViewが無視され、GridViewがサムネイル1行分の高さだけ表示され、低い高さのGridViewにスクロールバーが表示されるという謎な結果になって呆然…
当初は楽勝と思ったのだが、これにはなかなか深い問題が関わっており、意外と面倒だった。
GridViewクラスのオーバーライド
GridViewやListViewは確保された領域に全体が収まらない時に自身のViewの中に自動でスクロールバーが表示される仕様。そもそも、その手のViewに対してScrollViewを外側に配置するのは推奨されていないらしい。
なんとかGridViewの改造をせずに済む方法は無いものかと探したが、どうやら他に方法が無くそれ以外だとGridViewを1から作り直す位しかないようで、結局こちらの記事を見つけ、GridViewの拡張クラスを定義する方法を取った。
要はOnMeasureメソッドで高さを計測する時に、常にGridViewに表示するべき内容の高さを返すことがポイントのようだ。
親クラスはGridViewであり、基本動作に何も手を加えていないので、javaコード上はクラス変数の型宣言を変えるだけでコードの変更は特に必要ない。
ExpandableHeightGridView.java
package com.example.photologger;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.GridView;
import android.content.Context;
public class ExpandableHeightGridView extends GridView {
public ExpandableHeightGridView(Context context) {
super(context);
}
public ExpandableHeightGridView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public ExpandableHeightGridView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(MEASURED_SIZE_MASK, MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
ViewGroup.LayoutParams params = getLayoutParams();
params.height = getMeasuredHeight();
}
}
画面のレイアウトXML
前回記事で作成したレイアウトをベースに、上記のGridView改造クラスExpandableHeightGridViewとScrollViewを追加する。
上記ExpandableHeightGridViewクラスを使う場合は、単にクラス名を書くのでは駄目でcom.example.photologger.ExpandableHeightGridViewとモジュールを明示する必要がある。
日付とカテゴリのテキストの下の残りの高さをScrollViewに割り当てるのにLinearLayoutの時のようにlayout_height=”fill_parent”と指定しても期待通りにならず悩む。色々調べた結果ConstrainedLayoutではlayout_heightに0を指定すればよいと分かり解決。
activity_detail.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".DetailActivity">
<TextView
android:id="@+id/date"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="3dp"
android:layout_marginTop="3dp"
android:padding="3dp"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/category"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="3dp"
android:layout_marginEnd="3dp"
android:textColor="#2020ff"
android:textSize="20sp"
android:padding="3dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/date" />
<ScrollView
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@+id/category">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="@+id/comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="3dp" />
<com.example.photologger.ExpandableHeightGridView
android:id="@+id/tn_grid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:columnWidth="100dp"
android:gravity="center"
android:isScrollContainer="false"
android:numColumns="auto_fit"
android:stretchMode="spacingWidthUniform" />
</LinearLayout>
</ScrollView>
</android.support.constraint.ConstraintLayout>
実行結果
実行結果は以下。まずは、スクロールする前の状態。
そして、下向きにスワイプして下までスクロールすると…
期待通り日付やカテゴリは固定表示され、コメントとサムネイルだけがスクロールされた。GridViewの改造クラスが必要になるとは予想外だったが、なんとか目標通りの画面にすることが出来た。
コメント