Androidのレイアウト計算について
レイアウト計算はViewのツリーを上から順にたどって行われる。同レベルの場合は、XML中で先に書かれたものが先。(描画も同様)
計算は2つのフェーズに分かれる。前半は必要なサイズを親が子へ問い合せる(measure phase)。後半では、親が実際の場所・サイズを決めて、子へ通知する。
前半
前半では、親が子のmeasure(int widthMeasureSpec, int heightMeasureSpec)を呼び出す。
measure(int,int)はonMeasure(int widthMeasureSpec, int heightMeasureSpec)を呼び出す。
measure(int,int)はfinalなので、計算方法をカスタマイズするときはonMeasureをオーバーライドする。
measureSpecは親が子に「こんな大きさになって欲しい」という要望を伝えるための変数。
上位2ビットにモード、下位30ビットにピクセル値が含まれるintの値で、View.MeasureSpecで計算する。
(ちなみに、メモリとメソッド呼び出し回数の節約のためか、内部実装ではこのようなビット演算が多用されている)
モードは以下の3種。
- UNSPECIFIED(お好きなように=ピクセル値指定なし)
- EXACTLY(厳密に指定のピクセル値にすべし)
- AT_MOST(最大でも指定のピクセル値にすべし)
measureSpecは子のLayoutParamsなどを参考にして設定する。
親は繰り返し measure(int, int)を呼ぶことができる。最初の呼び出しで、全体の大きさを取得して、その値から計算した値をあらためてmeasureSpecに指定するような処理が可能。
後半
後半は前半で収集した情報を使って、配置を確定し、子のlayout(int left, int top, int right, int bottom) を呼び出す。当然、配置のアルゴリズムはViewGroup毎に異なるが、layoutもfinalなのでカスタマイズは、onLayout(boolean changed, int left, int top, int right, int bottom)をオーバーライドする。