FindViewById()对于布局 XML 中的自定义组件返回 null,而对于其他组件返回 null

我有一个 res/layout/main.xml包括这些元素和其他:

<some.package.MyCustomView android:id="@+id/foo" (some other params) />
<TextView android:id="@+id/boring" (some other params) />

在我的活动 onCreate 中,我这样做:

setContentView(R.layout.main);
TextView boring = (TextView) findViewById(R.id.boring);
// ...find other elements...
MyCustomView foo = (MyCustomView) findViewById(R.id.foo);
if (foo == null) { Log.d(TAG, "epic fail"); }

成功地找到了其他元素,但是 foo返回 null。MyCustomView 有一个构造函数 MyCustomView(Context c, AttributeSet a),在该构造函数末尾的 Log.d(...)在 logcat 中成功地出现在“史诗般的失败”之前。

为什么 foo是无效的?

55063 次浏览

Because in the constructor, I had super(context) instead of super(context, attrs).

Makes sense, if you don't pass in the attributes, such as the id, then the view will have no id and therefore not be findable using that id. :-)

For me, the problem was solved when I added the res folder to the Source in Java Build Path in project Settings.

Same issue, but different solution: I didn't call

setContentView(R.layout.main)

BEFORE I tried to find the view as stated here

I had the same problem. My mistake was that: I wrote

        LayoutInflater inflater = (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View layout=inflater.inflate(R.layout.dlg_show_info, null);
alertDlgShowInfo.setView(layout);
TableRow trowDesc=(TableRow)findViewById(R.id.trowDesc);

and as I used an inflater to "load" the view from an XML file, the last line was wrong. To solve it, I had to write:

TableRow trowDesc=(TableRow)layout.findViewById(R.id.trowDesc);

I wrote my solution, in case someone have the same problem.

Seems there is a variety of reasons. I just used "Clean..." in Eclipse to solve a similar problem. (FindViewByID had worked before and for some reason started to return null.)

My problem was a typo. I had written android.id (dot) instead of android:id. :P

Apparently there is no syntax check within my custom component xml. :(

Had the same problem.

I had layout with few children. From constructor of one them I was trying to get reference (by using context.findViewById) to other child. It wasn't working because the second child was defined further in layout.

I've resolved it like this:

setContentView(R.layout.main);
MyView first = findViewById(R.layout.first_child);
first.setSecondView(findViewById(R.layout.second_child));

It would work as well if the order of children was opposite,but I guess it generally should be done like above.

The findViewById() method sometimes returns null when the root of layout has no android:id attribute. Eclipse wizard for generating layout xml file does not automatically generate android:id attribute for the root element.

In my case findViewById was returning null because my custom view looked something like this in the main XML:

        <com.gerfmarquez.seekbar.VerticalSeekBar
android:id="@+id/verticalSeekBar"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
/>

and I found out that when I added the xmlns stuff it worked like this:

        <com.gerfmarquez.seekbar.VerticalSeekBar
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/verticalSeekBar"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
/>

In my case the view was in the parent NOT in the view I was trying to call it in. So in the child view I had to call:

RelativeLayout relLayout = (RelativeLayout) this.getParent();
View view1 = relLayout.findViewById(R.id.id_relative_layout_search);

Be sure that the setContentView(R.layout.main) statement call before findViewById(...) statement;

The 'clean' option worked for me.

In my case, the root cause is that the source code resides on a network share, and my workstation and fileserver were not synchronized correctly, and had drifted by 5 seconds. The timestamps on the files created by Eclipse are in the past (because they are assigned by the fileserver) w.r.t. the workstation's clock, causing Eclipse to resolve dependencies between generated and source files incorrectly. In this case, a 'clean' appears to work, because it forces a complete rebuild instead of an incremental build that depends on wrong timestamps.

Once I fixed the NTP settings on my workstation, the problem never occured again. Without proper NTP settings, it would happen every few hours, as the clocks drift fast.

I ran into the same issue a while back when I added a custom View via the layout XML and then tried to attached a callback elsewhere in the application ...

I created a custom view and added it to my "layout_main.xml"

public class MUIComponent extends SurfaceView implements SurfaceHolder.Callback {
public MUIComponent (Context context, AttributeSet attrs ) {
super ( context, attrs );
}
// ..
}

And in the main Activity I wanted to attach some callbacks and get references to the UI elements from the XML.

public class MainActivity extends Activity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);


// ...


MUIInitializer muiInit = new MUIInitializer();
muiInit.setupCallbacks(this);
muiInit.intializeFields(this);
}
}

The initilizer wasn't doing anything fancy but any changes it tried to make to the custom view (MUIComponent) or other non-custom UI elements simply were not appearing in the application.

public class MUIInitializer {


// ...


public void setupCallbacks ( Activity mainAct ) {




// This does NOT work properly
// - The object instance returned is technically an instance of my "MUICompnent" view
//   but it is a *different* instance than the instance created and shown in the UI screen
// - Callbacks never get triggered, changes don't appear on UI, etc.
MUIComponent badInst = (MUIComponent) mainAct.findViewById(R.id.MUIComponent_TESTSURF);




// ...
// This works properly


LayoutInflater inflater = (LayoutInflater) mainAct.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View inflatedLayout = inflater.inflate ( R.layout.activity_main, null );


MUIComponent goodInst = (MUIComponent) inflatedLayout.findViewById(R.id.MUIComponent_TESTSURF);




// Add callbacks
// ...
}


}

The difference between the "badInst" and "goodInst" is:

  • badInst uses the Activity's findViewByID
  • goodInst inflates the layout and uses the inflated layout to do the lookup

If you have multiple layout versions (depending on screen densities, SDK versions) make sure that all of them include the element you are looking for.

To add another trivial mistake to the answers to look out for:

Check that you're actually editing the right layout XML file...

i had the same problem because i forgot to update the view id in all my layout folders.

I had the same problem, because in my custom view I have overridden the constructor, but invoked the super constructor without the attrs parameter. (That was a copy/paste mistake.)

My previous constructor version:

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

Now I have:

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

And that works!

This happened to me with a custom component for Wear, but is a generic piece of advice. If you're using a Stub (such as I was using WatchViewStub), you can't just put the call to findViewById() anywhere. Everything inside the stub must be inflated first, which doesn't just happen after setContentView(). Thus, you should write something like this in order to wait for that to happen:

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_wear);
final WatchViewStub stub = (WatchViewStub) findViewById(R.id.watch_view_stub);
stub.setOnLayoutInflatedListener(new WatchViewStub.OnLayoutInflatedListener() {
@Override
public void onLayoutInflated(WatchViewStub stub) {
myCustomViewInst = (MyCustomView) findViewById(R.id.my_custom_view);
...