カスタムコンポーネントをやろうとしました。 View
クラスを拡張し、onDraw
オーバーライドメソッドで描画を行います。なぜonMeasure
をオーバーライドする必要があるのですか?私がそうしなかったならば、すべてが正しいと思われた。誰かがそれを説明してもらえますか? onMeasure
メソッドをどのように書くべきですか?私はいくつかのチュートリアルを見ましたが、それぞれが他のものとは少し異なります。時には彼らは最後にsuper.onMeasure
を呼び出し、時には彼らはsetMeasuredDimension
を使用し、それを呼び出さなかった。違いはどこですか?
結局、私はいくつかの全く同じコンポーネントを使いたいのです。これらのコンポーネントを私のXML
ファイルに追加しましたが、それらがどれだけの大きさになるべきかわかりません。後でその位置とサイズを設定したいのですが(描画時にonMeasure
に設定する場合はonDraw
にサイズを設定する必要がありますが、これもカスタムコンポーネントクラスで機能します)。まさに私がそれをする必要がある時?
onMeasure()
は、カスタムビューを親から提供されるレイアウトの制約にどの程度依存させるかをAndroidに通知する機会です。これらのレイアウト制約が何であるかを学ぶのもカスタムビューの機会です(match_parent
状況とwrap_content
状況では異なる動作をしたい場合)。これらの制約は、メソッドに渡されるMeasureSpec
値にまとめられています。これは、モード値の大まかな相関関係です。
layout_width
またはlayout_height
の値が特定の値に設定されていたことを意味します。あなたはおそらくあなたの見解をこの大きさにするべきです。これはmatch_parent
が使用されたときにトリガーされ、サイズを親ビューに正確に設定します(これはフレームワークのレイアウトに依存します)。layout_width
またはlayout_height
の値がmatch_parent
またはwrap_content
に設定され、最大サイズが必要な場合(これはフレームワークのレイアウトによって異なります)、親ディメンションのサイズがその値です。あなたはこのサイズより大きくてはいけません。layout_width
またはlayout_height
の値が制限なしでwrap_content
に設定されたことを意味します。あなたはあなたが望むどんなサイズでもありえます。一部のレイアウトでは、このコールバックを使用して、2番目の測定要求で実際にどのスペックを通過させるかを決定する前に、希望のサイズを把握します。onMeasure()
との契約は、最後にsetMeasuredDimension()
MUSTをビューのサイズに合わせて呼び出します。このメソッドは、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);
}
お役に立てば幸いです。
実際には、値はラッピングコンテナにも依存するため、あなたの答えは完全ではありません。相対レイアウトまたは線形レイアウトの場合、値は次のように動作します。
水平スクロールビューの場合は、コードが機能します。
あなたが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()は常に100×100のサイズを設定する」と主張するこの 公式ドキュメントの部分 は間違っています。