I've created a simple app to illustrate a change in how LinearLayout behaves when wrapped in a RelativeLayout between SDK 17 and SDK 18. First, screenshots:
When targetSdkVersion is "17":
When targetSdkVersion is "18":
The layout for this activity is:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#00ffff"
android:orientation="vertical" >
<include layout="@layout/test_list_item"/>
<ListView
android:id="@+id/listview"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:overScrollMode="never"
android:scrollingCache="false" >
</ListView>
</LinearLayout>
text_list_item.xml is:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff0000" >
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#ffff00"
android:padding="10dp"
android:text="foobar"
android:textColor="#000000" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@id/text"
android:layout_alignTop="@id/text"
android:background="#00ff00"
android:orientation="horizontal"
android:weightSum="1.0" >
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.5"
android:background="#0000ff" />
</LinearLayout>
</RelativeLayout>
The activity's onCreate() looks like this:
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
findViewById(R.id.text).bringToFront();
final ListView mListView = (ListView) findViewById(R.id.listview);
mListView.setAdapter(new TestAdapter());
}
And TestAdapter looks like this:
public class TestAdapter extends BaseAdapter {
private static final int COUNT = 3;
@Override
public int getCount() {
return COUNT;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public boolean areAllItemsEnabled() {
return true;
}
@Override
public boolean isEnabled(int position) {
return false;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_list_item, null);
view.findViewById(R.id.text).bringToFront();
return view;
}
When target = 17 the LinearLayout's child view is properly resized to 50% of available width both when it's inside the ListView and when it's outside the ListView. When target = 18 it's not resized when used as the layout for a list item. In this case its width stays at "0dp" and is never resized.
Ideally I would like to target SDK 19. In order to do so, though, I to accommodate this change in behavior somehow. Any thoughts on how that could be accomplished?
btw, There are reasons why the layout is as complicated and "weird" as it is; I realize there are much more straightforward ways of achieving this particular visual look.
EDIT:
To give a little more info on why I'm using this pathological layout: its used for "gauges". The View inside the LinearLayout is stretched or shrunk using a ScaleAnimation so that it occupies the desired percentage of the total available width. What makes it tricky is that I need to overlay text on top of the gauge, and I'd like the gauge's height to be determined by the text.
So the exterior RelativeLayout should be "as wide as its parent" but only "as tall as the TextView it contains". The LinearLayout inside that RelativeLayout should be exactly as wide and tall as its parent. The View inside the LinearLayout should be as tall as the LinearLayout that contains it, but only half as wide.
This is exactly the behavior I get when targeting SDK 17 and/or in SDK 18+ when the layout isn't used as the contents of a ListView item.
EDIT
Posted on the developer's group:
https://groups.google.com/forum/#!msg/android-developers/KuoY5Tklyms/TbNq6ndD_PwJ
Change
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_list_item, null);
to:
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.test_list_item, parent, false);
and I think you will have better luck.
UPDATE
Well, here's what I can tell you.
If you fire up Hierarchy View, you will see that your View
supplying the blue background has a height of 0 in the "inside ListView" scenario. The width is correct, which would suggest that the problem is not tied to the weight, per the other answer to your question.
If you look at the source code to RelativeLayout
, they did add some smarts for changing behavior at the dividing line between an android:targetSdkVersion
of 17 versus 18+. My guess is that you're tripping over mAllowBrokenMeasureSpecs
, in some scenario that is tied to the ListView
container, but that's just a guess. I have a tough enough time grokking all the layout/measure stuff for a container when it's a simple container, that I wrote, and when I have had a full night of sleep -- making sense of RelativeLayout
on limited sleep is beyond my abilities. And I've poked and prodded at your sample, trying various things to see if they would fix things, with no results.
With regards to your edit that you posted while I was poking at this:
In principle, you do not need the base layout to be a RelativeLayout
. A FrameLayout
should work as well, as all you need it to do is be able to stack things on the Z axis and be able to center your label. That being said, I got even stranger results when going down that path, though those results did not seem tied to targetSdkVersion
, so I may have just been screwing up somewhere
Even if you stick with the RelativeLayout
, you should not need the LinearLayout
. At runtime, you know your width, and so you should be animating the width of the gauge, not its weight. Hence, just having the View
be in the RelativeLayout
(or FrameLayout
) should be sufficient.
But I'll readily agree that this is bizarre behavior.
Looks like since its just a view and nothing has to be rendered, for some optimization sake measure methods were skipped. Change View to TextView and set some text and u will see it does appears as expected. I do understand that LinearLayout rendered the View differently(not sure if thats correct or ListView is correct)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#ff0000" >
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#ffff00"
android:padding="10dp"
android:text="foobar"
android:textColor="#000000" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignBottom="@id/text"
android:layout_alignTop="@id/text"
android:background="#00ff00"
android:orientation="horizontal"
android:weightSum="1.0" >
<TextView
android:id="@+id/testView"
android:layout_width="0dp"
android:layout_height="fill_parent"
android:layout_weight="0.5"
android:background="#0000ff"
android:singleLine="false" />
</LinearLayout>
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.text_list_item, null);
view.findViewById(R.id.text).bringToFront();
TextView testView = (TextView)view.findViewById(R.id.testView);
testView.setText("some test text for viewNo:"+String.valueOf(position));
return view;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With