17

onDraw()キャンバスにビットマップを描画するオーバーライドされたカスタム ビューを作成しました。レイアウト ファイルで wrap_content が必要であることを指定すると、それでも画面全体がいっぱいになります。onMeasure()これは言います:

により大きなサイズが許可されていない限り、メジャーの基本クラスの実装はデフォルトで背景サイズになりMeasureSpecます。サブクラスは onMeasure(int, int) をオーバーライドして、コンテンツのより良い測定値を提供する必要があります。

わかりましたので、オーバーライドonMeasure()して操作する必要があることはわかっていますMeasureSpecこの回答によると

UNSPECIFIED は、layout_width または layout_height の値が wrap_content に設定されたことを意味します。好きなサイズにできます。

今、私は自分の問題にonMeasure()取り掛かります。まだ作成されていないビットマップを測定し、それを測定/ラップするにはどうすればよいですか? wrap_content に設定されている場合、他の Android ビューは画面全体をブロックしないため、何かをしなければならないことはわかっています。前もって感謝します!

4

2 に答える 2

34

これは、これらの一般的に使用されるビュー メソッドが実行される順序です。

1. Constructor    // choose your desired size 
2. onMeasure      // parent will determine if your desired size is acceptable
3. onSizeChanged
4. onLayout       
5. onDraw         // draw your view content at the size specified by the parent

希望のサイズを選ぶ

ビューが任意のサイズにできるとしたら、どのサイズを選択しますか? これはあなたのwrap_contentサイズであり、カスタム ビューの内容によって異なります。例:

  • カスタム ビューが画像の場合、目的のサイズはおそらくビットマップのピクセル寸法にパディングを加えたものになります。(サイズを選択してコンテンツを描画するときに、パディングを計算に組み込むのはユーザーの責任です。)
  • カスタム ビューがアナログ時計の場合、目的のサイズは、見栄えの良いデフォルト サイズである可能性があります。dp(デバイスのpxサイズはいつでも取得できます。)

必要なサイズが重い計算を使用する場合は、コンストラクターでそれを行います。それ以外の場合は、で割り当てることができますonMeasure。( onMeasureonLayout、およびonDrawは複数回呼び出される可能性があるため、ここで重い作業を行うのはよくありません。)

最終的なサイズの交渉

onMeasureは、子供が親にどのくらいの大きさになりたいかを伝え、親がそれが許容できるかどうかを決定する場所です. 多くの場合、このメソッドは数回呼び出され、そのたびにさまざまなサイズの要件が渡され、妥協できるかどうかが確認されます。ただし、最終的には、子は親のサイズ要件を尊重する必要があります。

私のセットアップ方法について復習が必要な場合は、常にこの回答に戻りonMeasureます。

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

上記の例では、目的の幅と高さがデフォルトに設定されています。代わりに、事前に計算して、クラス メンバー変数を使用してここに設定することもできます。

選択したサイズの使用

の後onMeasure、ビューのサイズがわかります。このサイズは、あなたが要求したものであるかもしれないし、そうでないかもしれませんが、あなたが今取り組まなければならないものです。そのサイズを使用して、 のビューにコンテンツを描画しますonDraw

ノート

  • 外観に影響するがサイズには影響しない変更をビューに加えるときはいつでも、 を呼び出しますinvalidate()。これによりonDraw、再度呼び出されます (ただし、他の以前のメソッドのすべてではありません)。
  • サイズに影響を与える変更をビューに加えるときはいつでも、 を呼び出しますrequestLayout()。これにより、測定と描画のプロセスが から最初からやり直されonMeasureます。通常はへの呼び出しと組み合わせて使用​​しますinvalidate()
  • なんらかの理由で適切なサイズを事前に決定できない場合は、@nmw の提案に従って、幅ゼロ、高さゼロを要求するだけでよいと思います。invalidate()次に、すべてがロードされた後に(だけでなく) レイアウトを要求します。ただし、ビュー階層全体を 2 回続けて配置する必要があるため、これは少し無駄に思えます。
于 2017-02-24T04:33:47.020 に答える
4

onMeasure 呼び出しの前にビットマップを測定できない場合は、ビットマップが読み込まれるまでゼロのサイズを返すことができます。ロードしたら、親 ViewGroup を無効にして別のメジャーを強制します (ビュー自体の invalidate() が onMeasure を強制するかどうかは思い出せません)。

于 2012-11-07T17:07:07.157 に答える