[UNMAINTAINED] Sticky Headers decorator for Android's RecyclerView

Overview

This project is no longer being maintained

sticky-headers-recyclerview

This decorator allows you to easily create section headers for RecyclerViews using a LinearLayoutManager in either vertical or horizontal orientation.

Credit to Emil Sjölander for creating StickyListHeaders, a library that many of us relied on for sticky headers in our listviews.

Here is a quick video of it in action (click to see the full video):

animated gif demo

animated gif demo

Download

Current version: Maven Central

compile 'com.timehop.stickyheadersrecyclerview:library:[latest.version.number]@aar'

Usage

There are three main classes, StickyRecyclerHeadersAdapter, StickyRecyclerHeadersDecoration, and StickyRecyclerHeadersTouchListener.

StickyRecyclerHeadersAdapter has a very similar interface to the RecyclerView.Adapter, and it is recommended that you make your RecyclerView.Adapter implement StickyRecyclerHeadersAdapter.

There interface looks like this:

public interface StickyRecyclerHeadersAdapter<VH extends RecyclerView.ViewHolder> {
  public long getHeaderId(int position);

  public VH onCreateHeaderViewHolder(ViewGroup parent);

  public void onBindHeaderViewHolder(VH holder, int position);

  public int getItemCount();
}

The second class, StickyRecyclerHeadersDecoration, is where most of the magic happens, and does not require any configuration on your end. Here's an example from onCreate() in an activity:

mRecyclerView = (RecyclerView) findViewById(R.id.recyclerview);
mAdapter = new MyStickyRecyclerHeadersAdapter();
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(new LinearLayoutManager(context));
mRecyclerView.addItemDecoration(new StickyRecyclerHeadersDecoration(mAdapter));

StickyRecyclerHeadersTouchListener allows you to listen for clicks on header views. Simply create an instance of StickyRecyclerHeadersTouchListener, set the OnHeaderClickListener, and add the StickyRecyclerHeadersTouchListener as a touch listener to your RecyclerView.

StickyRecyclerHeadersTouchListener touchListener =
    new StickyRecyclerHeadersTouchListener(recyclerView, headersDecor);
touchListener.setOnHeaderClickListener(
    new StickyRecyclerHeadersTouchListener.OnHeaderClickListener() {
      @Override
      public void onHeaderClick(View header, int position, long headerId) {
        Toast.makeText(MainActivity.this, "Header position: " + position + ", id: " + headerId,
            Toast.LENGTH_SHORT).show();
      }
    });
mRecyclerView.addOnItemTouchListener(touchListener);

The StickyHeaders aren't aware of your adapter so if you must notify them when your data set changes.

    mAdapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
      @Override public void onChanged() {
        headersDecor.invalidateHeaders();
      }
    });

If the Recyclerview's layout manager implements getExtraLayoutSpace (to preload more content then is visible for performance reasons), you must implement ItemVisibilityAdapter and pass an instance as a second argument to StickyRecyclerHeadersDecoration's constructor.

    @Override
    public boolean isPositionVisible(final int position) {
        return layoutManager.findFirstVisibleItemPosition() <= position
            && layoutManager.findLastVisibleItemPosition() >= position;
    }

Item animators don't play nicely with RecyclerView decorations, so your mileage with that may vary.

Compatibility

API 11+

Known Issues

  • The header views aren't recycled at this time. Contributions are most welcome.

  • I haven't tested this with ItemAnimators yet.

  • The header views are drawn to a canvas, and are not actually a part of the view hierarchy. As such, they can't have touch states, and you may run into issues if you try to load images into them asynchronously.

Version History

0.4.3 (12/24/2015) - Change minSDK to 11, fix issue with header bounds caching

0.4.2 (8/21/2015) - Add support for reverse ReverseLayout in LinearLayoutManager by AntonPukhonin

0.4.1 (6/24/2015) - Fix "dancing headers" by DarkJaguar91

0.4.0 (4/16/2015) - Code reorganization by danoz73, fixes for different sized headers, performance improvements

0.3.6 (1/30/2015) - Prevent header clicks from passing on the touch event

0.3.5 (12/12/2014) - Add StickyRecyclerHeadersDecoration.invalidateHeaders() method

0.3.4 (12/3/2014) - Fix issues with rendering of header views with header ID = 0

0.3.3 (11/13/2014) - Fixes for padding, support views without headers

0.3.2 (11/1/2014) - Bug fixes for list items with margins and deleting items

0.2 (10/3/2014) - Add StickyRecyclerHeadersTouchListener

0.1 (10/2/2014) - Initial Release

Comments
  • Structural Changes + Fix for padding support on RecyclerView

    Structural Changes + Fix for padding support on RecyclerView

    I moved around some of the logic to do various things (rendering, position calculation, view creation/caching, etc). After the restructuring, I included a fix for supporting padding on the RecyclerView itself. You should just be able to run the demo and see the padding/margin.

    If you don't want to structure this code in this manner, I can just leave this in my own fork no problem.

    :gift_heart:

    opened by danoz73 15
  • Bug when delete item

    Bug when delete item

    I use your lib to try recycler view with sticky header, anything is work good, but when i delete item, sometimes it became mess and when i want multiple delete from the bottom of item yes that bug appear again.

    opened by fjr619 15
  • Headers not updating when calling notifyDataSetChanged on adapter

    Headers not updating when calling notifyDataSetChanged on adapter

    I use a cursorloader that updates the adapter data set by calling notifyDataSetChanged. All the cells that aren't sticky get updated, but not the sticky headers. In fact, onBindHeaderViewHolder doesn't get called at all.

    opened by meilers 14
  • Error loading in Fragment

    Error loading in Fragment

    I have used this code successfully from my Main Activity in which I am loading data from my server onto the cards used in recycerview. Everything is working fine but now when I tried to shift the exact same code onto a fragment, it keeps giving me an error. I have changed all the getActivity to view.getContext() and made other appropriate changes in the import. Could someone help me out?

     Process: varun.brandlabs.com.zeko, PID: 13859
        android.view.InflateException: Binary XML file line #8: Error inflating class <unknown>
                at android.view.LayoutInflater.createView(LayoutInflater.java:633)
                at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:743)
                at android.view.LayoutInflater.rInflate(LayoutInflater.java:806)
                at android.view.LayoutInflater.inflate(LayoutInflater.java:504)
                at android.view.LayoutInflater.inflate(LayoutInflater.java:414)
                at varun.brandlabs.com.zeko.NewsFeed.MyStickyAdapter.onCreateHeaderViewHolder(MyStickyAdapter.java:53)
                at varun.brandlabs.com.zeko.NewsFeed.MyStickyAdapter.onCreateHeaderViewHolder(MyStickyAdapter.java:22)
                at com.timehop.stickyheadersrecyclerview.caching.HeaderViewCache.getHeader(HeaderViewCache.java:34)
                at com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersDecoration.getHeaderView(StickyRecyclerHeadersDecoration.java:138)
                at com.timehop.stickyheadersrecyclerview.StickyRecyclerHeadersDecoration.getItemOffsets(StickyRecyclerHeadersDecoration.java:64)
                at android.support.v7.widget.RecyclerView.getItemDecorInsetsForChild(RecyclerView.java:3583)
                at android.support.v7.widget.RecyclerView$LayoutManager.measureChildWithMargins(RecyclerView.java:6623)
                at android.support.v7.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1385)
                at android.support.v7.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1322)
                at android.support.v7.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:556)
                at android.support.v7.widget.RecyclerView.dispatchLayout(RecyclerView.java:2673)
                at android.support.v7.widget.RecyclerView.onLayout(RecyclerView.java:2971)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.support.v4.widget.SwipeRefreshLayout.onLayout(SwipeRefreshLayout.java:562)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.widget.RelativeLayout.onLayout(RelativeLayout.java:1076)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.support.v4.view.ViewPager.onLayout(ViewPager.java:1626)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1705)
                at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1559)
                at android.widget.LinearLayout.onLayout(LinearLayout.java:1468)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
                at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1705)
                at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1559)
                at android.widget.LinearLayout.onLayout(LinearLayout.java:1468)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
                at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.widget.LinearLayout.setChildFrame(LinearLayout.java:1705)
                at android.widget.LinearLayout.layoutVertical(LinearLayout.java:1559)
                at android.widget.LinearLayout.onLayout(LinearLayout.java:1468)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.widget.FrameLayout.layoutChildren(FrameLayout.java:573)
                at android.widget.FrameLayout.onLayout(FrameLayout.java:508)
                at android.view.View.layout(View.java:15631)
                at android.view.ViewGroup.layout(ViewGroup.java:4966)
                at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:2101)
                at android.view.ViewRootImpl.performTraversals(View
    
    
    public class MyStickyAdapter extends RecyclerView.Adapter<MyStickyAdapter.ViewHolder>
            implements StickyRecyclerHeadersAdapter<RecyclerView.ViewHolder> {
        Context mContext;
    
        MyStickyAdapter(Context mContext){this.mContext=mContext;}
    
        public static class ViewHolder extends RecyclerView.ViewHolder{
            public TextView HeadView, Row;
            public CardView cardView; ImageView image;
    
            ViewHolder(final View view){
                super(view);
    
            }
        }
    
        @Override
        public long getHeaderId(int position) {
                return position;
        }
    
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
            View v = LayoutInflater.from(viewGroup.getContext())
                    .inflate(R.layout.sample_feed_card, viewGroup, false);
            ViewHolder viewHolder = new ViewHolder(v);
            return viewHolder;
        }
    
        @Override
        public ViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
            View v = LayoutInflater.from(parent.getContext())
                    .inflate(R.layout.sample_feed_header, parent, false);
            ViewHolder viewHolder=new ViewHolder(v);
            return viewHolder;
        }
    
        @Override
        public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder, int position) {
    
        }
    
        @Override
        public void onBindViewHolder (ViewHolder holder, int i) {
    
        }
    
        @Override
        public int getItemCount() {
           return 4;
        }
        @Override
        public void onAttachedToRecyclerView(RecyclerView recyclerView) {
            super.onAttachedToRecyclerView(recyclerView);
        }
    
    }
    

    As of now the code above is using default values iv placed so the childs all look the same. 2) I keep loading data asynchronously, so i need to mAdapter.add(newData) and then mAdapter.notifyItemRangeChanged. Does that work efficiently with this?

    opened by varunagarwal315 13
  • RecyclerView jumps when adding items on the top

    RecyclerView jumps when adding items on the top

    I have an autoloading (from top) list of items. When user scroll to top of the list - new items are added to the top.

    I need to preserve the scroll position. The desired behaviour is to keep all the items as they are and add more items to top.

    NOW when adding items to top - the list jumps a little up - thus placing the first item to the very top (not under header as it was).

    Could you please fix this asap.

    P.S. Thank you for the great library, works great.

    opened by ealymbaev 9
  • Get Position of HeaderView

    Get Position of HeaderView

    Hello Everyone,

    I want to open popup menu for Header Item. I am able to get HeaderView Touch event but not it's actual position on screen. I debug the code but findHeaderPositionUnder(int x, int y) has not updated Rect() of HeaderView. Is it possible to get?

    opened by shivanic1986 8
  • Does hasStableIds=true in RecyclerArrayAdapter require a better getItemId() implementation?

    Does hasStableIds=true in RecyclerArrayAdapter require a better getItemId() implementation?

    RecyclerArrayAdapter.java you set setHasStableIds(true) but then declare

    public long getItemId(int position) {
        return position;
      }
    

    Isn't that a contradiction?

    The method getItemId() is documented as follows

    Return the stable ID for the item at position. If #hasStableIds() would return false this method should return #NO_ID. The default implementation of this method returns #NO_ID @param position Adapter position to query @return the stable ID of the item at position

    and in hasStableIds() we have

    Returns true if this adapter publishes a unique long value that can act as a key for the item at a given position in the data set. If that item is relocated in the data set, the ID returned for that item should be the same. @return true if this adapter's items have stable IDs

    I think that as long we don't know a any structure of the elements, it is better to fall back to the default implementation returning NO_ID.

    opened by webmaster128 8
  • Introduce a non sticky header switch

    Introduce a non sticky header switch

    This introduces the ability to change the header to a non sticky mode without breaking the api.

    To be used like this:

        final StickyRecyclerHeadersDecoration headersDecor = new StickyRecyclerHeadersDecoration(mAdapter);
        headersDecor.setHeadersSticky(false);
        recyclerView.addItemDecoration(headersDecor);
    

    I would kinda like to introduce a builder pattern and make a creator for StickyRecyclerHeadersDecoration.java. This would however break the api.

    recyclerView.addItemDecoration(StickyRecyclerHeadersDecoration.with(mAdapter)
                                                                  .setHeadersSticky(false);
    
    opened by markini 8
  • Dancing headers

    Dancing headers

    If you have items that update in the recycler view, the header item for the set that updates tends to dance around. Basically the header items moves to just above the item that has been updated and then moves back up to the correct position.

    opened by DarkJaguar91 8
  • Extra spacing added every time the adapter is updated

    Extra spacing added every time the adapter is updated

    Every time the adapter is refreshed in this code, the padding between headers and sections is increased. This is not the intended result

    Here is the code I use to update the dataset. (ultimately notify data set is changed is called, the headerDecors are updated and the listview is reattached to the headers and the new adapter)

                                             if(mPerf == null)
                                                    mPerf = new PerformanceAdapter(list, context);
                                                else{
                                                    mPerf.updateList(list);
                                                }
                                                recList.removeItemDecoration(headersDecor);
    
                                                if(headersDecor!=null){
                                                    headersDecor.invalidateHeaders();
                                                }
                                                headersDecor = new StickyRecyclerHeadersDecoration(mPerf);
                                                recList.addItemDecoration(headersDecor);
                                                recList.setAdapter(mPerf);
    
    opened by ericlw 6
  • Don't propagate touch to items behind a header (update StickyRecyclerHeadersTouchListener)

    Don't propagate touch to items behind a header (update StickyRecyclerHeadersTouchListener)

    When a sticky header is touched, a regular item behind it may receive the ACTION_DOWN event, and display "pressed" state. This commit fixes such undesired behavior by returning true for onInterceptTouchEvent() if it was DOWN inside the borders of a sticky header.

    opened by alexcohn 5
  • How to adjust position of headers in horizontal layout? Currently it's covering up content!

    How to adjust position of headers in horizontal layout? Currently it's covering up content!

    Hi. Currently my headers are covering the content of the layout I'm using. How can i reposition them to a bit higher? What part of code do i need to edit? image

    opened by FahadSaleem 2
  • Only last header is visible

    Only last header is visible

    The issue reproduces periodically without any rules. onCreateHeaderViewHolder and onBindHeaderViewHolder gets called, but headers are not visible My code looks like

    override fun getHeaderId(position: Int): Long {
        return clients[position].companyName.toUpperCase().get(0).toLong()
    }
    
    override fun onCreateHeaderViewHolder(parent: ViewGroup): HeaderHolder {
        val view = LayoutInflater.from(parent.context).inflate(R.layout.item_client_list_header, parent, false)
         return HeaderHolder(view)
    }
    
    override fun onBindHeaderViewHolder(holder: HeaderHolder, position: Int) {
        val letter = clients[position].companyName.get(0).toUpperCase().toString()
        holder.bind(letter)
    }
    

    and xml:

    <?xml version="1.0" encoding="utf-8"?>
    <TextView xmlns:tools="http://schemas.android.com/tools"
        xmlns:android="http://schemas.android.com/apk/res/android"
        style="@style/SansMediumBig"
        tools:text="A"
        android:layout_width="match_parent"
        android:padding="@dimen/padding_small"
        android:background="@color/transparent_grey_3"/>
    

    But no matter what font or any other style (even without any style) I'm using - result is the same. Note that I'm using this recycle view https://github.com/myinnos/AlphabetIndex-Fast-Scroll-RecyclerView Help me please :(

    opened by graisVictory 3
  • Showing duplicates data item inside recyclerview

    Showing duplicates data item inside recyclerview

    • OnBindViewHolder is getting called only for initial 5 items while I have 50 items in the list.
    • Because of this, those 5 items are getting repeated to accomplish 50 counts and showing duplicate data.
    opened by mtariquekhan 0
  • Problem of header click event when the data set changed

    Problem of header click event when the data set changed

    Hello, project owner, I recently used your library in my project. It runs perfectly when there is no click event.

    But when I added the click event to header view, after the data set changed, the item may perform unexpected action -- the same as clicking the header.

    I guess this may occurred due to the cached views, so is there any method to solve this problem ? If this is one known issue, I'd be glad to make contribution to this project.

    opened by Shouheng88 2
Owner
timehop
timehop
An android library for section headers that stick to the top

StickyListHeaders StickyListHeaders is an Android library that makes it easy to integrate section headers in your ListView. These section headers stic

Emil Sjölander 5.5k Jan 2, 2023
ListView with blur/parallax/sticky capabilities

BlurStickyHeaderListView What is BlurStickyHeaderListView? It is a custom ListView with a header that displays pictures from an URL. It then adds a ni

null 129 Oct 26, 2022
Kotlin way of building RecyclerView Adapter 🧩. You do not have to write RecyclerView Adapters again and again and suffer from handling of different view types. Kiel will help you.

Kiel Kiel is a RecyclerView.Adapter with a minimalistic and convenient Kotlin DSL which provides utility on top of Android's normal RecyclerView.Adapt

ibrahim yilmaz 370 Jan 2, 2023
Pumped up RecyclerView

##Description This is an attempt to make RecyclerView easier to use. Features built in: ProgressBar while adapter hasn't been set EmptyView if adapter

Anton Malinskiy 2.6k Jan 5, 2023
A very easy-to-use and non-intrusive implement of Swipe to dismiss for RecyclerView.

RecyclerViewSwipeDismiss A very easy-to-use and non-intrusive implement of Swipe to dismiss for RecyclerView. Preview How to use Add these lines to yo

xcodebuild 431 Nov 23, 2022
An adapter which could be used to achieve a parallax effect on RecyclerView.

android-parallax-recycleview Integration Step 1. Add the JitPack repository to your build file repositories { maven { url "https://jitpack

Pedro Oliveira 1.6k Nov 17, 2022
An Android Animation library which easily add itemanimator to RecyclerView items.

RecyclerView Animators RecyclerView Animators is an Android library that allows developers to easily create RecyclerView with animations. Please feel

Daichi Furiya 11.2k Jan 5, 2023
Android library providing simple way to control divider items (ItemDecoration) of RecyclerView

RecyclerView-FlexibleDivider Android library providing simple way to control divider items of RecyclerView Release Note [Release Note] (https://github

Yoshihito Ikeda 2.4k Dec 18, 2022
A Fast Scroller for the RecyclerView world!

RecyclerViewFastScroller The RecyclerViewFastScroller is a widget that can be added to a layout and connected to a RecyclerView for fast scrolling. Th

Daniel Smith 1.1k Dec 19, 2022
Android library defining adapter classes of RecyclerView to manage multiple view types

RecyclerView-MultipleViewTypeAdapter RecyclerView adapter classes for managing multiple view types Release Note [Release Note] (https://github.com/yqr

Yoshihito Ikeda 414 Nov 21, 2022
*** WARNING: This library is no longer maintained *** An easy way to add a simple 'swipe-and-do-something' behavior to your `RecyclerView` items. Just like in Gmail or Inbox apps.

SwipeToAction An easy way to add a simple 'swipe-and-do-something' behavior to your RecyclerView items. Just like in Gmail or Inbox apps. Integration

Victor Calvello 223 Nov 16, 2022
RecyclerView extension library which provides advanced features. (ex. Google's Inbox app like swiping, Play Music app like drag and drop sorting)

Advanced RecyclerView This RecyclerView extension library provides Google's Inbox app like swiping, Play Music app like drag-and-drop sorting and expa

Haruki Hasegawa 5.2k Dec 23, 2022
ItemDecoration for RecyclerView using LinearLayoutManager for Android

RecyclerItemDecoration RecyclerItemDecoration allows you to draw divider between items in recyclerview with multiple ViewType without considering item

magiepooh 328 Dec 27, 2022
[UNMAINTAINED] Sticky Headers decorator for Android's RecyclerView

This project is no longer being maintained sticky-headers-recyclerview This decorator allows you to easily create section headers for RecyclerViews us

timehop 3.7k Jan 8, 2023
. Android library that integrate sticky section headers in your RecyclerView

recyclerview-stickyheaders Recyclerview-stickyheaders is an Android library that makes it easy to integrate section headers in your RecyclerView. Thes

null 968 Nov 10, 2022
A layout manager for the RecyclerView with interchangeable linear, grid, and staggered displays of views, all with configurable section headers including the sticky variety as specified in the material design docs.

SuperSLiM This is the version 5 development branch. Project Plan Support me on Patreon Blog What is Version 5 Version 5 is the current development bra

Tonic Artos 2.1k Jan 2, 2023
A small android library for tagging views inside a ScrollView as "sticky" making them stick to the top of the scroll container until a new sticky view comes and takes it's place

StickyScrollViewItems StickyScrollViewItems is a ScrollView subclass that allowed you to mark items inside the ScrollView as sticky. The items marked

Emil Sjölander 1k Jan 7, 2023
Android ListView with sticky headers

DEPRECATED HeaderListView is deprecated. No new development will be taking place. Quickstart Import the HeaderListView module in your Android Studio p

Applidium 314 Nov 10, 2022
ItemDecorator - Custom item decorator for adding divider for only the first item of a RecyclerView

ItemDecorator Custom item decorator for adding divider for only the first item o

null 1 Apr 1, 2022
A simple app to showcase Androids Material Design and some of the cool new cool stuff in Android Lollipop. RecyclerView, CardView, ActionBarDrawerToggle, DrawerLayout, Animations, Android Compat Design, Toolbar

#Android-LollipopShowcase This is a simple showcase to show off Android's all new Material Design and some other cool new stuff which is (new) in Andr

Mike Penz 1.8k Nov 10, 2022