【Android】Viewの幅や高さを取得時に0が返却される時の対処法

今回はgetHeightやgetWidthでViewの高さや幅が取得できない時の対処法を説明します!

現象

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Buttonのインスタンスを取得
Button button = findViewById(R.id.button);
// Buttonの横幅を取得
int width = button.getWidth(); // 0 が返却される
}

onCreate内等でViewのサイズ取得すると0が返却されます。

なぜでしょうか?インスタンス等は取得できるのになぜサイズは取得できないのでしょうか

サイズが取得できない理由

まず、結論から言うとonCreate内のタイミングでは「Viewのサイズが未確定」だからです

ViewのサイズはonCreate時には未確定なのです。未確定だからサイズが取得できず0が返却されてしまいます。

ではサイズを取得する方法あるのでしょうか

サイズ取得方法①: onLayout ()を使用する

ViewのライフサイクルのView.onLayoutが呼び出されるとサイズが確定します。

なのでonLayout内であればサイズを取得することが可能です。

@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
    super.onLayout(changed, left, top, right, bottom);
    int width = getWidth(); // Viewの横幅が取得できる
}
override fun onLayout(changed: Boolean, left: Int, top: Int, right: Int, bottom: Int) {
    super.onLayout(changed, left, top, right, bottom)
    val width = width // Viewの横幅が取得できる
}

注意点としてはViewのライフサイクルをハンドリングするので何度もonLayoutが呼ばれる事があるので要注意です。

サイズ取得方法②: ViewTreeObserver.OnGlobalLayoutListener()を使用する

ViewTreeObserver.OnGlobalLayoutListener()を定義するとレイアウトが変更されるたびにコールバックされます。

最初のViewのサイズ未確定時からViewのサイズ確定した時にコールバックが呼ばれてサイズが取得できるということですね。

// ButtonにViewTreeObserver.OnGlobalLayoutListenerを定義する
button.getViewTreeObserver().addOnGlobalLayoutListener(() -> {
    // レイアウトが変更されるたび処理される
    int width = button.getWidth();  // Viewの横幅が取得できる
});
// Buttonのインスタンスを取得
val button = findViewById<Button>(R.id.button)
// ButtonにViewTreeObserver.OnGlobalLayoutListenerを定義する
button.viewTreeObserver.addOnGlobalLayoutListener {
    // レイアウトが変更されるたび処理される
    val width = button.width  // Viewの横幅が取得できる
}

注意点としてはonLayout同様、レイアウトが変更されるたびに何度もコールバックされるので要注意です

サイズ取得方法③: View.post()を使用する⭐︎おすすめ⭐︎

View.postはメソッド内に定義した処理をメインスレッドのキューにセットしてくれます。

レイアウト定義後にView.postを呼び出すとViewのサイズ確定後まで処理を待ってくれます。

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    // メインスレッドでレイアウトを定義
    setContentView(R.layout.activity_main);
    // Buttonのインスタンスを取得
    Button button = findViewById(R.id.button);
    // メインスレッドにキュー設定
    // setContentViewで処理終了後メインスレッドが空になるのでキューに溜まっている処理が実行される
    button.post(()-> {
        int width = button.getWidth(); // 横幅が返却される
    });
}
override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // メインスレッドでレイアウトを定義
    setContentView(R.layout.activity_main)
    // Buttonのインスタンスを取得
    val button = findViewById<Button>(R.id.button)
    // メインスレッドにキュー設定
    // setContentViewで処理終了後メインスレッドが空になるのでキューに溜まっている処理が実行される
    button.post {
        val width = button.width // 横幅が返却される
    }
}

View.postのおすすめはonLayoutやOnGlobalLayoutListenerと違い何度も処理がコールバックされないので様々な考慮が不要なので使いやすいです

最後に

いかがでしたでしたでしょうか

今回使いやすい3案を提案しましたが他にもViewのサイズを取得するために方法はあります。

3案以外にも「こっちが使いやすいぞ」等あれば教えていただければ幸いです。

コメント

タイトルとURLをコピーしました