How to inflate Android View in LinearLayout class?
Asked Answered
K

3

10

I've got a little piece of xml, which I'll be using in a lot of places in my app. For this reason I want to store it in a separate file. So I created mywidget.xml in which I have my xml. I then try to inflate this in mywidget.java, after which I want to include it in a different xml file like so:

<com.mycom.android.ui.widget.AmountWidget android:layout_width="fill_parent" android:layout_height="wrap_content"></com.mycom.android.ui.widget.AmountWidget>

In my java file, I try to inflate the initial xml like this:

public class AmountWidget extends LinearLayout {
    public AmountWidget(Context context) {
        super(context);
        LinearLayout ll = (LinearLayout) findViewById(R.layout.amount_widget);
        addView(ll);
    }
}

But with the code above I get an error saying that there's an error inflating class com.mycom.android.ui.widget.AmountWiget.

My question: Does anybody know how I can inflate a layout so that I can use it as a class in another xml layout file?

The xml from the widget looks like this:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content" 
    android:layout_margin="10dp"
    android:padding="10dp"
    android:background="@layout/border"
    >
    <EditText
        android:id="@+id/payment_amount_major"
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:textSize="35sp"
        android:textStyle="bold"
        android:inputType="number"
        android:digits="0,1,2,3,4,5,6,7,8,9"
        android:maxLength="9"  
        android:gravity="right"
        />
</LinearLayout>
Keenankeene answered 1/10, 2013 at 14:32 Comment(1)
Why not use fragments? developer.android.com/guide/components/fragments.htmlShouldst
Z
6

The View class has an inflate method which wraps LayoutInflater.inflate. You should be able to use:

LinearLayout ll = (LinearLayout) inflate(context, R.layout.amount_widget, this);

to inflate your widget from xml. The call to addView() won't be needed, as inflate will add the newly inflated view for you!

Edit: Just a note, because this View is already a LinearLayout, there's no need to have the root of the xml you're inflating also be a LinearLayout. It can increase your performance if you inflate only the EditText and just add that to the parent, rather than nesting a second LinearLayout within the parent. You can set the LinearLayout attributes (such as background and padding) directly on the AmountWidget wherever it's added in xml. This shouldn't matter too much in this specific case, but may be good to know going forward if you have a situation with many nested views.

Edit2: The View class has three constructors: View(Context), View(Context, AttributeSet), and View(Context, AttributeSet, int). When the system inflates a view from xml, it will use one of the latter two. Any custom View will need to implement all three of these constructors. An easy way to do this while reusing your code is like this:

public AmountWidget(Context context) {
    super(context);
    LinearLayout ll = (LinearLayout) inflate(context, R.layout.amount_widget, this);
}

public AmountWidget(Context context, AttributeSet attrs) {
    this(context);
}

public AmountWidget(Context context, AttributeSet attrs, int defStyle) {
    this(context);
}

This will work if you don't care what the attributes or style arguments are, and just want the AmountWidget created the same any time it's inflated.

Zennas answered 1/10, 2013 at 14:43 Comment(7)
Hi @Zennas - I tried your suggestion, but I get the same android.view.InflatException: Error inflating class com.mycom.android.ui.widget.AmountWidget. Any idea what could be wrong?Keenankeene
Are you using this view by referencing it in another xml file? You probably need to override all three of the View constructors, as the system will attempt to use one with more arguments. I'll edit my post with an example.Zennas
Alright, this got me a step further; I now have an error saying that "the specific child already has a parent, and that I need to call removeView() on the child's parent first". Although it seems to say exactly what I need to do, I'm kinda lost in this. Is this because the thing you said about my LinearView being inflated in a new LinearView, or is this because something else?Keenankeene
Try removing the call to addView(), inflate may be attaching the inflated view. If that doesn't work, instead of removing addView() try passing null instead of this as the third argument for inflate.Zennas
Awesome! removing the addView() solved everything! You sir, made my day! Greets from Amsterdam!Keenankeene
Happy to help! I'll modify the answer once more to reflect the final working solution and to clarify my comments about performance.Zennas
View is already a LinearLayout, there's no need to have the root of the xml you're inflating also be a LinearLayout. It can increase your performance . Solution to that commetn https://mcmap.net/q/580256/-how-to-correctly-extend-linearlayout-to-create-a-custom-viewHerrenvolk
C
14

Try this:

// ViewGroup to add views
LinearLayout list = (LinearLayout) findViewById(R.id.linear_layout_list);

// Inflate you custom xml to view
// Ex: res/layout/item_data.xml
View item = getLayoutInflater().inflate(R.layout.item_data, null); 

// Link item in list here
list.addView(item);

mContainerView is LinearLayout which contain your EditText and row is your xml filename.

Crosshatch answered 1/10, 2013 at 14:40 Comment(1)
Hi @ved - I tried your suggestion, but I get a message saying that "The method getSystemService(String) is undefined for the type AmountWidget". Any ideas how I could solve thatKeenankeene
Z
6

The View class has an inflate method which wraps LayoutInflater.inflate. You should be able to use:

LinearLayout ll = (LinearLayout) inflate(context, R.layout.amount_widget, this);

to inflate your widget from xml. The call to addView() won't be needed, as inflate will add the newly inflated view for you!

Edit: Just a note, because this View is already a LinearLayout, there's no need to have the root of the xml you're inflating also be a LinearLayout. It can increase your performance if you inflate only the EditText and just add that to the parent, rather than nesting a second LinearLayout within the parent. You can set the LinearLayout attributes (such as background and padding) directly on the AmountWidget wherever it's added in xml. This shouldn't matter too much in this specific case, but may be good to know going forward if you have a situation with many nested views.

Edit2: The View class has three constructors: View(Context), View(Context, AttributeSet), and View(Context, AttributeSet, int). When the system inflates a view from xml, it will use one of the latter two. Any custom View will need to implement all three of these constructors. An easy way to do this while reusing your code is like this:

public AmountWidget(Context context) {
    super(context);
    LinearLayout ll = (LinearLayout) inflate(context, R.layout.amount_widget, this);
}

public AmountWidget(Context context, AttributeSet attrs) {
    this(context);
}

public AmountWidget(Context context, AttributeSet attrs, int defStyle) {
    this(context);
}

This will work if you don't care what the attributes or style arguments are, and just want the AmountWidget created the same any time it's inflated.

Zennas answered 1/10, 2013 at 14:43 Comment(7)
Hi @Zennas - I tried your suggestion, but I get the same android.view.InflatException: Error inflating class com.mycom.android.ui.widget.AmountWidget. Any idea what could be wrong?Keenankeene
Are you using this view by referencing it in another xml file? You probably need to override all three of the View constructors, as the system will attempt to use one with more arguments. I'll edit my post with an example.Zennas
Alright, this got me a step further; I now have an error saying that "the specific child already has a parent, and that I need to call removeView() on the child's parent first". Although it seems to say exactly what I need to do, I'm kinda lost in this. Is this because the thing you said about my LinearView being inflated in a new LinearView, or is this because something else?Keenankeene
Try removing the call to addView(), inflate may be attaching the inflated view. If that doesn't work, instead of removing addView() try passing null instead of this as the third argument for inflate.Zennas
Awesome! removing the addView() solved everything! You sir, made my day! Greets from Amsterdam!Keenankeene
Happy to help! I'll modify the answer once more to reflect the final working solution and to clarify my comments about performance.Zennas
View is already a LinearLayout, there's no need to have the root of the xml you're inflating also be a LinearLayout. It can increase your performance . Solution to that commetn https://mcmap.net/q/580256/-how-to-correctly-extend-linearlayout-to-create-a-custom-viewHerrenvolk
H
6

Easiest Solution

LinearLayout item = (LinearLayout )findViewById(R.id.item);//where you want to add/inflate a view as a child

View child = getLayoutInflater().inflate(R.layout.child, null);//child.xml

item.addView(child);

ImageView Imgitem = (ImageView ) child.findViewById(R.id.item_img);

Imgitem.setOnClick(new ...
Herrenvolk answered 25/10, 2014 at 13:26 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.