Лучшее место для добавленияHeaderView в ListFragment

У меня возникли проблемы с настройкой собственного заголовка в моем списке.

Я создаю ListFragment с помощью специального адаптера. У меня список работает нормально, но я пытаюсь выяснить, где в жизненном цикле фрагмента прикрепить заголовок.

Я знаю, что заголовок должен быть добавлен до того, как вы установите адаптер.

Я попытался добавить свой заголовок в onActivityCreated, но он вызывается каждый раз, когда мой фрагмент возвращается из стека, и, поскольку я также устанавливаю свой адаптер в onActivityCreated, он терпит неудачу.

Я попытался добавить его в onCreate, но иерархия представлений недоступна на этом этапе жизненного цикла.

Я попытался добавить его в onCreateView, но не смог преобразовать представление, возвращаемое из inflate, в ListView. Поэтому я не мог добавить свой заголовок в ванильное представление.

Есть предположения?


person brockoli    schedule 18.04.2011    source источник


Ответы (7)


Я не знаю, решили ли вы свою проблему, но вот решение, которое сработало для меня:

Не звоните ListFragment.setListAdapter() в свой ListFragment.onCreate(). Убедитесь, что у вас есть переменная поля, которая может содержать представление заголовка, например:

View mheaderView;

Затем в вашем ListFragment.onCreateView() раздуйте представление заголовка и назначьте его своей переменной следующим образом:

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);
person ugo    schedule 01.06.2011
comment
Это не работает для меня - я получаю сбой при переворачивании экрана, когда он жалуется на назначение представления заголовка списку после того, как я вызвал setListAdapter. - person Eric Mill; 14.11.2011
comment
Я тоже сталкиваюсь с той же проблемой. в этих двух методах моего фрагмента - person Naveen Chauhan; 11.06.2012
comment
Как насчет раздувания заголовка, добавления и setListAdapter в onViewCreated? - person dvd; 05.08.2012
comment
Для тех, у кого есть проблемы, такие как Konklone и Naveen Chauhan: вызов setListAdapter(null) в вашем onDestroyView должен помочь. - person Rad Haring; 27.06.2014

Это решение работает с переворотом экрана:

В onActivityCreated():

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

И в onDestroyView()

setListAdapter(null);
person delformo    schedule 15.11.2011
comment
Спасибо, это поможет мне. И я думаю, что это ошибка в ListFragment, которая должна справиться с этим сама. - person ATom; 08.02.2012
comment
спасибо, важно указать, чтобы установить ListAdapter в null в onDestroyView - person martyglaubitz; 30.10.2012
comment
У меня были те же проблемы, и они были решены после добавления «setListAdapter (null);» в onDestroyView() - person mutable2112; 25.06.2013
comment
после setListAdapter(null) в onDestroyView... когда я возвращаюсь к своему фрагменту, он показывает только пустой вид, однако я установил новый адаптер для своего списка!! .. любая помощь ? - person mnagy; 12.02.2014

Мое решение:

public void onActivityCreated(Bundle savedInstanceState) {
    setListAdapter(null);
    getListView().addHeaderView(mHeader);
    setListAdapter(mAdapter);
}
person Jakob Eriksson    schedule 06.04.2012
comment
Это кажется самым чистым решением. - person louielouie; 22.05.2012
comment
onActivityCreated() кажется лучшим местом для установки onScrollListener(). - person user1549672; 05.04.2015

Это мое решение для обработки нижнего колонтитула/заголовка в виде списка. Я использую его в сохраненном фрагменте. Адаптер инициализируется в методе 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);
}
person petrnohejl    schedule 20.10.2012

Как короткое решение, которое сработало для меня:

@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);
}
person LuckyMalaka    schedule 02.03.2013

В настоящее время я использую следующее решение в своем классе, расширяющем ListFragment:

1) Вы в своем классе onActivityCreated проверяете, является ли ваш адаптер (который является переменной класса) нулевым, а затем создаете его экземпляр. Затем раздуйте нижний колонтитул, например, так:

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

Вам нужно сделать это только один раз! Нижний колонтитул и адаптер необходимо создать только один раз. Я создаю оба из них в моем onActivityCreated

Теперь к «сложной части» установите свой фрагмент в onCreate следующим образом:

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

Мне нравится делать это в onCreate, потому что это не относится к делу. Теперь с setRetainInstance(true) ваш фрагмент не будет воссоздан после уничтожения активности, такого события, как ориентация экрана.

Теперь после этих строк добавьте нижний колонтитул следующим образом:

getListView().addFooterView(footerView);

А затем подключите адаптер к списку:

setListAdapter(adapter);

Это нужно делать каждый раз, когда активность умирает, сделайте это в onActivityCreated.

И одна из других важных вещей, о которых вы обычно должны думать, когда дело доходит до фрагментов, заключается в том, что вы не создаете фрагмент каждый раз, когда вызывается onCreate активности.

Например, сделайте это (если вы НЕ используете пакет поддержки):

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

Это создаст фрагмент только один раз, если, конечно, тег уникален для этого фрагмента.

person Johan S    schedule 15.02.2013
comment
Вы не должны быть вынуждены установитьRetainInstance(true). Это агностик к вопросу. - person saiyancoder; 19.03.2014

У меня были некоторые проблемы с высотой макета заголовка, поэтому я следовал этому решение:

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

Заголовок — это экземпляр View, а DiarioAdapter — пользовательский ArrayAdapter.

ОБНОВЛЕНИЕ 1

Если у вас есть проблемы с повторяющимся фрагментом списка, просто измените ADD FragmentTransaction на REPLACE.

person Beto Caldas    schedule 17.04.2015