Leos Literak
Leos Literak

Reputation: 9406

NPE when adding inflated button

I try to create new Button programatically and it to existing ViewGroup (I moved from custom to classical LinearLayout to ensure bug is not in custom ViewGroup).

A code is simple and it is working in different use case:

private void appendTile() {
    View view = getLayoutInflater().inflate(R.layout.template_tile, null);
    view.setId(View.generateViewId());
    view.setOnClickListener(tileListener);
    hiddenPicture.addView(view, view.getLayoutParams());
}

template_tile.xml:

<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text=""
style="@style/FormulaValue" />

But it fails, because view.getLayoutParams() is null (though there are layout_width and layout_height in the XML).

Caused by: java.lang.NullPointerException: Attempt to read from field 'int android.view.ViewGroup$LayoutParams.width' on a null object reference
   at android.view.ViewGroup$LayoutParams.<init>(ViewGroup.java:6453)
   at android.view.ViewGroup$MarginLayoutParams.<init>(ViewGroup.java:6735)
   at android.widget.LinearLayout$LayoutParams.<init>(LinearLayout.java:1901)
   at android.widget.LinearLayout.generateLayoutParams(LinearLayout.java:1799)
   at android.widget.LinearLayout.generateLayoutParams(LinearLayout.java:62)
   at android.view.ViewGroup.addViewInner(ViewGroup.java:3945)
   at android.view.ViewGroup.addView(ViewGroup.java:3786)
   at android.view.ViewGroup.addView(ViewGroup.java:3758)
   at lelisoft.com.lelimath.activities.PuzzleActivity.appendTile(PuzzleActivity.java:39)
   at lelisoft.com.lelimath.activities.PuzzleActivity.onCreate(PuzzleActivity.java:31)

I went to ViewGroups constructor and it reads width from null params. What shall I do differently in my activity? Why layout_width from XML is not set?

Upvotes: 0

Views: 47

Answers (2)

dhke
dhke

Reputation: 15398

LayoutParams are specific to the container (i.e. the ViewGroup). Since you are inflating without a root parameter, the layout parameters are ignored, because there is simply no "proper" way to create a suitable instance of LayoutParams without knowing the container type.

To use the layout parameters from XML, you need to supply a root ViewGroup when inflating:

View view = getLayoutInflater().inflate(
    R.layout.template_tile,
    hiddenPicture,
    false // don't attachToRoot
);
view.setId(View.generateViewId());
view.setOnClickListener(tileListener);
hiddenPicture.addView(view, view.getLayoutParams());

Note that the third parameter attachToRoot is false here. If you set it to true, the inflated view is automatically attached to its parent container, which simplifies the typical case.

However, when attachToRoot == true the return value from inflate() is not the Button instance but hiddenPicture, i.e. the supplied root View of the affected hierarchy.

In that case, never get direct access to the Button object. Since you do manual setup (attach a listener, set the ID) on the inflated button, manual attach is simpler.

Upvotes: 2

laalto
laalto

Reputation: 152887

Layout params come from the parent object. Since you're inflating with a null parent, your view doesn't have layout params yet.

Replace

addView(view, view.getLayoutParams())

with

addView(view)

to not supply the layout params yourself and let the framework generate ones for you.

Upvotes: 2

Related Questions