【Android】ScrollViewでスクロールが表示されているかを判定する方法

こんばんは。

最近、「悪魔城ドラキュラx 血の輪廻」という横スクロールゲームをやったのですが、めちゃくちゃ難しかったです。

昔のゲームって鬼畜仕様多いですよね。

ってことでスクロールについての話題を。。

ScrollViewでスクロールが表示されているかを判定する方法

ScrollViewでスクロールが出ていた場合、メッセージ表示やレイアウト変更したいというケースが稀にあります。

例えば、下記のように文字追加を行いスクロールが出たら画面下部に「続きがあります」と表示したい場合など。。

方法としてはScrollViewとScrollViewの内側にいるLayoutViewの高さを比較し、LayoutViewの方が大きければスクロールが出ていると判断出来ます。

下の例だとscrollViewとscrollLayoutを比較。

f:id:toyo0110:20211110190658p:plain

具体的なソースはこちら

public class MainActivity extends AppCompatActivity {
    private TextView txtInfo;
    private ScrollView scrollView;
    private LinearLayout scrollLayout;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        final Button btnAdd = findViewById(R.id.btnAdd);
        final Button btnClear = findViewById(R.id.btnClear);
        final TextView textView = findViewById(R.id.textView1);
        txtInfo = findViewById(R.id.txtInfo);
        scrollView = findViewById(R.id.scrollView);
        scrollLayout = findViewById(R.id.scrollLayout);

        for (int i = 0; i < 10; i++) {
            textView.append("aaaaaa\n");
        }

        // scrollLayoutがの可視状態が変わった場合または、変更があったい場合に動く
        scrollLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
            @Override
            public void onGlobalLayout() {
                setInfo();
                // 初回描画時だけ判定したいのであれば、下記でイベントを削除する。
                //  scrollLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
            }
        });

        btnAdd.setOnClickListener(v -> {
                    textView.append("aaaaaa\n");
                }
        );
        
        btnClear.setOnClickListener(v -> {
            textView.setText("");
        });
    }

    private void setInfo() {

       // ScrollViewとScrollViewの内側にいるLayoutViewの高さを比較
        if (scrollView.getHeight() <= scrollLayout.getHeight()) {
            txtInfo.setText("続きがあります。");
        } else {
            txtInfo.setText("");
        }
    }
}

ポイントとしては「.getHeight()で高さをとっていますが描画後でないと正しい値が取れません。

onCreatenの中で「.getHeight()」で高さを取得してもScrollViewで描画が完了してない為、高さが0で返ってきてしまいます。

文字列追記後も描画反映まで少しラグがあるため、文字追加前の高さが取れます。

描画後に確実に判定するには、ViewTreeObserver.OnGlobalLayoutListenerで可視状態及び変更を監視し、LayoutViewの状態が変わったタイミングで判定する必要があります。