🀄

【レイアウトXML】ViewGroup in ScrollViewでlayout_gravityを設定する時の注意

2021/09/14に公開

結論

重要な議論は用意してないため、最初に結論から失礼します。

ScrollViewの子ViewGroup(ConstraintLayoutやLinearLayoutなど...)にて中央揃えをする場合に、 layout_gravity="center_vertical""center" を設定するのは良くないです。

もう少し正確には...
ScrollViewのスクロール方向と同じ成分が含まれるcenter系のlayout_gravityを、子ViewGroupに指定するのは良くないです。(この場合はcenter_verticalcenter
すると、スタート位置が見切れて末尾に余計なマージンが入ってしまいます。要はレイアウト崩れます。

[補足] HorizontalScrollViewの場合

こちらは水平方向にスクロールするので、子ViewGroupに layout_gravity="center_horizontal""center" を設定すると同じことが起きます。

解決案

  • ScrollViewの子ViewGroupが中央揃えをしたい場合は layout_gravity="center_horizontal" を使う
    • ただし水平方向にしか揃えられない
    • もし何かを垂直方向に揃えたい場合、さらに子Viewや子ViewGroupで、gravityを設定してあげるしかない

そもそもレイアウトXMLはオワコン?説もあるので、Jetpack Composeに乗り換えていきたいですね!(自戒も込めて)

ちなみに...Stack Overflowでは意見が様々だった

同じ現象と思われる方の投稿を見つけたのですが、回答された方々の対処法がバラバラで、いまいち信憑性がありませんでした。
android:fillViewport="true"設定しろ、と答えてるのもあって、試したけどそれじゃ解決したなかったり...)
https://stackoverflow.com/questions/19540310/center-content-in-scroll-view


具体例

以降はこの事象を発見した経緯とともに説明です。
上記内容で理解された方には特に不要かと思われます。

垂直にスクロールしたい画面

次のように垂直にスクロールしたい画面があるとします(この例では、アルファベットAからZまで整列しているだけです)。

先頭 末尾
start end

上の例の場合、レイアウトはこんな感じです↓。

<?xml version="1.0" encoding="utf-8"?>
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">

        <TextView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="ABC\n\n\nDEF\n\n\nGHI\n\n\nJKL\n\n\nMNO\n\n\nPQR\n\n\nSTU\n\n\nVWX\n\n\nYZ"
            android:textSize="80sp"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</ScrollView>

子を中央揃えしたい

Viewを中央揃えしたいときって頻繁にあるかと思います。
そんな時、ScrollViewの子にあたるViewGroup(この例ならConstraintLayout)にて、安易に

layout_gravity="center"

を設定してしまうとスクロール位置がおかしくなります。(layout_gravity="center_vertical"も同様)
↓下の画像参照。スタート位置がAからのはずなのに見切れて、Zの下には空白入れてないのに末尾に余計なマージン(ちょうど見切れた先頭の高さ分くらい)が入っている。

先頭 末尾
start end

正しく中央揃えする

ScrollViewの子、ConstraintLayoutにこう書けば、水平方向に中央揃えできました!

layout_gravity="center_horizontal"
先頭 末尾
start end

終わりに

特に難しいことはなかったかと思います。
深く考えずに要らない方向にまでlayout_gravityを設定してしまうと、今回ご紹介させていただたような問題に遭遇することがあります。

今回の現象の原因は未調査

なんとなく、ScrollViewと子ViewGroupの要素の高さ計算のタイミングなどによるものだと想像してます。
なので逆に、コード上で描画の適切なタイミングでlayout_gravityとか設定してあげれば、今回NGだと書いたlayout_gravity="center"な実装も可能なのかな〜とか、思ってます。

これを深掘りするなら、その分をJetpack Composeの勉強に充てたいという素直な気持ちもありますし。。

拙い解説でしたが最後まで見ていただきありがとうございました!
今回ご紹介した内容が、AndroidでレイアウトXMLを書いてる方に届けばとても嬉しいです!

Discussion