34

I'm having some trouble setting up my custom header in my list.

I'm creating a ListFragment with a custom adapter. I have the list working fine, but I'm trying to figure out where in the lifecycle of a Fragment to attach the header.

I know the header has to be added before you set your adapter.

I tried adding my header in onActivityCreated, but that gets called every time my Fragment comes back from the backstack, and since I also set my adapter in onActivityCreated, it fails.

I tried adding it in onCreate, but the view hierarchy isn't available at that stage of the lifecycle.

I tried adding it in onCreateView, but I couldn't cast the view returned from inflate to a ListView. So I couldn't add my header to a vanilla View.

Any thoughts?

Programmer Bruce
  • 64,977
  • 7
  • 99
  • 97
brockoli
  • 4,516
  • 7
  • 38
  • 45

7 Answers7

34

I don't know if you have solved your problem but here is a solution that worked for me:

Do not call ListFragment.setListAdapter() in your ListFragment.onCreate(). Make sure you have a field variable that can hold the header view, maybe like:

View mheaderView;

Then in your ListFragment.onCreateView(), inflate the header View and assign it to your variable like so:

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;

Finally, in your ListFragment.onActivityCreated() you can now call ListFragment.getListView().addHeaderView(). Basically something like so:

super.onActivityCreated(savedInstanceState);
if (mheaderView != null)  this.getListView().addHeaderView(headerView);
// Don't forget to now call setListAdapter()
this.setListAdapter(listAdapter);
Fernando Carvalhosa
  • 1,098
  • 1
  • 15
  • 23
ugo
  • 2,705
  • 2
  • 30
  • 34
  • 8
    This doesn't work for me - I get a crash on screen flip, when it complains about assigning a header view to a list after I've called setListAdapter. – Eric Mill Nov 14 '11 at 19:29
  • I am also facing the same problem. in these two methods of my fragment – Naveen Chauhan Jun 11 '12 at 07:09
  • `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 Jun 11 '12 at 07:14
  • `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 Jun 11 '12 at 07:15
  • What about inflating the header, adding and setListAdapter in onViewCreated? – dvd Aug 04 '12 at 23:19
  • 2
    For anyone having problems like Konklone and Naveen Chauhan: calling setListAdapter(null) in your onDestroyView should do the trick. – Rad Haring Jun 27 '14 at 13:03
32

This solution works with screen flipping:

In onActivityCreated():

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

And in onDestroyView()

setListAdapter(null);
delformo
  • 1,001
  • 10
  • 7
  • 2
    Thanks this help my. And I think that this is bug in ListFragment is should handle this itself. – ATom Feb 08 '12 at 14:22
  • thanks, its important to point out to set the ListAdapter to null in *onDestroyView* – martyglaubitz Oct 30 '12 at 11:38
  • I had same issues and resolved after adding 'setListAdapter(null);' to onDestroyView() – mutable2112 Jun 25 '13 at 16:44
  • after setListAdapter(null) in onDestroyView ... when I return to my fragment it only show the empty view however I set a new adapter to my listview !! .. any help ? – mnagy Feb 12 '14 at 07:37
18

My solution:

public void onActivityCreated(Bundle savedInstanceState) {
    setListAdapter(null);
    getListView().addHeaderView(mHeader);
    setListAdapter(mAdapter);
}
Fernando Carvalhosa
  • 1,098
  • 1
  • 15
  • 23
Jakob Eriksson
  • 18,597
  • 1
  • 25
  • 34
2

This is my solution for handling footer/header in list view. I use it in retained fragment. Adapter is initialized in renderView() method. This method can be called how many times you need (e.g. for refresh data in view) and footer/header works fine. I tested this code on 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);
}
petrnohejl
  • 7,581
  • 3
  • 51
  • 63
2

As short solution that worked for me:

@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);
}
0xPixelfrost
  • 10,244
  • 5
  • 39
  • 58
0

I am currently using the following solution in my class extending ListFragment:

1) You, in your class onActivityCreated check if your adapter (which is a class variable) is null, then instantiate it. Then, inflate the footer, for example like this:

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

You do only have to do this once! The footerView and the adapter only has to be created once. I create both of these in my onActivityCreated

Now to the "hard part", set your fragment in your onCreate to like this:

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

I like to do it in the onCreate because it's not relevant for the activity. Now with the setRetainInstance(true) your fragment will not be recreated after the activity is destroyed, an event such as a screen orientation.

Now after those rows add the footer like this:

getListView().addFooterView(footerView);

And then connect the adapter to the list:

setListAdapter(adapter);

This should be done every time the activity dies, do this in onActivityCreated.

And one of the other important things you should generally think about when it's coming to fragments is that you don't create the fragment every time the activity's onCreate is called.

For example do this (if your NOT using the supportpackage):

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

This will only create the fragment once, if the tag is unique for that fragment of course.

Johan S
  • 3,531
  • 6
  • 35
  • 63
0

I had some problems with header layout height, so I followed this solution:

@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 is a instance of View and DiarioAdapter is a custom ArrayAdapter.

UPDATE 1

If you have problems with duplicate listfragment, just change FragmentTransaction ADD for REPLACE.

Community
  • 1
  • 1
Beto Caldas
  • 2,198
  • 2
  • 23
  • 23