androidでheightやwidthが0になって取得できない時
は、ViewTreeObserverを使う。
inflateしたviewの高さや幅を動的に設定したいとか、inflate済みの別のviewの高さや幅を参考にしたいときは以下のようにViewのheightやwidthを取得する。
LayoutParams param = view.getLayoutParams();
param.width = 100;
view.setLayoutParams(param);
が、activityのonCreateとかのなかでgetLayoutParams()してviewのwidthを取得しても、0が返ってくることがある。何でだろうと思って調べてみたところ、その時点ではまだviewはinflateされていないっぽい。タイミングの問題。なので、そのviewがinflateされて高さとか幅を持った時にさせたいことをすればいい。 で、そのタイミングのフックになるのがViewTreeObserver。それにリスナーを追加することで、viewが高さを持ってレンダリングされた直後に呼ばれるメソッドを書いてやればいい。そのリスナーの中ならば、getLayoutParams()をしてもちゃんと高さや幅が取得できる。
final TextView tv = (TextView)findViewById(R.id.image_test); ViewTreeObserver vto = tv.getViewTreeObserver(); vto.addOnGlobalLayoutListener(new OnGlobalLayoutListener() { @Override public void onGlobalLayout() { ViewGroup.LayoutParams params =tv.getLayoutParams(); params.width = 100; tv.setLayoutParams(params); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { vto.removeOnGlobalLayoutListener(this); } else { vto.removeGlobalOnLayoutListener(this); } } });
ターゲットとなるviewまたはそのviewをラップしている親viewから、getViewTreeObserver()でViewTreeObserverを取得して、それにリスナーを追加する。で、そのonGlobalLayoutのなかで任意の処理を行えばいい。
removeOnGlobalLayoutListenerはその名の通り、リスナーを消去する。リスナーを登録したviewがinflateする度にこのonGlobalLayoutが呼ばれてしまうので、1回計算すればいいような処理なんかは何度も呼ばれたら面倒なので、リスナーを消去してしまう。これでいい感じのタイミングでviewのwidthやheightを取得して、それに合わせた処理をできる。
参考: