RecyclerView header and footer
Solution 1
in your adapter add this class:
private class VIEW_TYPES {
public static final int Header = 1;
public static final int Normal = 2;
public static final int Footer = 3;
}
then Override the following method like this:
@Override
public int getItemViewType(int position) {
if(items.get(position).isHeader)
return VIEW_TYPES.Header;
else if(items.get(position).isFooter)
return VIEW_TYPES.Footer;
else
return VIEW_TYPES.Normal;
}
Now in the onCreateViewHolder method inflate your layout based on the view type::
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
View rowView;
switch (i) {
case VIEW_TYPES.Normal:
rowView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.normal, viewGroup, false);
break;
case VIEW_TYPES.Header:
rowView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.header, viewGroup, false);
break;
case VIEW_TYPES.Footer:
rowView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.footer, viewGroup, false);
break;
default:
rowView = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.normal, viewGroup, false);
break;
}
return new ViewHolder (rowView);
}
Now in the onBindViewHolder method bind your layout based on the view holder:
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
int viewType = getItemViewType(position);
switch(viewType) {
case VIEW_TYPES.Header: // handle row header
break;
case VIEW_TYPES.Footer: // handle row footer
break;
case VIEW_TYPES.Normal: // handle row item
break;
}
}
Hope this can help.
Solution 2
This is very easy with ItemDecorations and without modifying any other code:
recyclerView.addItemDecoration(new HeaderDecoration(this,
recyclerView, R.layout.test_header));
Reserve some space for drawing, inflate the layout you want drawn and draw it in the reserved space.
The code for the Decoration:
public class HeaderDecoration extends RecyclerView.ItemDecoration {
private View mLayout;
public HeaderDecoration(final Context context, RecyclerView parent, @LayoutRes int resId) {
// inflate and measure the layout
mLayout = LayoutInflater.from(context).inflate(resId, parent, false);
mLayout.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
}
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// layout basically just gets drawn on the reserved space on top of the first view
mLayout.layout(parent.getLeft(), 0, parent.getRight(), mLayout.getMeasuredHeight());
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
if (parent.getChildAdapterPosition(view) == 0) {
c.save();
final int height = mLayout.getMeasuredHeight();
final int top = view.getTop() - height;
c.translate(0, top);
mLayout.draw(c);
c.restore();
break;
}
}
}
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
if (parent.getChildAdapterPosition(view) == 0) {
outRect.set(0, mLayout.getMeasuredHeight(), 0, 0);
} else {
outRect.setEmpty();
}
}
}
Solution 3
If all you need is a blank header and footer, here is a very simple way to achieve this (written in Kotlin):
class HeaderFooterDecoration(private val headerHeight: Int, private val footerHeight: Int) : RecyclerView.ItemDecoration() {
override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
val adapter = parent.adapter ?: return
when (parent.getChildAdapterPosition(view)) {
0 -> outRect.top = headerHeight
adapter.itemCount - 1 -> outRect.bottom = footerHeight
else -> outRect.set(0, 0, 0, 0)
}
}
}
Call it this way:
recyclerView.addItemDecoration(HeaderFooterDecoration(headerHeightPx, footerHeightPx))
Solution 4
You can use this GitHub] library to add a Header or Footer to your RecyclerView
in the simplest way possible.
You need to add the HFRecyclerView library in your project or you can also grab it from Gradle:
compile 'com.mikhaellopez:hfrecyclerview:1.0.0'
This library is based on a work at @hister
This is a result in image:
Solution 5
recyclerview:1.2.0 introduces ConcatAdapter
ConcatAdapter is a new RecyclerView Adapter that can combine multiple adapters linearly.
How to use ConcatAdapter?
add following dependency into your build.gradle
file
androidx.recyclerview:recyclerview:1.2.0-alpha04
Then, if you have multiple adapters, you can easily merge them using
MyAdapter adapter1 = ...;
AnotherAdapter adapter2 = ...;
ConcatAdapter merged = new ConcatAdapter(adapter1, adapter2);
recyclerView.setAdapter(merged);
For the sample above, ConcatAdapter will present items from adapter1 followed by adapter2.
Here you can find the complete documentation.
Find complete working sample here.
Read this article for more info.
Here you can find the source code.
Sandra
Updated on July 08, 2022Comments
-
Sandra almost 2 years
Maybe this question has been asked before, but I could not seem to find a precise answer or solution. I started using the RecyclerView, and I implemented it using the LinearLayoutManager. Now I want to add custom header and footer items, that differ from the rest of the items in my RecyclerView. The header and footer should not be sticky, I want them to scroll with the rest of the items. Can somebody point out some example how to do this or just share ideas. I will appreciate it very much. Thx
-
AndroidDev about 9 yearsI could implement the header fine but when I added the footer its getting inserted after the first item of the Recycler view though I specified the Footer attribute as "layout_alignParentBottom=true". Any idea what might be the reason?
-
Bronx about 9 yearsHi, header and footer are item too, so you have to add the header first (checking that it is in the first position) then add your items and finally in the last position add the footer
-
Ronak Joshi almost 9 yearsThanks for this solution but can you tell me where is the methods name, "isHeader" and "isFooter" ? @Bronx
-
Bronx almost 9 yearsHi @RonakJoshi isHeader and isFooter are not methods, they are variables you have in the items of your list. For example you have a class named MyItem and the constructor is public MyItem(Object myObject, boolean isHeader, boolean isFooter)
-
Ronak Joshi almost 9 yearsYes,Got it. Thanks @Bronx +1 for your solution.
-
chin87 over 8 yearshow can you add click handling in item decorator? I have to add a button as header, its can be seen with your code but I am unable to add on click as findViewById return null
-
David Medenjak over 8 years@chin87 ...since
mLayout
is a view - just add it to the view? Either add a getter or modify the constructor. (And it depends where you call findViewById...Since the view is neither attached to the recyclerView nor to the fragment) -
Martin Erlic over 8 yearsI keep getting 'cannot resolve symbol .isHeader'. Also 'method call expected' for 'return new ViewHolder (rowView);' The solution doesn't work for me.
-
Bronx over 8 yearsHi @bluemunch, check my previous answer to RonakJoshi, isHeader is a parameter you must have in your item class. This was an example, you can use whatever logic you want to choose which item is the header or the footer.
-
android developer over 8 yearsHow do you use it to add a footer? Will it also work when LinearLayoutManager is horizontal?
-
David Medenjak over 8 years@androiddeveloper you would have to modify or adapt the code, this sample is more proof of concept. to use it as footer you would check for last item rather than
position == 0
and draw beneath instead of above, for using it horizontally, you would also need to adapt it, modifying the drawing for left/right instead of above -
android developer over 8 years@DavidMedenjak That's too bad. Do you know perhaps of a repo that has them all? Are there any disadvantages of using your solution, other than the need of extra code for each header&footer for horizontal&vertical
-
David Medenjak over 8 years@androiddeveloper The only real downside is, that it is just a decoration. You won't be able to add onClick to it. I don't know of any full implementations, but modifying the code for all 4 cases is not too hard. You can find the repo that I play around with including more samples at github.com/bleeding182/recyclerviewItemDecorations
-
android developer over 8 years@DavidMedenjak Why can't I add onClick to it? because it has no real view, as it only draws stuff? Is it a lot of changes to the current code? Maybe it's better to just add parameters to the decorator, instead of creating new, similar classes.
-
android developer over 8 years@DavidMedenjak Also, suppose I want to remove the header at some point, will it have a nice animation like of normal items of the RecyclerView (when they have an id) ? Or will it just disappear right away?
-
David Medenjak over 8 years@androiddeveloper you don't get any animation for free. you can use the childs alpha and translation to animate with the child, I wrote an article on that here: bleeding182.blogspot.com/2015/11/…
-
android developer over 8 years@DavidMedenjak The article is about normal items. You mean the same holds for decorators too?
-
David Medenjak over 8 yearsLet us continue this discussion in chat.
-
Dima about 8 yearsVery helpful? but I faced of with another problem. I've added progress bar in header but it animates only when I move list or call notifydatasetchange()
-
David Medenjak about 8 years@Dima Yes. This is not a classic View, it's just some drawing on top. If you change it you will have to find a way to invalidate it yourself and update the recyclerview. The main use case of the presented answer is to show some image or text as a header
-
X09 about 7 years@Bronx Adding the link to that your other answer will be nice though.
-
X09 about 7 years@Bronx or add the full code because I am still having the same issue santafebound is having.
-
Andy over 6 yearsI've attempted your answer but I can't get it to work. How simple is your
R.layout.test_header
? The layout I am attempting to add is a bit more complex. LinearLayout and stuff. Is this not possible? -
David Medenjak over 6 years@Andy it really depends. Try using a fixed height/width and see if that helps, maybe it can't be properly measured/layouted. But I'd try to keep it simple
-
Leonid Ustenko over 6 yearsI use a TextView as a footer layout and it makes no new lines, just draws single line textview which goes out of bounds
-
lenhuy2106 almost 6 yearsi didn't find a way to create a footer yet.
-
lenhuy2106 almost 6 yearsIf someones looking for the footer version: gist.github.com/dreiklangdev/4b092eade09feb26bdf1c090fd2e5643
-
lenhuy2106 almost 6 years@LeoDroidcoder did you try wrap it in a e.g. frameLayout?
-
Kathir over 5 yearsOkay, your approach is straightforward. But, isn't it a waste of memory to have the view as
View.GONE
for most of the time? -
Raktim Bhattacharya over 5 years@Kathir...it is processed only when its visible in the screen area...other time it is treated and kept in background as usual array of records to be shown..
-
Kathir over 5 years
-
AMAN77 almost 5 yearsNot very often that I find the best solution way down here. Short Concise, readable, gets the job done. Full Marks Sean :D
-
Amar Yadav almost 4 yearsA very good example with source code to create RecyclerView with header and footer loopwiki.com/ui-ux-design/…
-
Anurag Bhalekar over 3 yearsYour code came out really handy, but I tried modifying it for footer by changing parent.getChildAdapterPosition(view) == 0 to parent.getChildAdapterPosition(view) == parent.getAdapter().getItemCount() But it didn;t work. Well, it does work for any other position in between, but not the last one, could you suggest any changes?
-
Umberto Covino over 3 yearsThanks a lot @Trunks! In my case I have added a simple header containing only a TextView in this way:
recyclerView.addItemDecoration(new HeaderItemDecoration(aTextView), 0)
. -
meekash55 almost 3 yearsseems promising. But need a full code. as Such I'm using Kotlin and didn't find items.get(position).isHeader (isHeader) property here. As well I am looking to put "Load more" button at the footer. So on click event also need to handle somewhere. Please help!
-
Driss Bounouar over 2 years@AnuragBhalekar it's parent.getChildAdapterPosition(view) == (parent.getAdapter().getItemCount()-1)