2012-09-04 8 views
263

カスタムコンポーネントを作成しようとしました。私はViewクラスを拡張し、onDrawの方法でいくつかの描画を行います。なぜ私はonMeasureを上書きする必要がありますか?もし私がそうしなければ、すべてが正しいとみなされました。誰か説明してもらえますか?私のonMeasureメソッドはどうすればいいですか?私はカップルのチュートリアルを見たことがありますが、それぞれが少し違っています。ときどきsuper.onMeasureが最後に呼び出されることもありますが、時にはsetMeasuredDimensionを使用して呼び出していないこともあります。違いはどこですか?私はいくつかのまったく同じコンポーネントを使用するすべての後onMeasureカスタムビューの説明

。私はこれらのコンポーネントを私のXMLファイルに追加しましたが、どれくらいの大きさであるべきかわかりません。私は、カスタム・コンポーネント・クラスに(私はそれを描くときonDrawで、同様に動作しているかどうかonMeasureにサイズを設定する必要がある理由)後でその位置とサイズを設定したいです。正確に私はそれを行う必要があるとき?

答えて

649

onMeasure()は、カスタムビューは、親が提供するレイアウト制約依存するようにしたいどのように大きなアンドロイドを伝えるためにあなたの機会です。また、これらのレイアウト制約がある(場合には、あなたがwrap_content状況よりmatch_parent状況で異なる動作をします)何を学ぶためにカスタムビューの機会です。これらの制約は、メソッドに渡される値MeasureSpecにパッケージ化されます。ここで、モード値の大まかな相関がある:

  • を正確layout_width又はlayout_height値が特定の値に設定したことを意味します。あなたはおそらくこのサイズのビューを作成する必要があります。 match_parentが親ビューに正確にサイズを設定するために、使用されたときにこれは(これは、レイアウトのフレームワークで依存している)トリガ得ることができます。
  • AT_MOSTは、典型的には、layout_widthまたはlayout_height値(これはフレームワークに依存レイアウトである)match_parentまたは最大サイズが必要とされるwrap_contentに設定し、親ディメンションのサイズが値であることを意味します。このサイズより大きくてはいけません。
  • UNSPECIFIEDは、一般的にlayout_widthまたはlayout_height値は制限なしでwrap_contentに設定されたことを意味します。あなたはあなたが好きな大きさにすることができます。いくつかのレイアウトでは、このコールバックを使用して、2番目のメジャーリクエストで実際にどのスペックを再度通過するかを判断する前に、目的のサイズを把握します。

onMeasure()に存在する契約はsetMeasuredDimension()ビューがなりたいサイズで最後に呼び出さなければならないことです。このメソッドは、Viewにあるデフォルトの実装を含む、すべてのフレームワーク実装によって呼び出されます。そのため、ユースケースに適合する場合はsuperを呼び出すのが安全です。

フレームワークにデフォルトの実装が適用されるため、このメソッドをオーバーライドする必要はないかもしれませんが、そうでない場合は表示スペースがコンテンツよりも小さい場合にクリッピングが発生することがあります。あなたは、フレームワークは、それがどのように大規模な知らないので、あなたのビューがまったく表示されない場合があり、両方向でwrap_contentでカスタムビューをレイアウト!

@Override 
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 

    int desiredWidth = 100; 
    int desiredHeight = 100; 

    int widthMode = MeasureSpec.getMode(widthMeasureSpec); 
    int widthSize = MeasureSpec.getSize(widthMeasureSpec); 
    int heightMode = MeasureSpec.getMode(heightMeasureSpec); 
    int heightSize = MeasureSpec.getSize(heightMeasureSpec); 

    int width; 
    int height; 

    //Measure Width 
    if (widthMode == MeasureSpec.EXACTLY) { 
     //Must be this size 
     width = widthSize; 
    } else if (widthMode == MeasureSpec.AT_MOST) { 
     //Can't be bigger than... 
     width = Math.min(desiredWidth, widthSize); 
    } else { 
     //Be whatever you want 
     width = desiredWidth; 
    } 

    //Measure Height 
    if (heightMode == MeasureSpec.EXACTLY) { 
     //Must be this size 
     height = heightSize; 
    } else if (heightMode == MeasureSpec.AT_MOST) { 
     //Can't be bigger than... 
     height = Math.min(desiredHeight, heightSize); 
    } else { 
     //Be whatever you want 
     height = desiredHeight; 
    } 

    //MUST CALL THIS 
    setMeasuredDimension(width, height); 
} 

希望に役立ちます:あなたはViewではなく、別の既存のウィジェットをオーバーライドしている場合

は一般的に、このようなもののように単純であったとしても、おそらく実装を提供することをお勧めします。

+1

ちょっと@Devunwired素敵な説明私がこれまでに読んだ最高。あなたの説明は、私が持っていた多くの疑問に答えて、疑問を解決しましたが、それでもまだ残っています。私のカスタムビューが他のビューグループと一緒にViewGroup内にある場合ViewGroupはViewGroupがすべての子供をLayoutParams制約のための1つのプローブごとに、それぞれの子が制約に従って自己測定するように頼みますか? – pharaoh

+0

はい、それは 'ViewGroup'の' measureChildren() 'メソッドがメジャー/レイアウトプロセス中に行うものです。 – Devunwired

+34

ViewGroupサブクラスのonMeasureをオーバーライドすると、このコードは機能しません。あなたのサブビューは表示されず、すべて0x0のサイズになります。カスタムViewGroupのonMeasureをオーバーライドする必要がある場合は、widthMode、widthSize、heightModeおよびheightSizeを変更し、MeasureSpec.makeMeasureSpecを使用してmeasureSpecsにコンパイルし、結果の整数をsuper.onMeasureに渡します。 – Alexey

4

実際には、値はラッピングコンテナにも依存するため、答えは完全ではありません。相対的または線形レイアウトの場合には、値がこのように振る舞う:

  • 丁度 match_parent親のちょうど+サイズ
  • AT_MOST AT_MOST MeasureSpec
  • wrap_content結果
  • UNSPECIFIED決してトリガーされた

水平スクロールビューの場合、コードは機能します。

+49

ここでの回答が不完全であると感じる場合は、部分的な回答ではなく、追加してください。 –

+0

これはレイアウトの仕組みにリンクするための良いonyaですが、私のケースでは、私のカスタムビューではonMeasureが3回呼び出されます。問題のビューには、wrap_contentの高さと重み付けされた幅(width = 0、weight = 1)があります。最初のコールはUNSPECIFIED/UNSPECIFIED、2番目のコールはAT_MOST/EXACTLY、3番目のコールはEXACTLY/EXACTLYでした。 –