在ListFragment中添加HeaderView的最佳位置

34

我在设置自定义列表头部时遇到了一些问题。

我正在创建一个带有自定义适配器的ListFragment。我的列表运行良好,但我正在尝试确定Fragment的生命周期何时可以附加头部。

我知道头部必须在设置适配器之前添加。

我尝试在onActivityCreated中添加我的头部,但每次我的Fragment从后台返回时都会调用该方法,因为我也在onActivityCreated中设置了适配器,所以它失败了。

我尝试在onCreate中添加它,但是视图层次结构在生命周期的这个阶段不可用。

我尝试在onCreateView中添加它,但我无法将从inflate返回的视图转换为ListView。 因此,我无法将我的标题添加到原始的View中。

有什么想法吗?

7个回答

34
我不知道你是否解决了问题,但这里有一个对我有效的解决方案:
不要在ListFragment.onCreate()中调用ListFragment.setListAdapter()。确保你有一个可以保存头部视图的字段变量,可能像这样:
View mheaderView;

然后在你的ListFragment.onCreateView()中,通过inflate方法来填充头部视图并将其分配给变量,代码如下:

View list_root = inflater.inflate(R.layout.fragment_list, null);
// Get the list header - to be added later in the lifecycle
// during onActivityCreated()
mheaderView = inflater.inflate(R.layout.list_header, null);
return list_root;

最后,在您的ListFragment.onActivityCreated()中,您现在可以调用ListFragment.getListView().addHeaderView()。基本上就像这样:

super.onActivityCreated(savedInstanceState);
if (mheaderView != null)  this.getListView().addHeaderView(headerView);
// Don't forget to now call setListAdapter()
this.setListAdapter(listAdapter);

8
这对我不起作用 - 当屏幕翻转时,我会遇到崩溃,它抱怨在调用setListAdapter之后将标题视图分配给列表。 - Eric Mill
我也在我的片段的这两种方法中遇到了同样的问题。 - Naveen Chauhan
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle bundle){ View v = inflater.inflate(R.layout.fragment_pager_list, container,false); View tv = v.findViewById(R.id.text); ((TextView)tv).setText("Contacts"); return v; } - Naveen Chauhan
`public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); this.setListAdapter(null); mAdapter = new SimpleCursorAdapter(getActivity(), android.R.layout.simple_list_item_1, null, new String[] {ContactsContract.Contacts.DISPLAY_NAME}, new int[] { android.R.id.text1}, 0); this.setListAdapter(mAdapter); getLoaderManager().initLoader(0, null, this); }` - Naveen Chauhan
在onViewCreated中,尝试扩展标题、添加和设置setListAdapter怎么样? - dvd
2
对于像Konklone和Naveen Chauhan这样遇到问题的人:在onDestroyView中调用setListAdapter(null)应该可以解决问题。 - Rad Haring

32

这个解决方案适用于屏幕翻转:

在 onActivityCreated() 方法中:

getListView().addHeaderView(mHeaderView);
if (mMyAdapter == null) {
    mMyAdapter = new MyAdapter(getActivity(), null);
}
setListAdapter(mMyAdapter);

在onDestroyView()方法中:

setListAdapter(null);

2
谢谢,这很有帮助。我认为这是ListFragment中的一个错误,它应该自己处理这个问题。 - ATom
谢谢,重要的是要指出在 onDestroyView 中将 ListAdapter 设置为 null。 - martyglaubitz
我之前也遇到了同样的问题,后来在 onDestroyView() 中添加了 'setListAdapter(null);' 后得以解决。 - mutable2112
在 onDestroyView 中执行 setListAdapter(null) 后,当我返回到我的 fragment 时,它只显示空视图,尽管我已经为我的 listview 设置了新的适配器!!有什么帮助吗? - mnagy

18

我的解决方案:

public void onActivityCreated(Bundle savedInstanceState) {
    setListAdapter(null);
    getListView().addHeaderView(mHeader);
    setListAdapter(mAdapter);
}

1
这似乎是最简洁的解决方案。 - louielouie
onActivityCreated() 似乎是设置 onScrollListener() 的最佳位置。 - user1549672

2

这是我处理列表视图页脚/页眉的解决方案。我在保留的片段中使用它。适配器在renderView()方法中初始化。您可以根据需要多次调用此方法(例如,刷新视图中的数据),页脚/页眉将正常工作。我已在Android 2、3、4上测试了此代码。

@Override
public void onActivityCreated(Bundle savedInstanceState)
{
    super.onActivityCreated(savedInstanceState);

    ...

    renderView();
}


@Override
public void onDestroyView()
{
    super.onDestroyView();

    // free adapter
    setListAdapter(null);
}


private void renderView()
{
    // reference
    ListView listView = getListView();

    // adapter
    if(getListAdapter()==null)
    {
        // init adapter
        mAdapter = new MyAdapter(...);
    }
    else
    {
        // refill adapter
        // this method assign array list object to adapter and call notifyDataSetChanged()
        mAdapter.refill(...);
    }

    // add footer
    setListAdapter(null);
    if(listView.getFooterViewsCount()==0)
    {
        mFooterView = getActivity().getLayoutInflater().inflate(R.layout.my_footer, null);
        listView.addFooterView(mFooterView);
    }

    // set adapter
    setListAdapter(mAdapter);
}

2

以下是对我有效的简短解决方案:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);

    View headerView = getActivity().getLayoutInflater().inflate(R.layout.header, null);
    getListView().addHeaderView(headerView);

    ArrayAdapter<XY> mAdapter = createAdapter(); // create here your own adapter
    setListAdapter(mAdapter);
}

@Override
public void onDestroyView() {
    super.onDestroyView();
    setListAdapter(null);
}

0

我在头部布局高度方面遇到了一些问题,所以我按照这个解决方案进行了操作:

@Override
public void onActivityCreated(Bundle savedInstanceState) {
    super.onActivityCreated(savedInstanceState);
    setListAdapter(null);//avoid problems with orientation changes
    header = getActivity().getLayoutInflater().inflate(R.layout.row_diario_header,getListView(),false);
    getListView().addHeaderView(header);
    ArrayList<Notificacao> nots = new ArrayList<>();

    nots.add(new Notificacao("16/04/2015", "Test", "Erro"));
    setListAdapter(new DiarioAdapter(getActivity(), R.layout.listview_diario, nots));
}

Header是View的一个实例,DiarioAdapter是自定义的ArrayAdapter。

更新1

如果您遇到重复listfragment的问题,只需将FragmentTransaction ADD更改为REPLACE。


0

我目前在我的类中扩展了ListFragment,并使用以下解决方案:

1)在你的类的onActivityCreated方法中检查你的适配器(它是一个类变量)是否为空,如果是,则实例化它。然后,像这样填充页脚:

View footerView = View.inflate
    (getActivity(), R.layout.list_footer_loader_view, null);

你只需要做一次!footerView和adapter只需要创建一次。我在我的onActivityCreated中创建了它们两个。

现在是“难点”了,将你的fragment在onCreate中设置为如下:

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
}

我喜欢在onCreate中完成它,因为它与活动无关。现在使用setRetainInstance(true)后,当活动被销毁时,例如屏幕方向发生变化,您的片段将不会重新创建。

现在,在这些行之后添加页脚,如下所示:

getListView().addFooterView(footerView);

然后将适配器连接到列表:

setListAdapter(adapter);

每次活动结束时都应该完成此操作,可在onActivityCreated中执行。

当涉及到片段时,另一个重要的事情是你不应该在每次活动的onCreate被调用时都创建片段。

例如,如果您没有使用SupportPackage,则可以执行以下操作:

MyFragment myFragment  = (MyFragment)
    getFragmentManager().findFragmentByTag(tag);
if (myFragment == null) {
    myFragment = MyFragment.newInstance();
    getFragmentManager().beginTransaction().
            add(myFragment, tag).commit();
}

如果该标签对应的片段是唯一的,那么这将只创建一次该片段。


1
你不应该被强制设置setRetainInstance(true)。那与问题无关。 - saiyancoder

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