Android の View 描画方法

フォーカスを取得した Activity は、レイアウトを描画するようリクエストされます。描画の手順は Android フレームワークによって処理されますが、レイアウト階層のルートノードは Activity で指定する必要があります。

描画は、レイアウトのルートノードから始まります。続いて、レイアウト ツリーの測定と描画がリクエストされます。描画の処理では、ツリーをたどって無効な領域と交差する各 View がレンダリングされます。その過程で、各 ViewGroup はすべての子に対して(draw() メソッドで)描画をリクエストする役割を担い、また各 View は自身の描画を行う役割を担います。ツリーは前順で走査されるため、親は子より前(つまり背後)に描画され、兄弟はツリーに表示された順序で描画されます。

注: フレームワークでは、有効な領域にない View オブジェクトは描画されません。なお、View 背景の描画も処理されます。

View を強制的に描画させるには、invalidate() を呼び出します。

レイアウトの描画は、測定パスとレイアウトパスの 2 つのパスからなるプロセスです。

測定パス

測定パスは measure(int, int) で実装されます。走査は View ツリーの上から下に向かって行われます。この繰り返しの間に、各 View により寸法指定がツリーの下方にプッシュされます。測定パスの終了時には、すべての View に測定値が保存されています。2 番目のパスは layout(int, int, int, int) で実装されます。この走査も上から下に向かって行われます。このパスでは、各親は、測定パスで計算されたサイズを使用して、すべての子を配置する役割を担います。

View オブジェクトの measure() メソッドが返された時点では、その View オブジェクトのすべての子孫も含め、getMeasuredWidth()getMeasuredHeight() の値が設定されているはずです。View オブジェクトの幅と高さの測定値は、その View オブジェクトの親から課された制約に従う必要があります。これにより、測定パスの最後には、すべての親がすべての子の測定値を受け入れることが保証されます。親 View は、子に対して measure() を複数回呼び出すことがあります。たとえば、親はまず寸法を指定せずに子の測定を行い、それぞれがどのくらいの大きさになるかを確認します。そして、制約をかけないと子のサイズの合計が大きすぎる、または小さすぎる場合は、再度 measure() を呼び出して実際の数を指定します(つまり、子同士でそれぞれの獲得スペースについての折り合いがつかない場合は、親が介入して 2 回目のパスでルールを設定するということです)。

測定パスでは、寸法の伝達に 2 つのクラスが使用されます。ViewGroup.LayoutParams クラスは、View オブジェクトによって、希望する測定値と配置を親に通知するために使用されます。つまり、ベースの ViewGroup.LayoutParams クラスには、View の幅と高さの両方について、希望値が記述されているにすぎません。このクラスには、各寸法に次のいずれかを指定できます。

  • 特定値。
  • MATCH_PARENTView が親(からパディングを差し引いた領域)と同じ大きさになるという意味。
  • WRAP_CONTENTView がコンテンツ(にパディングを加えた領域)を囲める大きさになるという意味。

ViewGroup のさまざまなサブクラスに、ViewGroup.LayoutParams サブクラスがあります。たとえば、RelativeLayout には固有の ViewGroup.LayoutParams サブクラスがあり、これには子 View オブジェクトを水平および垂直方向に中央揃えする機能が含まれています。

MeasureSpec オブジェクトは、要件を親から子に向けて、ツリー下方にプッシュするために使用されます。MeasureSpec のモードは、次の 3 つのうちのいずれかになります。

  • UNSPECIFIED: これは、親が子 View の適切な寸法を判断するために使用します。たとえば LinearLayout では、子に対して高さを UNSPECIFIED、幅を EXACTLY 240 に指定して measure() を呼び出すことにより、子 View の幅が 240 ピクセルだと高さはどの程度になるのかを調べることができます。
  • EXACTLY: これは、親が子のサイズを特定値で指定するために使用します。子は指定されたサイズを使用する必要があり、さらに子孫がすべてこのサイズに収まることを保証しなければなりません。
  • AT MOST: これは、親が子の最大サイズを指定するために使用します。子は、自身と子孫がすべてこのサイズに収まることを保証しなければなりません。

レイアウトパス

レイアウトを開始するには、requestLayout() を呼び出します。このメソッドは通常、View によって、それ自身が現在の範囲に収まらないと判断された場合に呼び出されます。