Skip to content Skip to sidebar Skip to footer

Android Listview Custom Section Header

How can i implement custom section or header of ListView like Instagram app in android. http://prsarahevans.com/wp-content/uploads/2011/06/photo.PNG When scroll up the bar that hav

Solution 1:

I was able to solve this issue by using scroll listener on the listview. (tested on 2.1)

Lets say for each list row I have a layout like the one below. There is a content part and a header part. It doesn't matter what view type you use for header or content.

<?xml version="1.0" encoding="utf-8"?><RelativeLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical"android:background="#FFFFFF"><ImageViewandroid:id="@+id/content"android:layout_width="fill_parent"android:layout_height="300dp"android:scaleType="centerCrop"android:src="@drawable/pic"android:background="#aaaaff"android:layout_marginTop="40dp"/><TextViewandroid:id="@+id/header"android:layout_width="fill_parent"android:layout_height="40dp"android:padding="12dp"android:text="Deneme Row"android:textColor="#000000"android:background="#99ffffff"/></RelativeLayout>

The test layout for the activity is as in the following:

<?xml version="1.0" encoding="utf-8"?><LinearLayoutxmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="fill_parent"android:layout_height="fill_parent"android:orientation="vertical" ><ListViewandroid:id="@+id/list"android:layout_width="fill_parent"android:layout_height="fill_parent" ></ListView></LinearLayout>

Finally the code for the activity is given below. Here I had to have an adapter which uses ViewHolder to store the header view and also a variable to keep track of the change in the scroll for each successive scroll events (previousTop). This is because of the fact that offsetTopAndBottom() changes offset of the view related to the previous location of it.

publicclassTestActivityextendsActivityimplementsAbsListView.OnScrollListener{

    ListView list; 
    @OverridepublicvoidonCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);
        list = (ListView) findViewById(R.id.list);
        list.setAdapter(newAdapter(this));        
        list.setOnScrollListener(this); 
    }

    @OverridepublicvoidonScroll(AbsListView view, int firstVisibleItem,
            int visibleItemCount, int totalItemCount) {     
        //the listview has only few children (of course according to the height of each child) who are visiblefor(int i=0; i < list.getChildCount(); i++){
            Viewchild= list.getChildAt(i);
            ViewHolderholder= (ViewHolder) child.getTag();

            //if the view is the first item at the top we will do some processingif(i == 0){             
                booleanisAtBottom= child.getHeight() <= holder.header.getBottom();
                intoffset= holder.previousTop - child.getTop();
                if(!(isAtBottom && offset > 0)){                    
                    holder.previousTop = child.getTop();
                    holder.header.offsetTopAndBottom(offset);                   
                    holder.header.invalidate();
                }
            } //if the view is not the first item it "may" need some correction because of view re-useelseif (holder.header.getTop() != 0) {
                intoffset= -1 * holder.header.getTop(); 
                holder.header.offsetTopAndBottom(offset);
                holder.previousTop = 0;
                holder.header.invalidate();
            }
        }
    }

    @OverridepublicvoidonScrollStateChanged(AbsListView view, int scrollState) {}

    privatestaticclassAdapterextendsArrayAdapter<String> {
        publicAdapter(Context context) {
            super(context, R.layout.row, R.id.header);
            for(int i=0; i < 50; i++){
                add(Integer.toString(i));
            }
        }

        @Overridepublic View getView(int position, View convertView, ViewGroup parent) {
            if(convertView == null){
                convertView = LayoutInflater.from(getContext()).inflate(R.layout.row, parent, false);
                ViewHolderholder=newViewHolder();
                holder.header = (TextView) convertView.findViewById(R.id.header);
                convertView.setTag(holder);             
            }
            ViewHolderholder= (ViewHolder) convertView.getTag();
            holder.header.setText(getItem(position));
            return convertView;
        }
    }

    privatestaticclassViewHolder {
        TextView header;
        intpreviousTop=0;
    }
}

Solution 2:

I've improved Siyamed's question a little bit so that the header is not gone when the view is redrawn for example if a bitmap in the listview item is changed.

Instead of using coordinates relative to the last position I use coordinates relative to the top of the view and use padding instead of offsets.

@OverridepublicvoidonScroll(AbsListView absListView, int firstVisibleItem, int visibleItemCount, int totalItemCount) {

    for(int i=0; i < list.getChildCount(); i++){
        Viewchild= list.getChildAt(i);
        ViewHolderholder= (ViewHolder) child.getTag();

        if(i == 0){
            try {
                booleanisAtBottom= child.getHeight() <= holder.movingHeader.getBottom();
                if(!(isAtBottom)){
                    if (child.getTop() >= 0){}
                    elseif (child.getHeight() - movingHeader.getHeight() - 1 > -child.getTop())
                    {
                        holder.movingHeader.setPadding(0, -child.getTop(), 0, 0);
                        holder.movingHeader.invalidate();
                    }
                    else {
                        holder.movingHeader.setPadding(0, child.getHeight() - movingHeader.getHeight(), 0, 0);
                        holder.movingHeader.invalidate();
                    }
                }
            }
            catch (Exception e)
            {
                e.printStackTrace();
            }
        }
        elseif (holder.movingHeader.getPaddingTop() != 0)
        {
            holder.movingHeader.setPadding(0, 0, 0, 0);
            holder.movingHeader.invalidate();
        }
    }
}

Solution 3:

If you are using list view then have 2 views inside your list item. One is header and other is actually item.

Hide header view initially and as scroll state changes or user touches your list item change the visibility of header.

Solution 4:

After your setcontentview do something like the below code.

ListViewlist= getListView();
    Viewfooter= getLayoutInflater().inflate(R.layout.footerlayout, list, false);
    Viewheader= getLayoutInflater().inflate(R.layout.headerlayout, list, false);
    list.addHeaderView(header);
    list.addFooterView(footer); 

Solution 5:

I know this question is a little old, but managed to find the StickyListHeaders library, which does a pretty good job of abstracting the creation of the section headers.

https://github.com/emilsjolander/StickyListHeaders

Post a Comment for "Android Listview Custom Section Header"