findViewById() 为布局 XML 中的自定义组件返回 null,而不是其他组件

Posted

技术标签:

【中文标题】findViewById() 为布局 XML 中的自定义组件返回 null,而不是其他组件【英文标题】:findViewById() returns null for custom component in layout XML, not for other components 【发布时间】:2010-12-14 01:50:10 【问题描述】:

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

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

在我的 Activity 的 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 为空?

【问题讨论】:

【参考方案1】:

因为在构造函数中,我有super(context) 而不是super(context, attrs)

有道理,如果您不传递属性,例如 id,那么视图将没有 id,因此无法使用该 id 找到。 :-)

【讨论】:

另外,像(MyCustomView) foo = findViewById(R.id.foo); 这样的行不应该是MyCustomView foo = (MyCustomView) findViewById(R.id.foo);吗? 有同样的问题,在我的情况下,我忘记了 setContentView().. XD 在 vogella.com 上有一个很好的例子:vogella.com/articles/AndroidCustomViews/article.html 另外,如果您有多个构造函数,您需要将所有 super() 方法更新为正确的方法。 当我看到好的 'ol findViewById() 返回 null 时,我开始心脏病发作......你节省了我的时间!谢谢!【参考方案2】:

我遇到了同样的问题,因为在我的自定义视图中我已经覆盖了构造函数,但是调用了没有attrs 参数的超级构造函数。 (这是一个复制/粘贴错误。)

我之前的构造函数版本:

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

现在我有:

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

这行得通!

【讨论】:

【参考方案3】:

我遇到了同样的问题。我的错误是:我写了

        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);

当我使用充气器从 XML 文件“加载”视图时,最后一行是错误的。为了解决它,我不得不写:

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

我写了我的解决方案,以防有人遇到同样的问题。

【讨论】:

【参考方案4】:

似乎有多种原因。我只是在 Eclipse 中使用“Clean ...”来解决类似的问题。 (FindViewByID 之前工作过,但由于某种原因开始返回 null。)

【讨论】:

显然,根本问题是 R.java ID 以某种方式损坏或可能未更新。我不仅在 ID 上注意到了这一点,而且在其他情况下也注意到了这一点,例如TextView 等中显示的字符串错误。不过,我真的不知道为什么会发生这种情况。【参考方案5】:

同样的问题,但不同的解决方案:我没有打电话

setContentView(R.layout.main)

在我试图找到here 中所述的视图之前

【讨论】:

如果您在其他视图而不是当前相关视图上获取元素,我认为这是解决方案。【参考方案6】:

如果您有多个布局版本(取决于屏幕密度、SDK 版本),请确保所有布局版本都包含您要查找的元素。

【讨论】:

【参考方案7】:

在我的例子中 findViewById 返回 null 因为我的自定义视图在主 XML 中看起来像这样:

        <com.gerfmarquez.seekbar.VerticalSeekBar  
            android:id="@+id/verticalSeekBar"
            android:layout_ 
            android:layout_ 
            />

我发现当我添加 xmlns 的东西时,它的工作原理是这样的:

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

【讨论】:

【参考方案8】:

确保setContentView(R.layout.main) 语句在findViewById(...) 语句之前调用;

【讨论】:

【参考方案9】:

对我来说,当我将 res 文件夹添加到项目设置中 Java 构建路径中的 Source 时,问题就解决了。

【讨论】:

【参考方案10】:

不久前,当我通过布局 XML 添加自定义视图时,我遇到了同样的问题 然后尝试在应用程序的其他地方附加回调...

我创建了一个自定义视图并将其添加到我的“layout_main.xml”中

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

在主 Activity 中,我想附加一些回调并从 XML 中获取对 UI 元素的引用。

public class MainActivity extends Activity 

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

        // ...

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

初始化器并没有做任何花哨的事情,但它试图对自定义视图 (MUIComponent) 进行任何更改 或其他非自定义 UI 元素根本没有出现在应用程序中。

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
        // ...
    


“badInst”和“goodInst”的区别是:

badInst 使用 Activity 的 findViewByID goodInst 膨胀布局并使用膨胀的布局进行查找

【讨论】:

注意到文森特有相同的解决方案......他的答案更短......改为+1 :)【参考方案11】:

这发生在我的 Wear 自定义组件上,但这是一条通用的建议。如果您使用的是 Stub(例如我使用的是 WatchViewStub),您不能只在任何地方调用 findViewById()。存根中的所有内容都必须首先膨胀,这不仅仅发生在setContentView() 之后。因此,你应该写这样的东西来等待它发生:

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);
            ...

【讨论】:

【参考方案12】:

我的问题是一个错字。我写的是android.id(点)而不是android:id。 :P

显然我的自定义组件 xml 中没有语法检查。 :(

【讨论】:

【参考方案13】:

遇到同样的问题。

我有几个孩子的布局。从其中一个的构造函数中,我试图(通过使用 context.findViewById)获取对其他孩子的引用。它不起作用,因为第二个孩子在布局中被进一步定义。

我已经这样解决了:

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

如果孩子的顺序相反也可以,但我想一般应该像上面那样做。

【讨论】:

一般你不应该在View的构造函数中使用findViewById,而是将初始化代码放在OnFinishInflate中?【参考方案14】:

当布局的根没有android:id 属性时,findViewById() 方法有时会返回null。用于生成布局 xml 文件的 Eclipse 向导不会自动为根元素生成 android:id 属性。

【讨论】:

【参考方案15】:

在我的情况下,视图在父视图中而不是在我试图调用它的视图中。所以在子视图中我必须调用:

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

【讨论】:

【参考方案16】:

“干净”选项对我有用。

就我而言,根本原因是源代码位于网络共享上,而我的工作站和文件服务器没有正确同步,并且偏移了 5 秒。 Eclipse 创建的文件的时间戳是过去的(因为它们是由文件服务器分配的)w.r.t。工作站的时钟,导致 Eclipse 错误地解析生成文件和源文件之间的依赖关系。在这种情况下,“清理”似乎有效,因为它强制完全重建,而不是依赖于错误时间戳的增量构建。

一旦我在我的工作站上修复了 NTP 设置,问题就再也没有发生过。如果没有适当的 NTP 设置,它每隔几个小时就会发生一次,因为时钟漂移得很快。

【讨论】:

应将此添加到上述answer 的评论中【参考方案17】:

在要注意的答案中添加另一个小错误:

检查您是否真的在编辑正确的布局 XML 文件...

【讨论】:

【参考方案18】:

我遇到了同样的问题,因为我忘记更新所有布局文件夹中的视图 ID。

【讨论】:

以上是关于findViewById() 为布局 XML 中的自定义组件返回 null,而不是其他组件的主要内容,如果未能解决你的问题,请参考以下文章

实例化Layout中的布局文件(xml)

findViewById() 为对话框中的视图返回 null

findViewById 为现有的包含布局返回 null

View 类中的 findViewById 返回 null 而在 Activity 类中有效

片段中的按钮自定义视图

Android LayoutInflater详解