カスタム ViewGroup を作成しています。(以下に示すように) LayoutParams を指定せずにカスタム ViewGroup をレイアウトに追加すると、正しく表示されます。
...
MyCustomViewGroup myViewGroup = new MyCustomViewGroup(this);
myRelativeLayout.addView(myViewGroup);
...
LayoutParams を指定し、幅を MATCH_PARENT に、高さを WRAP_CONTENT に設定すると (以下に示すように)、表示されません。
...
MyCustomViewGroup myViewGroup = new MyCustomViewGroup(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
myRelativeLayout.addView(myViewGroup, params);
...
両方のシナリオでデバッグを実行しました。
シナリオ 1
LayoutParams を指定しない場合、子ビューは正しく測定され、メソッドがすべての子ビューを反復処理して最大の高さを決定すると、child.getMeasuredHeight()
毎回正しい値が返されます。
シナリオ 2
幅を MATCH_PARENT、高さを WRAP_CONTENT として LayoutParams を指定すると、システムは以下に説明するように onMeasure を 2 回通過します。
1
を渡します widthSpecMode = EXACTLY
width = widthSpecSize = 親 RelativeLayout の幅である 758
heightSpecMode = EXACTLY
height = heightSpecSize = 親 RelativeLayout の高さである 1055
Pass 2
widthSpecMode = EXACTLY
width = widthSpecSize = 親の幅である 758 RelativeLayout
heightSpecMode = AT_MOST
次に、メソッドはすべての子ビューを反復して最大の高さを決定しますが、child.getMeasuredHeight()
毎回 0 を返します。
子ビューは、いくつかの ImageButtons と TextView です。それらにはすべてコンテンツがあり、そのコンテンツは最初のシナリオで正しく表示されます。2 番目のシナリオで高さが 0 になるのはなぜですか? どうすれば修正できますか?
問題 を再現するための簡単なテスト アプリを作成しました。必要なすべてのコードを以下に示します。誰でも試してみたい場合は、切り取って貼り付けるだけです。
MainActivity.java
public class MainActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RelativeLayout rootLayout = (RelativeLayout)findViewById(R.id.LayoutRoot);
// Create an instance of MyViewGroup
MyViewGroup viewGroupOne = new MyViewGroup(this);
viewGroupOne.setId(1);
rootLayout.addView(viewGroupOne);
// Create a second instance and set layout width and height both to WRAP_CONTENT
MyViewGroup viewGroupTwo = new MyViewGroup(this);
viewGroupTwo.setId(2);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW, 1);
rootLayout.addView(viewGroupTwo, params);
// Create a third Instance. Set layout width to MATCH_PARENT and height to WRAP_CONTENT
MyViewGroup viewGroupThree = new MyViewGroup(this);
params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW, 2);
rootLayout.addView(viewGroupThree, params);
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/LayoutRoot"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
</RelativeLayout>
MyViewGroup.java
public class MyViewGroup extends ViewGroup {
private static int instanceCounter;
public MyViewGroup(Context context) {
super(context);
instanceCounter++;
// Add a TextView
TextView textView = new TextView(context);
String text = "Instance " + instanceCounter;
textView.setText(text);
addView(textView);
// Add an ImageView
ImageView imageView = new ImageView(context);
imageView.setImageResource(android.R.drawable.ic_menu_add);
addView(imageView);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int childCount = getChildCount();
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int widthSpecSize = MeasureSpec.getSize(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int heightSpecSize = MeasureSpec.getSize(heightMeasureSpec);
int width = 0;
int height = 0;
int childState = 0;
// Measure Width
if (widthSpecMode == MeasureSpec.EXACTLY) {
width = widthSpecSize;
} else {
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
measureChild(child, widthMeasureSpec, heightMeasureSpec);
width += child.getMeasuredWidth();
}
}
}
if (widthSpecMode == MeasureSpec.AT_MOST) {
width = Math.min(width, widthSpecSize);
}
// Measure Height
if (heightSpecMode == MeasureSpec.EXACTLY) {
height = heightSpecSize;
} else {
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
height = Math.max(height, child.getMeasuredHeight());
}
}
}
if (heightSpecMode == MeasureSpec.AT_MOST) {
height = Math.min(height, heightSpecSize);
}
// Combine child states
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
if (child.getVisibility() != GONE) {
childState = combineMeasuredStates(childState, child.getMeasuredState());
}
}
// Check against minimum width and height
width = Math.max(width, getSuggestedMinimumWidth());
height = Math.max(height, getSuggestedMinimumHeight());
// Report final dimensions
setMeasuredDimension(resolveSizeAndState(width, widthMeasureSpec, childState),
resolveSizeAndState(height, heightMeasureSpec, childState << MEASURED_HEIGHT_STATE_SHIFT));
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int childCount = getChildCount();
int leftPos = 0;
for (int i = 0; i < childCount; i++) {
final View child = getChildAt(i);
final int childWidth = child.getMeasuredWidth();
if (child.getVisibility() != GONE) {
child.layout(leftPos, 0, leftPos + childWidth, getMeasuredHeight());
leftPos += childWidth;
}
}
}
}