findViewById在include中无法工作?

63

我有一个类似这样的include:

<include
    android:id="@+id/placeHolder"
    layout="@layout/test" />

include布局如下(test.xml):

<?xml version="1.0" encoding="utf-8"?>

<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/outer"
    ... >

    <ImageView
        android:id="@+id/inner"
        ... />
</FrameLayout>

我似乎无法在运行时找到ID为"inner"的内部ImageView:

ImageView iv = (ImageView)findViewById(R.id.inner);
if (iv == null) {
    Log.e(TAG, "Not found!");
}

我应该能找到它吗?似乎由于使用了“include”,正常的findViewById方法无法工作。

---------- 更新 ----------------

所以我可以找到分配给include的id:

View view = findViewById(R.id.placeHolder); // ok

但我无法通过id找到任何它的子元素,例如:

view.findViewById(R.id.outer); // nope
view.findViewById(R.id.inner); // nope

如果我直接搜索它们,结果会与原始结果相同:

findViewById(R.id.outer); // nope
findViewById(R.id.inner); // nope

在运行时,id 在 <a> 元素中是否被剥离了?

谢谢


抱歉,“test.xml”有错别字,谢谢。 - user291701
如果有任何错误,请发布logcat。 - Samir Mangroliya
最后我发现是在引用包含的布局文件中的视图时出了问题。我正在包含SDK提供的一个布局资源,因此必须在我的findViewById()中使用android.R.id.而不是R.id.。一旦我解决了这个问题,dymmeh的解决方案就很好用了。 - jhavatar
9个回答

105
尝试检索<include />,然后在其中进行搜索。
确保您的根元素具有与所包含的XML文件中根元素相同的ID。例如:
<include
    android:id="@+id/outer"
    layout="@layout/test" />

然后使用以下代码获取您的“内部”内容:

FrameLayout outer = (FrameLayout)findViewById(R.id.outer);

ImageView iv = (ImageView)outer.findViewById(R.id.inner);
if (iv == null) {
    Log.e(TAG, "Not found!");
}

13
请尝试使用我的更新答案。确保你所包含的文件的ID与你所包含的XML文件中的根元素相同。 - dymmeh
2
如果include的id与内部的根元素相同,则没有覆盖它的必要,它将按原样展开。根据LayoutInflater源码,它不会覆盖合并根元素的id - 这使得重用布局几乎无用,因为它们可能共享id,并在包含时被覆盖。 - Rafael Nobre
1
这是一篇旧帖子,但在今天仍然非常有用。非常感谢@dymmeh,也许您可以通过添加注释来小心使用强制转换到Android小部件,例如FrameLayout。也许这很明显,但在我的情况下,我将FrameLayout转换为LinearLayout时遇到了NPE。 - e2a
7
我想强调一下确保包含元素的id与被包含XML文件中的根元素的id相同的重要性,正如@dymmeh所说的那样。第一次阅读答案时我完全错过了这个建议,而且真的很重要,需要特别注意。非常感谢您的帮助! - blastervla
对于那些想知道的人。标签<include>默认情况下会像父布局一样运作,你可能需要在android:layout="@layout/your_layout"内部将其转换为YourParentLayout,否则会抛出ClassCastException异常。 - mochadwi

21

我想为未来的谷歌搜索者补充一点内容:我遇到了相反的问题,即在调用findViewById()时,包含的xml文件(在此示例中为outer)的根布局为空,但outer的子项不为空。

我的问题是通过删除include 'view'(在本例中为placeHolder)的id属性来解决的。当该属性被删除后,我可以找到outer但它的id没有了 :)

如果您需要为include项分配id,我认为最好将其包装在另一个viewgroup中。


7
这对我很有用 - 谢谢。我进行了更深入的调查,发现以下内容(至少在我的非片段代码中):如果你有一个带有ID的include,并且想访问所包含结构的顶层,则include ID必须与所包含项的顶层结构的ID相同。这感觉不对,因为它似乎是一个重复的名称 - 但它有效。另一种可行的选择是你的解决方案 - 即在include上省略名称。 - Richard
对于其他 Google 用户,如果您正在使用片段,则可以尝试在 onViewCreated 中调用 findViewById :) - nmu
1
对于未来的读者,<include/> id 将覆盖子布局 id,如果您使用该 <include/> id 而不是子布局 id 进行 findViewById,它仍然可以工作,否则将为空。另一种方法是这个答案建议的,即删除 <include/> id,只保留子布局 id。并且不要忘记更改其他内容,例如 layout_above,它引用了您添加的 <include> id。 - 林果皞
谢谢,那就是我的问题。你不需要寻找包含的布局,只需要寻找它的子视图即可。 - chill appreciator

1

未来的研究人员: https://dev59.com/DXI-5IYBdhLWcg3wn566#1760010 当您给include标签添加id时,它会覆盖来自“真实布局”的外部标签的id。

鉴于大多数约束布局需要一个id来定位项目,从include中删除id并不是一个理想的答案。


1
不要给包含的布局设置ID。
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/twitterItem"
    android:id="@+id/item1">

    <include
        layout="@layout/item_common_layout"/>

</FrameLayout>

item_common_layout

<FrameLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    ... >

    <ImageView
        android:id="@+id/inner"
        ... />
</FrameLayout>

现在您可以找到内部视图:

ImageView iv = (ImageView) view.findViewById(R.id.inner); // works fine!

0

我曾经遇到过同样的问题。后来发现我使用了一个带有两个参数的错误函数,其名称与正确函数相同,如下所示:

public void onCreate(@Nullable Bundle savedInstanceState, @Nullable PersistableBundle persistentState) {
    super.onCreate(savedInstanceState, persistentState);
    setContentView(R.layout.activity_ble_connecting);
    //findViewByid() => wrong
} 

只有一个参数的正确函数:

    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_ble_connecting);
        //findViewByid() => correct
    }

0

你能试试找到外部parent(我们称之为op),然后尝试op.findViewById()吗?


0

我按照Daniel Wilson的答案并获得了解决方案。我在同一屏幕上多次使用相同的布局,并希望使用不同的ID引用它们。因此,我没有给包含标签分配ID,而是将它们简单地包装在另一个ViewGroup中(我使用FrameLayout),并为FrameLayout分配了ID以访问内部子项。那成功了。 例如:

在我的main.xml中:

<!--    ITEM 1-->
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/twitterItem"
    android:id="@+id/item1">

    <include
        layout="@layout/item_common_layout"/>

</FrameLayout>

<!--    ITEM 2-->
<FrameLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_constraintTop_toBottomOf="@id/item1"
    android:id="@+id/item2">

    <include
        layout="@layout/item_common_layout"/>

</FrameLayout>

0
如果您已经给include元素分配了id,并且在所包含的布局中只有一个单独的视图(不在任何ViewGroup中),例如我的情况下是AdView,那么请使用include元素的id而不是被包含的视图的id。
<?xml version="1.0" encoding="utf-8"?>
<!--adview.xml-->
<com.google.android.gms.ads.AdView
        xmlns:ads="http://schemas.android.com/apk/res-auto"
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/adView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        ads:adSize="BANNER"
        ads:adUnitId="@string/admob_banner_id">
</com.google.android.gms.ads.AdView>

activity_main.xml

<include
        layout="@layout/adview"
        android:id="@+id/adsLayout"
  />

在活动中

AdView nullAdView=findViewById(R.id.adView); //this will give null
AdView adView = findViewById(R.id.adsLayout);  //this will be AdView

如果您没有为include元素提供id,那么使用视图id是可以的。此外,如果您的view位于任何ViewGroup中,例如FrameLayout,则使用视图id也是可以的。


0
我找到了这个问题的答案。
当我们设置Incloud ID和第一个内层时,就会出现这个问题。
我使用合并来解决这个问题。
 <include
    android:id="@+id/layout_stories"
    layout="@layout/layout_stories" />

并且在layout_stories中

<merge>
<LinearLayout
android:id="@+id/llItem">
 ...
</LinearLayout>
<merge>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接