【问题标题】:ListFragment RuntimeException 'android.R.id.list' missingListFragment RuntimeException 'android.R.id.list' 缺失
【发布时间】:2013-01-07 22:52:53
【问题描述】:

2013 年 1 月 16 日编辑:问题已解决!两天前,我发布了应用程序的更新版本,并正确识别了ListViews,就像我最初所做的那样,使用android:id="@android:id/list。但实际上,抛出的异常具有误导性,与实际问题无关......

当我实施 Sean 提出的通过将 null 传递给 ViewGroup 来增加 ListFragment 视图的建议时,真正的解决方案来了。我没有收到来自新版本的相同异常,并且新版本已被足够多的设备下载,可以安全地说此问题已解决。但是,我非常感谢 Lawrence 的 cmets 和回答,并将继续阅读有关他如何实例化 Fragments 的信息。虽然他提供了将 null 传递给 View inflater 的建议,但实际上 Sean 是第一个这样做的,并且由于他的解决方案似乎已经解决了问题,因此将相应地奖励赏金。如果我了解/阅读更多关于实例化片段的不同方法的信息,我将在未来更新这个线程,正如劳伦斯的出色回答所建议的那样。可惜我不能分红!

编辑 1/14/2013: ListView 相关的 XML 文件位于 http://pastebin.com/2xnG1VfF @LawrenceChoy 的要求

2013 年 1 月 13 日编辑:我发布了我的应用程序的更新版本,并根据 Bishan 的建议使用了 android:id="@+id/android:list 而不是 android:id="@android:id/list",但是我仍然收到我的 Fragment Activity 的随机异常没有a ListView whose id attribute is 'android.R.id.list',即使该活动没有任何ListViews。添加了 LGE 手机的另一个例外。

编辑 2013 年 1 月 9 日: 虽然 Bishan 提供了答案,但我希望能得到更多解释,说明他的解决方案为何可以防止此异常。进一步阅读后,我相信这是因为在使用自定义 ListView 时,应该使用 + 符号。但是想得到确认我是正确的或得到澄清,特别是考虑到两者似乎都可以在许多不同的设备和模拟器上工作。在大约 20 分钟内筹集赏金以寻求有关此事的信誉良好的消息来源/答案。谢谢。


在最近更新到 Play 商店后,我发现一些设备出现了一些非常奇怪的行为。

在我的新版本中,我有一个 FragmentActivity(称为 TabsStyled),它使用 FragmentStatePagerAdapter 来管理多个片段。我总共有 4 个片段,其中 2 个片段扩展了 ListFragment,并且列表视图由 LoaderManagerCursorLoader 填充,全部来自 support.v4 库。其他两个片段没有ListViews,也没有扩展ListFragment

我在多个设备以及 AVD 上进行了广泛的测试,并且从未遇到过因缺少 android.R.id.list 而导致的任何 RuntimeExceptions。

但是,在发布后,FragmentAcitivty "Your content must have a ListView whose id attribute is 'android.R.id.list'" 出现了一些 ACRA 崩溃。 TabsStyled.class 是管理 ListFragments 的FragmentActivity,它本身没有任何“ListViews”。这对我来说似乎很奇怪,因为我认为如果要抛出这个异常,它应该是 ListFragment 之一,而不是“父”FragmentActivity。

这是堆栈跟踪的顶部,我很乐意根据要求提供任何其他代码或信息,而不是在此处粘贴所有内容。

java.lang.RuntimeException: Unable to start activity ComponentInfo{com.myapp.hiddenforsecurity/com.myapp.hiddenforsecurity.TabsStyled}: 
java.lang.RuntimeException: Your content must have a ListView whose id attribute is 'android.R.id.list'

此处的 pastebin 上提供了完整的堆栈跟踪:http://pastebin.com/aiV7pwXP

包含 ListViews 的每个 ListFragments 的 XML(每个 ListFragment 的单独 XML 中的单独 ListView):

<ListView android:id="@android:id/list" 
    <!--the release currently uses "@+id/android:list)-->
    android:layout_width="fill_parent" 
    android:layout_height="0dip" 
    android:layout_weight="45" />

其中一个ListFragments(稍加简化以便于诊断)上传到pastebin(另一个ListFragment非常相似):http://pastebin.com/Nts3nVx1

还有一个稍微简化的完整版 TabsStyled (FragmentActivity):http://pastebin.com/ZS4Xg8kP

迄今为止创建异常的设备(截至 1/14,它们继续发布崩溃):

MODEL       BRAND    ANDROID API
GT-I9305    Samsung  4.1.1 (using @android:id/list)
GT-S7562    Samsung  4.0.4 (using @android:id/list)
GalaxyNexus Samsung  4.2.1 (same exception seen with @+id/android:list)
L-01E       LGE      4.0.4 (same exception seen with @+id/android:list)

【问题讨论】:

  • 从 logcat 中可以看到,当错误发生时,Activity 正在尝试创建 ListFragment。你能发布那些ListFragment 的所有布局文件吗?另外,id的正确形式应该是`android:id="@android:id/list",如下所述,碧山的回答是不正确的。
  • 感谢@LawrenceChoy 的评论,我在这里上传了布局文件:pastebin.com/2xnG1VfF。两个 ListFragment 几乎相同。
  • 请看下面我的回答。
  • 我总是将视图 ID 设置为 @+id/id_name。我认为 Android 开发者网站上的 View page 有一个很好的解释。此外,正如First app tutorial 所述,仅当您第一次定义资源 ID 时才需要资源类型前的加号 (+)。 我猜您正在定义它第一次?
  • @NiklasEkman,感谢您的评论。对于 ListFragment 中的 ListView,这是一种非常特殊的情况,在定义 ListView 时一定不能使用@+id。根据文档,您只能使用 android:id="@android:id/list"。无论如何,这确实是/不是我遇到的问题的根源(丢失的 'android.R.id.list' 异常被抛出是误导性的),因为核心问题看起来与对 ViewGroup 中的引用有关每个 ListFragment 和/或片段被实例化的方式。

标签: android android-listfragment android-fragmentactivity


【解决方案1】:

只是一种预感,但请尝试在不使用 ViewGroup 的情况下为 ListFragment 扩展自定义视图。所以在你的 ListFragment 改变

View logv = inflater.inflate(R.layout.listroot, container, false);

View logv = inflater.inflate(R.layout.listroot, null);

【讨论】:

  • 并将 android:id 改回“@android:id/list”。碧山的回答是错误的。
  • 我快速跟踪到发布,应该在明天或后天知道,因为我每天都会看到几个这样的异常。再次感谢,希望这有效。
  • 这似乎已经解决了问题,随着更新版本和您的建议的实施,我不再看到此崩溃。现在将赏金奖励给您,并尝试了解这解决问题的原因。
【解决方案2】:

我无法使用您提供的代码重现您的问题,因为我没有您上面列出的任何设备。我的第一个想法是它可能与您初始化片段的方式有关。请在下面查看我修改后的nFragmentAdapter 课程。我已更改 getItem() 以不同的方式初始化片段:

nFragmentAdapter:

class nFragmentAdapter extends FragmentStatePagerAdapter {

        public nFragmentAdapter(FragmentManager fm) {
            super(fm);
        }

        @Override        
        public int getItemPosition(Object object) {
            return POSITION_NONE;
        }

        @Override
        public Fragment getItem(int position) {

            Fragment f = null;
            Bundle args = new Bundle();
            args.putString("content", TabsStyled.CONTENT[position
                    % TabsStyled.CONTENT.length]);
            switch (position) {
            case 0:
                f = Fragment.instantiate(TabsStyled.this,
                        ReviewFragment.class.getName(), args);
                break;
            case 1:
                f = Fragment.instantiate(TabsStyled.this,
                        MyListFragment.class.getName(), args);
                break;
            case 2:
                f = Fragment.instantiate(TabsStyled.this,
                        SmartFragment.class.getName(), args);
                break;
            case 3:
                f = Fragment.instantiate(TabsStyled.this,
                        MyListFragment.class.getName(), args);
                break;
            case 4:
                f = Fragment.instantiate(TabsStyled.this,
                        f.getClass.getName(), args);
                break;
            case 5:
                f = Fragment.instantiate(TabsStyled.this,
                        f.getClass.getName(), args);
                break;
            case 6:
                f = Fragment.instantiate(TabsStyled.this,
                        f.getClass.getName(), args);
                break;
            }
            return f;
        }

        @Override
        public int getCount() {
            return TabsStyled.CONTENT.length;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return TabsStyled.CONTENT[position % TabsStyled.CONTENT.length].toUpperCase();
        }
    }

以及对应的MyListFragment:

public class MyListFragment extends ListFragment implements LoaderManager.LoaderCallbacks<Cursor> {

        myCursorAdapter mAdapter;
        String orderBy = DBAdapter.activityDateTime + " DESC";

        static final String[] activityQuery = new String[] { DBAdapter.activityRowID, DBAdapter.activityReason, DBAdapter.activityDateTime };

        String content;

    public void refreshView() {
        getLoaderManager().restartLoader(0, null, this);
    }

    @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
                Bundle args = getArguments();
                if(args != null)
                    content = args.getString("content");
                View logv = inflater.inflate(R.layout.listroot, null);
                return logv;           
        }      

        @Override
    public void onActivityCreated(Bundle savedInstanceState) {
                  super.onActivityCreated(savedInstanceState);
                mAdapter = new myCursorAdapter(getActivity(), null);
                setListAdapter(mAdapter);              
                getLoaderManager().initLoader(0, null, this);
        }

        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
        }

        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {
                return new CursorLoader(getActivity().getApplicationContext(), myListProvider.activity_URI, activityQuery, null, null, orderBy);
        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
                mAdapter.swapCursor(data);             
        }

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
                mAdapter.swapCursor(null);     
        }

    public class myCursorAdapter extends CursorAdapter {

        private final LayoutInflater mInflater;

        public myCursorAdapter(Context context, Cursor c) {
                super(context, c, 0);
                mInflater = LayoutInflater.from(context);
        }

        @Override
        public void bindView(View view, Context context, Cursor cursor) {

            TextView logleft = (TextView) view.findViewById(R.id.logleft);
                        logleft.setText(cursor.getString(cursor.getColumnIndex(DBAdapter.activityDateTime)));

            TextView logcenter = (TextView) view.findViewById(R.id.logcenter);
            logcenter.setText(cursor.getString(cursor.getColumnIndex(DBAdapter.activityReason)));

        }

        @Override
        public View newView(Context context, Cursor cursor, ViewGroup parent) {
                View view = null;
                view = mInflater.inflate(R.layout.vlist, parent, false);
            return view;
        }
    }

}

如果这有什么改变,请及时通知我。

【讨论】:

  • 谢谢,非常感谢您的回答和时间。我看到您从使用静态构造函数 newInstance 转变为使用实例化的方式(我仍在阅读有关差异的内容)。我还看到你是如何移动捆绑代码的。我还看到你建议与 SeanPONeil 相同的方法来膨胀视图,将 null 用于容器。我目前在生产中有他的建议,还没有看到抛出异常,但仍然需要一天左右的时间。哦,是的,我也无法在 4 种不同的设备或模拟器上重现此问题。
【解决方案3】:

试试这个。

<ListView android:id="@+id/android:list"
    android:choiceMode="singleChoice" 
    android:paddingTop="4dp" 
    android:paddingBottom="4dp" 
    android:layout_width="fill_parent" 
    android:layout_height="0dip" 
    android:layout_weight="45" />

我已将android:id="@android:id/list" 替换为android:id="@+id/android:list"

【讨论】:

  • 好吧,当我使用您的代码时,它不会在我的测试设备或模拟器上崩溃。但是,当我在没有 + 的情况下使用 id/list 时,它并没有崩溃。有没有解释为什么 + 不会产生崩溃但不使用 + 呢?我已经阅读了太多关于同一问题的相互矛盾的帖子,我不确定该使用哪个。谢谢
  • 阅读这个quetion的答案
  • 谢谢,我之前确实通读过那个,我想我和以前一样困惑。我读过的所有内容都说使用@android:id/list,考虑到这两个都在我的测试设备上工作,我不相信这是正确的答案。再次感谢。
  • 这是最让我困惑的评论! “'+' 表示如果符号尚不存在则创建符号。在引用 android: 符号时不需要它(也不应该使用它),因为平台已经为您定义了这些符号,而您无论如何都无法在该命名空间中创建自己的命名空间。”
  • 抱歉,您的回答没有解决问题。即使使用 android:id="@+id/android:list" 我也会遇到同样的异常。
【解决方案4】:

不要在 ListView 中使用 android:id="@+id/list" 。您可以使用另外一个选项创建 id,即 android:id="@id/android:list" 或者您可以使用 android:id="@+id/list1"。

【讨论】:

  • 感谢您的回答,但这基本上只是重复已经说过的话,并且根据其他两个人的说法也是不正确的。我也从未使用过“@+id/list”。我相信您在第二部分不正确,在我的情况下应该使用“@+id/list1”或@id/android:list。在此处查看文档 developer.android.com/reference/android/app/ListFragment.html 作为 SeanPONeil 对 Bishan 的回答发表的评论。
  • 我在上面提到了在 ListView 中创建 id 的代码行。我没有将它与您的代码行进行比较。如果你按照我的代码行实现,你会得到想要的结果..
  • 再次感谢您的回答和评论,但这与我的问题内容没有任何意义。
【解决方案5】:

感谢您的帖子。当我刚开始使用 Android 编程时,我在这方面有点挣扎。将其更改为 android:id="@android:id/list" 对我有用。

虽然我刚刚编写了一个示例程序,其中只有一个 ListView 显示在 ListFramgment 中,但最终在阅读了这篇文章后我可以让它工作。现在我要尝试看看是否可以在列表中显示一些自定义对象(比如在一行中包含一个文本和图标),而不是显示字符串数组。

非常感谢!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多