Reputation: 5411
I am creating a custom ViewGroup. If I add my custom ViewGroup to a layout without specifying an LayoutParams (as shown below), it displays correctly:
...
MyCustomViewGroup myViewGroup = new MyCustomViewGroup(this);
myRelativeLayout.addView(myViewGroup);
...
If I specify LayoutParams and set the width to MATCH_PARENT and height to WRAP_CONTENT (as shown below), it does not display:
...
MyCustomViewGroup myViewGroup = new MyCustomViewGroup(this);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
RelativeLayout.LayoutParams.MATCH_PARENT,
RelativeLayout.LayoutParams.WRAP_CONTENT);
myRelativeLayout.addView(myViewGroup, params);
...
I've run Debug for both scenarios.
Scenario 1
If I don't specify any LayoutParams, the child views measure correctly, and when the method iterates through all the child views to determine the maximum height, child.getMeasuredHeight()
returns correct values each time.
Scenario 2
If I specify LayoutParams with width as MATCH_PARENT, and height as WRAP_CONTENT, the system does two passes through onMeasure as described below.
Pass 1
widthSpecMode = EXACTLY
width = widthSpecSize = 758 which is the width of the parent RelativeLayout
heightSpecMode = EXACTLY
height = heightSpecSize = 1055 which is the height of the parent RelativeLayout
Pass 2
widthSpecMode = EXACTLY
width = widthSpecSize = 758 which is the width of the parent RelativeLayout
heightSpecMode = AT_MOST
The method then iterates through all the child views to determine the maximum height, but child.getMeasuredHeight()
returns 0 each time.
The child views are several ImageButtons and a TextView. They all have content, and that content is displayed correctly in the first scenario. Why does it come out as 0 height in the second scenario, and how can I fix it?
`' I have written a simplified test app to recreate the problem. All the necessary code is shown below. If anyone wishes to try it, you can simply cut and paste.
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;
}
}
}
}
Upvotes: 2
Views: 9439
Reputation: 24720
ok soit seems you forgot to call measureChild(child, widthMeasureSpec, heightMeasureSpec); when calculating the height in onMeasure
Upvotes: 8