Tuesday, May 24, 2011

Pinned Header ListView as in the Contacts app

The standard Android Contacts application has this PinnedHeaderListView implementation. This renders a letter (A, B, C, ...) in a section header on your ListView for all the list items starting with that letter. What's nice is that this section header stays visible as long as there is a list item visible starting with that letter. And it's being pushed up when the last list item is becoming invisible while scrolling the list view.

A sample project with the pinned header list view can be found at http://code.google.com/p/android-playground. Another example project using this list view is Devoxx 2010 Schedule app.


Layout definitions

First we need to define the layout definitions. We are not using a default ListView, but the custom PinnedHeaderListView.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
    <view class="net.peterkuterna.android.apps.pinnedheader.PinnedHeaderListView"
        android:id="@android:id/list"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

The layout for the header is also specified in a separated layout. An instance of this view will be passed on to the PinnedHeaderListView when setting up the list view.

<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/header_text"
    android:layout_width="match_parent"
    android:layout_height="25dip"
    android:textStyle="bold"
    android:background="@color/pinned_header_background"
    android:textColor="@color/pinned_header_text"
    android:textSize="14sp"
    android:paddingLeft="6dip"
    android:gravity="center_vertical" />

Next, we also need to include this header view in the layout that defines the list item.

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/list_item"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">
    <include layout="@layout/list_item_header" />
    <include layout="@android:layout/simple_list_item_1" />
    <View android:id="@+id/list_divider"
        android:layout_width="match_parent"
        android:layout_height="1px"
        android:background="@android:drawable/divider_horizontal_dark" />
</LinearLayout>

Setting up the PinnedHeaderListView

To actually use this PinnedHeaderListView you only need to provide it a suitable ListAdapter, the header View and an OnScrollListener.

private void setupListView() {
    PinnedHeaderListView listView = (PinnedHeaderListView) findViewById(android.R.id.list);
    listView.setPinnedHeaderView(LayoutInflater.from(this).inflate(R.layout.list_item_header, listView, false));
    listView.setOnScrollListener(mAdapter);
    listView.setDividerHeight(0);
}

Implementing the ListAdapter

The ListAdapter that you will pass on to the PinnedHeaderListView needs to have the following interfaces implemented:
  • PinnedHeaderAdapter
  • SectionIndexer
  • OnScrollListener

The PinnedHeaderAdapter interface defines two methods that need to be implemented. The method getPinnedHeaderState needs to return the state of the pinned header in your list view for a certain position. This allows the list view to identify if the header is gone, is visible or if it needs to be pushed up. The second method configurePinnedHeader is being called when your list view is being layout and allows you to adapt (text, colors, ...) the pinned header view.

A SectionIndexer is being used to easily identify a section for a given position and a position for a given section in the list view. These methods are being used when binding the pinned header, when detecting the pinned header state and when configuring the pinned header.

As last a OnScrollListener callback is implemented which will be called when the list has been scrolled and which will call on its turn the configureHeaderView method of the PinnedHeaderListView.

10 comments:

  1. This comment has been removed by the author.

    ReplyDelete
  2. Thanks for your code!
    PinnedHeaderListView.java
    case PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP: {
    ..... if(firstView!=null){
    bottom = firstView.getBottom();
    itemHeight = firstView.getHeight();
    }
    }

    ReplyDelete
  3. I kinda wish you had a much simpler example for download.

    ReplyDelete
  4. Could u please upload the PinnedHeaderListView, when i click on your link, google says: this service has been shut down....

    thanks

    ReplyDelete
  5. PinnedHeaderListView.java here: https://github.com/android/platform_packages_apps_contacts/blob/master/src/com/android/contacts/widget/PinnedHeaderListView.java

    ReplyDelete
  6. Any one know how can I achieve this pinnded header scrolling on html/css?

    ReplyDelete
  7. Hi,
    You can use refer this link for custom listview.
    http://custom-listview-with-separate-headers.blogspot.in/

    ReplyDelete
  8. Hi

    Is it possible to use the pinned header listview with a SQLite database, custome cursoradapter and a content provider? If so could you kindly give a few pointers.

    ReplyDelete
  9. Hi

    Is it possible to use the pinned header listview with a SQLite database, custome cursoradapter and a content provider? If so could you kindly give a few pointers.

    ReplyDelete
  10. Anyone has the source?

    ReplyDelete