337

カスタムコンポーネントを試してみました。クラスを拡張し、オーバーライドされたメソッドViewで描画を行います。onDrawなぜオーバーライドする必要があるのonMeasureですか?私がそうしなかった場合、すべてが正しいように見えました。誰かがそれを説明してもらえますか?onMeasureメソッドはどのように書くべきですか?いくつかのチュートリアルを見てきましたが、それぞれが他のチュートリアルとは少し異なります。super.onMeasure最後に電話することもあれば、使用することもsetMeasuredDimensionしないこともあります。違いはどこですか?

結局のところ、まったく同じコンポーネントをいくつか使用したいと思います。これらのコンポーネントをXMLファイルに追加しましたが、どのくらいの大きさにする必要があるのか​​わかりません。カスタムコンポーネントクラスで、後でその位置とサイズを設定したい(描画するときにサイズを設定する必要があるのはなぜですかonMeasureonDraw。正確にいつそれをする必要がありますか?

4

3 に答える 3

773

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

  • EXACTLYは、layout_widthorlayout_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両方向のカスタムビューでは、フレームワークがビューの大きさを認識していないため、ビューがまったく表示されない場合があります。

一般に、オーバーライドViewしていて、別の既存のウィジェットではない場合は、次のような単純なものであっても、実装を提供することをお勧めします。

@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);
}

お役に立てば幸いです。

于 2012-09-04T15:58:20.277 に答える
5

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

  • 正確にmatch_parentは、正確に+親のサイズです
  • AT_MOST wrap_contentは、AT_MOSTMeasureSpecになります
  • UNSPECIFIEDはトリガーされませんでした

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

于 2013-10-30T07:28:42.903 に答える
3

onMeasureで何かを変更する必要がない場合は、それをオーバーライドする必要はまったくありません。

Devunwiredコード(ここで選択され、最も投票された回答)は、SDK実装がすでに行っていることとほぼ同じです(そして私はチェックしました-それは2009年以来それを行っていました)。

ここでonMeasureメソッドを確認できます:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
            getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
}

public static int getDefaultSize(int size, int measureSpec) {
    int result = size;
    int specMode = MeasureSpec.getMode(measureSpec);
    int specSize = MeasureSpec.getSize(measureSpec);

    switch (specMode) {
    case MeasureSpec.UNSPECIFIED:
        result = size;
        break;
    case MeasureSpec.AT_MOST:
    case MeasureSpec.EXACTLY:
        result = specSize;
        break;
    }
    return result;
}

SDKコードをオーバーライドして、まったく同じコードに置き換えることは意味がありません。

「デフォルトのonMeasure()は常に100x100のサイズを設定する」と主張するこの公式ドキュメントの部分は間違っています。

于 2019-03-28T12:22:05.037 に答える