0

Why do some views in a linearlayout within a scrollview fill the parent when fill_parent is set when inflated from xml directly but don't when added via a custom component that inflates the same xml?

The result of inflating the same xml in two different places:

http://imgur.com/DF6kl

The top item of the two is as a result of inflating the xml in a class that extends linearLayout and then adding that class to the linearLayout inside the scroll view. This view isn't filling the parent and I can't work out why.

The bottom item is the result of inflating the xml directly in the activity that this is all contained within. It appears as I expected.

This is the code that adds the two views in the activity:

scrollList.addView(new CommunityTaskListItem(this, null));
scrollList.addView(View.inflate(getBaseContext(), R.layout.communitytask, null));

This is the code that inflates the activity within a custom component class "CommunityTaskListItem":

View communityTask = View.inflate(context, R.layout.communitytask, null);
addView(communityTask);

I assume the xml is okay because it works fine when inflated directly in the activity. My question is why the fill_parent property seems to be lost when the xml is inflated in a custom component's class?

For good measure, here is the xml being inflated: EDIT: I gave you the wrong one before!!! Here's the right one:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent" android:layout_height="wrap_content">
    <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout1" android:background="@drawable/plainbutton" android:layout_marginBottom="5px" android:layout_marginTop="5px" android:layout_weight="1">
        <TextView android:layout_height="wrap_content" style="@style/CommunityTaskText" android:layout_width="fill_parent" android:layout_weight="1" android:id="@+id/textViewCommunityTaskTimes" android:text="xx:xx - xx:xx"></TextView>
        <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" style="@style/CommunityTaskText" android:id="@+id/textViewCommunityTaskDescription" android:text="Task"></TextView>
    </LinearLayout>

</LinearLayout>

I'd like to keep the custom component and not inflate the xml within my activity if I could.

EDIT: Code where CommunityTaskListItem is inflated:

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.TextView;

public class CommunityTaskListItem extends LinearLayout {

    TextView textViewCommunityTaskTimes;
    TextView textViewCommunityTaskDescription;

    public CommunityTaskListItem(Context context, AttributeSet attrs) {
        super(context, attrs);

        View communityTask = View.inflate(context, R.layout.communitytask, null);
        addView(communityTask);

        textViewCommunityTaskTimes = (TextView)findViewById(R.id.textViewCommunityTaskTimes);
        textViewCommunityTaskDescription = (TextView)findViewById(R.id.textViewCommunityTaskDescription);

    }
...

EDIT: All working now! Here's the final code for anyone else incase they have a similar issue: Contructor for the custom object:

    public CommunityTaskListItem(Context context, AttributeSet attrs) {
        super(context, attrs);

        setOrientation(VERTICAL);
        addView(inflate(context, R.layout.communitytask, null));

        textViewCommunityTaskTimes = (TextView)findViewById(R.id.textViewCommunityTaskTimes);
        textViewCommunityTaskDescription =     (TextView)findViewById(R.id.textViewCommunityTaskDescription);
    }

Custom object xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout1" android:background="@drawable/plainbutton" android:layout_marginBottom="5px" android:layout_marginTop="5px" android:layout_weight="1">
        <TextView android:layout_height="wrap_content" style="@style/CommunityTaskText" android:layout_width="fill_parent" android:layout_weight="1" android:id="@+id/textViewCommunityTaskTimes" android:text="xx:xx - xx:xx"></TextView>
        <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" style="@style/CommunityTaskText" android:id="@+id/textViewCommunityTaskDescription" android:text="Task"></TextView>
    </LinearLayout>

</LinearLayout>

To be honest I don't know exactly what the mistake I was making was so if someone could make that clear I'd be very grateful.

Sam
  • 1,509
  • 3
  • 19
  • 28

1 Answers1

2

The first line should be:

scrollList.addView(new CommunityTaskListItem(this, null)
    new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));

You call inflate() inside CommunityTaskListItem class and inflated view becomes a child view of the CommunityTaskListItem view. But you don't set width and height for this view so it doesn't match its parent's width.

EDIT: I think your XML should look like this:

<?xml version="1.0" encoding="utf-8"?>
<packagename.CommunityTaskListItem
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:orientation="vertical"
  android:layout_width="fill_parent" android:layout_height="wrap_content">
    <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout1" android:background="@drawable/plainbutton" android:layout_marginBottom="5px" android:layout_marginTop="5px" android:layout_weight="1">
        <TextView android:layout_height="wrap_content" style="@style/CommunityTaskText" android:layout_width="fill_parent" android:layout_weight="1" android:id="@+id/textViewCommunityTaskTimes" android:text="xx:xx - xx:xx"></TextView>
        <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" style="@style/CommunityTaskText" android:id="@+id/textViewCommunityTaskDescription" android:text="Task"></TextView>
    </LinearLayout>

</packagename.CommunityTaskListItem>

where packagename is the name of the package of the CommunityTaskListItem class.

In this case you should change the CommunityTaskListItem:

public class CommunityTaskListItem extends LinearLayout {

    TextView textViewCommunityTaskTimes;
    TextView textViewCommunityTaskDescription;

    public CommunityTaskListItem(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    @Override
    protected void onFinishInflate() {
        textViewCommunityTaskTimes = (TextView)findViewById(R.id.textViewCommunityTaskTimes);
        textViewCommunityTaskDescription = (TextView)findViewById(R.id.textViewCommunityTaskDescription);
    }

    // ....
}

This class cannot be created using its constructor. It can be only inflated.

The second way is to inflate children in the constructor like you do. But the XML should differ:

<?xml version="1.0" encoding="utf-8"?>
<merge
  xmlns:android="http://schemas.android.com/apk/res/android">
    <LinearLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:id="@+id/linearLayout1" android:background="@drawable/plainbutton" android:layout_marginBottom="5px" android:layout_marginTop="5px" android:layout_weight="1">
        <TextView android:layout_height="wrap_content" style="@style/CommunityTaskText" android:layout_width="fill_parent" android:layout_weight="1" android:id="@+id/textViewCommunityTaskTimes" android:text="xx:xx - xx:xx"></TextView>
        <TextView android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_weight="1" style="@style/CommunityTaskText" android:id="@+id/textViewCommunityTaskDescription" android:text="Task"></TextView>
    </LinearLayout>

</merge>

The CommunityTaskListItem will be: public class CommunityTaskListItem extends LinearLayout {

    TextView textViewCommunityTaskTimes;
    TextView textViewCommunityTaskDescription;

    public CommunityTaskListItem(Context context) {
        this(context, null);
    }

    public CommunityTaskListItem(Context context, AttributeSet attrs) {
        super(context, attrs);

        setOrientation(VERTICAL);
        inflate(context, R.layout.communitytask);

        textViewCommunityTaskTimes = (TextView)findViewById(R.id.textViewCommunityTaskTimes);
        textViewCommunityTaskDescription = (TextView)findViewById(R.id.textViewCommunityTaskDescription);
    }

    // ....
}

This class can be created using its contructor and inflated from an XML. But if you want to inflate it, you should create a separate XML file. Let's call it task.xml.

<?xml version="1.0" encoding="utf-8"?>
<packagename.CommunityTaskListItem
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent" android:layout_height="wrap_content"/>

And that's how you can use it:

scrollList.addView(new CommunityTaskListItem(this),
    new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
scrollList.addView(View.inflate(getBaseContext(), R.layout.task, null));
Michael
  • 53,859
  • 22
  • 133
  • 139
  • I just tried that now but I had no luck. Everything still looks the same. I assume there should be a comma between "...null)" and "new LayoutPar...". – TomOctober Sep 11 '11 at 11:18
  • Could you please show a piece of code where `CommunityTaskListItem` inflates its children? – Michael Sep 11 '11 at 11:20
  • Done. I also found that calling setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT)); in the CommunityTaskListItem didn't help either. – TomOctober Sep 11 '11 at 11:26
  • I don't understand why setting `LayoutParams` doesn't work but I offer you another solution in the answer. – Michael Sep 11 '11 at 11:34
  • I've changed my xml as you suggested but now I get an error inflating the xml. "Error inflating lass packagename.blah.blah" – TomOctober Sep 11 '11 at 11:50
  • In this case you shouldn't call `inflate()` inside `CommunityTaskListItem` constructor. Just bind to the views in `onFinishInflate()`. – Michael Sep 11 '11 at 11:53
  • I gave you the wrong xml to be inflated, I've edited my question accordingly. – TomOctober Sep 11 '11 at 12:00
  • I don't really understand what you mean by "Just bind to the views in onFinishInflate()" – TomOctober Sep 11 '11 at 12:09
  • I've added more information to my answer. Sorry for such a long text :) – Michael Sep 11 '11 at 12:18
  • Thank you! In the second approach, what is the thing about? – TomOctober Sep 11 '11 at 12:37
  • I tried to use the second approach but I'm still getting errors when I open the relevant activity in the emulator: android.view.InflateException: can be used only with a valid ViewGroup root and attachToRoot=true – TomOctober Sep 11 '11 at 12:53
  • Brilliant, all sorted now. I just modified your second aproach by removing the merge tags and using linearLayout instead and putting addView.inflate(context, R.layout.communitytask); instead of inflate(context, R.layout.communitytask); – TomOctober Sep 11 '11 at 13:00
  • The idea is not to use `LinearLayout` because in this case you get additional view in the hierarchy. But layout with the `` root tag must be inflated with non-null parent. It means this layout can't be added to view after inflation, it can be just inflated like a child of some other view. Try reading this: http://developer.android.com/resources/articles/layout-tricks-merge.html. – Michael Sep 11 '11 at 14:57
  • That makes a lot of sense. Thank you. – TomOctober Sep 13 '11 at 19:41