【问题标题】:Can a ListView contain FragmentsListView 可以包含片段吗
【发布时间】:2012-07-06 23:28:04
【问题描述】:

如上,ListView 的 ELEMENTS 可以是 Fragments。我知道您可以将 TextView XML 分配给 ListView 以更改其外观,但您可以将 Fragments 添加到 ListView 中。

例如:我有一个片段。所述片段的 XML 包含一个 ImageView、几个大样式的 TextView 和一个小样式的 TextView。 Fragment 类代码接收一个 Bundle,然后根据内容相应地填充 TextViews 和 ImageView。片段 XML 和片段代码都可以正常工作 (我可以很好地显示一个单独的片段)。我有一个 FragmentActivity,我想在其中显示上述片段列表。这是我用来尝试在 FragmentActivity 的视图中填充 ListView 的代码:

    ArrayList<Fragment> fragList = new ArrayList<Fragment>();
    Fragment fragment = Fragment.instantiate(this, TileItem.class.getName());
    Bundle bundle = new Bundle();
    bundle.putInt("key", 0);
    fragment.setArguments(bundle);
    fragList.add(fragment);

    ArrayAdapter<Fragment> adapter = new ArrayAdapter<Fragment>(this, R.layout.tile_item, fragList);
    listItems.setAdapter(adapter);

这是我对此的思考方式。我制作了一个 ArrayList of Fragments 来保存我所有的实例化视图。然后我创建一个 Fragment,创建一个 Bundle,将数据添加到 Bundle(以便 Fragment 可以正确地将数据编组到它的视图中),将 Bundle 添加到 Fragment,最后将 Fragment 添加到 ArrayList。之后,我制作了一个 ArrayAdapter,添加了我想要使用的元素布局,以及我制作的片段列表;然后将 ListView 设置为从我的适配器中读取。

运行此代码的任何人都可能会得到 NPE @ 实例化 ArrayAdapter。是什么赋予了?这甚至可能吗?在我一直为此绞尽脑汁之前,有人能告诉我我是不是在浪费时间吗?有没有更好的办法?我一直在考虑使用 ScrollView,但是 ListView 的很多功能都需要重新实现,我讨厌-讨厌-讨厌在没有必要时重新发明轮子。

感谢所有阅读的人,特别感谢您在决定离开时提出的想法。我已经尝试过寻找一个既定的答案,但我似乎找到的只是关于在片段内部使用 ListView 的问题/网页;不使用片段作为 ListView 的元素

编辑:我接受了以下建议并开始进行更多调查。从事情出现的方式来看,我应该能够使用一个自定义适配器来膨胀片段,而不是仅仅从 XML 构建(因为缺乏更好的方法来描述这个过程)但是,我当前的实现在尝试设置时抛出了一个 NPE适配器。

这是我的自定义适配器代码(为简洁起见):

public class AdapterItem extends ArrayAdapter<Fragment> {

Context c;
List<Fragment> f;

public AdapterItem(Context c, List<Fragment> f) {
    super(c, R.layout.tile_item, f);
    this.c = c;
    this.f = f;
}

@Override
public View getView(int pos, View v, ViewGroup vg) {
    LayoutInflater i = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    return i.inflate(R.layout.tile_item, vg, false);
}

}

这是我的实现方式:

ArrayList<Fragment> fragList = new ArrayList<Fragment>();
    Fragment fragment = Fragment.instantiate(this, TileItem.class.getName());
    Bundle bundle = new Bundle();
    bundle.putInt("key", 0);
    fragment.setArguments(bundle);
    fragList.add(fragment);

    AdapterItem adapter = new AdapterItem(this, fragList);
    adapter.add(fragment);
    listItems.setAdapter(adapter);

所以已经有几天了,我很确定这个帖子已经被埋没了。但是,我想我会添加最后一个更新,以防有人想尝试这个并且谷歌搜索将他们带到这里。所以在我的实现中,当给 ListView 适配器时,我得到了一个 NPE。不需要火箭外科医生就可以确定它肯定是适配器而不是 ListView 抛出错误。对于我的生活,我无法弄清楚为什么......

无论如何,我想我有一些想法。首先,来个小故事:不久前,我试图在 FragmentDialog 中创建 FragmentTransactions。每次我尝试这样做时,我都会得到一个 NPE。最终,通过大量研究,我发现原因与 Fragments 的实例化方式有关。调用 Fragment 时,它需要来自其父级的上下文。由于 Dialog 的父级是启动它的 Activity,因此 Dialog 本身不符合必要的条件。我相信,在尝试将片段添加到 ListView 时,情况也是如此。由于 ListView 不符合实例化 Fragment 的协议,因此它会抛出 NPE,因此让我悬而未决并回到约定。 D@mn...我真的希望我能够做到这一点。使用片段而不是简单的 XML 会使组织/搜索列表变得更加容易。哦,好吧……如果有人想知道的话,这可能是不可能的。

【问题讨论】:

  • 为什么你甚至想要片段成为“ListView的元素”?开发人员多年来一直在创建ListViews,但他们的元素并未成为片段。您希望通过将它们变成碎片获得什么?
  • 好吧,我希望能够在列表中显示的不仅仅是一个简单的 TextView(实际上我将能够显示一个图像和几行额外的文本) .此外,我将能够反复重用来自不同活动的片段代码,而无需重复代码。我还看到了比简单的 TextView 更动态的项目滚动列表(如 Google Play 中的列表,它们比 TextView 更复杂)。这是如何实现的?使用 Fragment 会不会产生同样的效果(更简单)?
  • 您可以通过使用自定义适配器和从 XML 为您的行膨胀的视图来做到这一点。
  • 我喜欢这个替代方案,但是它将我用于实例化这些视图的所有代码都放在了托管 ListView 的活动中。如果这些视图在片段中出现错误,那么所有代码​​都在片段中。您是否有可以处理此问题的自定义适配器示例?我可以在它自己的类中扩展适配器,从而将所有用于实例化这些视图的代码保留在一个位置吗?
  • "使用 Fragment 不是可以实现相同的效果(更简单)吗?" -- 好吧,考虑到我所知道的没有人在ListView 中使用片段作为元素,这肯定不太容易。在没有碎片的情况下做到这一点是微不足道的。现在,您当然可以通过检查PagerAdapterFragmentPagerAdapter 之间的相似关系来制作一个由片段支持的ListAdapter。但是,您正在开辟新天地,尚不清楚是否需要这样做。

标签: android listview android-arrayadapter fragment


【解决方案1】:

我想说这是不可能的,因为将片段放入 ListView 意味着片段可以在多个容器中成倍增加。当您使用 FragmentManager 创建片段时,会使用标识符对其进行标记,从而可以轻松地重新加载和重新排列方向和其他配置更改。它还鼓励跨多个设备配置使用。

片段实际上是 Activity 的子集。您是否将 Activity 作为列表的一部分?绝对不是(应该是答案!)!!!

此外,在片段移入和移出视图时连续附加()和分离()片段并不是很有用(单元格被回收)。这些都是 ListView 不应该处理的昂贵操作。列表应该快速滚动。

从 cmets 上的对话中,我可以看出您希望通过在 Activity 中很好地分离视图设置代码和适配器来实现漂亮的代码。这样做:

  1. 覆盖View 类并在那里进行自定义绘图和设置。
  2. 创建一个新类,在其中提供所需的上下文和数据集,以便返回列表需要显示的视图 - 这是我通常所做的。
  3. 有一个 Utils 类在别处构建您的视频(愚蠢)。

只是不要在列表中使用片段。不是他们的目标用例。 HTH。

【讨论】:

    【解决方案2】:

    事实证明,您可以创建一个ListView,其中listView 中的每个项目都是Fragment。诀窍是将 Fragment 包装在 FrameLayout 中。

    2014 年 9 月 16 日更新

    尽管可能创建一个包含FragmentsListView,但这看起来并不是一个好主意。这似乎绝对是 Android 世界中的一个极端案例,并且有龙。对于像下面示例中的一个简单片段,一切都很好,但如果你有一个复杂的项目,其中有很多事情发生,那么这可能不是要走的路。我的新方法是将所有与 GUI 相关的代码提取到扩展 FrameLayoutView 中,然后将其插入到 ListView 中——这样效果更好,更符合 Android 的预期使用方式.如果您在代码的其他部分需要 Fragment 的功能,您也可以简单地在那里使用这个新的 View

    回到原来的答案...

    如果您想尝试一下,我已经在我的 AnDevCon 14 Fragments example app 中添加了一个新的 ManyFragments 示例。基本上它归结为BaseAdapter,在我的示例中如下所示:

        BaseAdapter adapter = new BaseAdapter() {
            @Override public int getCount() { return 10000; }
            @Override public Object getItem(int i) { return new Integer(i); }
            @Override public long getItemId(int i) { return i; }
    
            @Override
            public View getView(int i, View view, ViewGroup viewGroup) {
    
                if (view!=null){
                    ManyListItemFragment fragment = (ManyListItemFragment) view.getTag();
                    fragment.setCount(i);
                } else {
                    FrameLayout layout = new FrameLayout(getActivity());
                    layout.setLayoutParams(frameLayoutParams);
                    int id = generateViewId();
                    layout.setId(id);
                    ManyListItemFragment fragment = new ManyListItemFragment();
                    fragment.setCount(i);
                    getChildFragmentManager()
                            .beginTransaction()
                            .replace(id,fragment)
                            .commit();
                    view = layout;
                    view.setTag(fragment);
                }
    
                return view;
            }
        };
    

    如果你好奇,这里是 generateViewId():

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
    public static int generateViewId() {
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN_MR1) {
            for (;;) {
                final int result = sNextGeneratedId.get();
                // aapt-generated IDs have the high byte nonzero; clamp to the range under that.
                int newValue = result + 1;
                if (newValue > 0x00FFFFFF) newValue = 1; // Roll over to 1, not 0.
                if (sNextGeneratedId.compareAndSet(result, newValue)) {
                    return result;
                }
            }
        } else {
            return View.generateViewId();
        }
    }
    private static final AtomicInteger sNextGeneratedId = new AtomicInteger(1);
    

    【讨论】:

    • 你能指导我如何为每个列表项创建不同的布局
    【解决方案3】:

    您不需要使用片段。

    编写一个自定义 ViewAdapter 并让它膨胀一个更复杂的布局(或者如果你需要真正花哨,可能会增加几个更复杂的布局),然后根据需要填充布局的字段。

    [旁白:对于在 cmets 中回答的人——如果您实际上是在回答问题,请使用答案而不是 cmets!如果只是因为你通过这种方式获得更多的声望点!]

    【讨论】:

    • PS:如果您尝试在另一个布局中重用一个布局的部分内容,请查看 元素
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-04-01
    • 2011-05-26
    • 2021-02-19
    • 2015-05-15
    • 2019-03-12
    • 2021-02-08
    • 1970-01-01
    相关资源
    最近更新 更多