Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Sticky Header RecyclerView
package com.saber.customstickyheader;
import android.graphics.Color;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class SimpleRecyclerView extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements StickHeaderItemDecoration.StickyHeaderInterface {
private List<Data> mData;
public SimpleRecyclerView() {
mData = new ArrayList<>();
mData.add(new Data(1));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));`
mData.add(new Data(0));
mData.add(new Data(2));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(1));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(2));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
mData.add(new Data(0));
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case HeaderDataImpl.HEADER_TYPE_1:
return new SimpleRecyclerView.HeaderViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.header1_item_recycler, parent, false));
case HeaderDataImpl.HEADER_TYPE_2:
return new SimpleRecyclerView.Header2ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.header2_item_recycler, parent, false));
default:
return new SimpleRecyclerView.ViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_recycler, parent, false));
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if (holder instanceof ViewHolder) {
((ViewHolder) holder).bindData(position);
} else if (holder instanceof HeaderViewHolder){
((HeaderViewHolder) holder).bindData(position);
} else if (holder instanceof Header2ViewHolder){
((Header2ViewHolder) holder).bindData(position);
}
}
@Override
public int getItemViewType(int position) {
return mData.get(position).getViewType();
}
@Override
public int getItemCount() {
return mData.size();
}
@Override
public int getHeaderPositionForItem(int itemPosition) {
int headerPosition = 0;
do {
if (this.isHeader(itemPosition)) {
headerPosition = itemPosition;
break;
}
itemPosition -= 1;
} while (itemPosition >= 0);
return headerPosition;
}
@Override
public int getHeaderLayout(int headerPosition) {
if (mData.get(headerPosition).getViewType() == 1)
return R.layout.header1_item_recycler;
else {
return R.layout.header2_item_recycler;
}
}
@Override
public void bindHeaderData(View header, int headerPosition) {
}
@Override
public boolean isHeader(int itemPosition) {
if (mData.get(itemPosition).getViewType() == 1 || mData.get(itemPosition).getViewType() == 2)
return true;
else
return false;
}
class HeaderViewHolder extends RecyclerView.ViewHolder {
TextView tvHeader;
HeaderViewHolder(View itemView) {
super(itemView);
tvHeader = itemView.findViewById(R.id.tvHeader);
}
void bindData(int position) {
tvHeader.setText(String.valueOf(position / 5));
}
}
class Header2ViewHolder extends RecyclerView.ViewHolder {
TextView tvHeader;
Header2ViewHolder(View itemView) {
super(itemView);
tvHeader = itemView.findViewById(R.id.tvHeader);
}
void bindData(int position) {
tvHeader.setText(String.valueOf(position / 5));
}
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView tvRows;
ViewHolder(View itemView) {
super(itemView);
tvRows = itemView.findViewById(R.id.tvRows);
}
void bindData(int position) {
tvRows.setText("saber" + position);
((ViewGroup) tvRows.getParent()).setBackgroundColor(Color.parseColor("#ffffff"));
}
}
class Data {
int viewType;
public Data(int viewType) {
this.viewType = viewType;
}
public int getViewType() {
return viewType;
}
public void setViewType(int viewType) {
this.viewType = viewType;
}
}
}
package com.saber.customstickyheader;
import android.graphics.Canvas;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
public class StickHeaderItemDecoration extends RecyclerView.ItemDecoration {
private StickyHeaderInterface mListener;
private int mStickyHeaderHeight;
public StickHeaderItemDecoration(@NonNull StickyHeaderInterface listener) {
mListener = listener;
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
View topChild = parent.getChildAt(0);
if (topChild == null) {
return;
}
int topChildPosition = parent.getChildAdapterPosition(topChild);
if (topChildPosition == RecyclerView.NO_POSITION) {
return;
}
int headerPos = mListener.getHeaderPositionForItem(topChildPosition);
View currentHeader = getHeaderViewForItem(headerPos, parent);
fixLayoutSize(parent, currentHeader);
int contactPoint = currentHeader.getBottom();
View childInContact = getChildInContact(parent, contactPoint, headerPos);
if (childInContact != null && mListener.isHeader(parent.getChildAdapterPosition(childInContact))) {
moveHeader(c, currentHeader, childInContact);
return;
}
drawHeader(c, currentHeader);
}
private View getHeaderViewForItem(int headerPosition, RecyclerView parent) {
int layoutResId = mListener.getHeaderLayout(headerPosition);
View header = LayoutInflater.from(parent.getContext()).inflate(layoutResId, parent, false);
mListener.bindHeaderData(header, headerPosition);
return header;
}
private void drawHeader(Canvas c, View header) {
c.save();
c.translate(0, 0);
header.draw(c);
c.restore();
}
private void moveHeader(Canvas c, View currentHeader, View nextHeader) {
c.save();
c.translate(0, nextHeader.getTop() - currentHeader.getHeight());
currentHeader.draw(c);
c.restore();
}
private View getChildInContact(RecyclerView parent, int contactPoint, int currentHeaderPos) {
View childInContact = null;
for (int i = 0; i < parent.getChildCount(); i++) {
int heightTolerance = 0;
View child = parent.getChildAt(i);
//measure height tolerance with child if child is another header
if (currentHeaderPos != i) {
boolean isChildHeader = mListener.isHeader(parent.getChildAdapterPosition(child));
if (isChildHeader) {
heightTolerance = mStickyHeaderHeight - child.getHeight();
}
}
//add heightTolerance if child top be in display area
int childBottomPosition;
if (child.getTop() > 0) {
childBottomPosition = child.getBottom() + heightTolerance;
} else {
childBottomPosition = child.getBottom();
}
if (childBottomPosition > contactPoint) {
if (child.getTop() <= contactPoint) {
// This child overlaps the contactPoint
childInContact = child;
break;
}
}
}
return childInContact;
}
/**
* Properly measures and layouts the top sticky header.
* @param parent ViewGroup: RecyclerView in this case.
*/
private void fixLayoutSize(ViewGroup parent, View view) {
// Specs for parent (RecyclerView)
int widthSpec = View.MeasureSpec.makeMeasureSpec(parent.getWidth(), View.MeasureSpec.EXACTLY);
int heightSpec = View.MeasureSpec.makeMeasureSpec(parent.getHeight(), View.MeasureSpec.UNSPECIFIED);
// Specs for children (headers)
int childWidthSpec = ViewGroup.getChildMeasureSpec(widthSpec, parent.getPaddingLeft() + parent.getPaddingRight(), view.getLayoutParams().width);
int childHeightSpec = ViewGroup.getChildMeasureSpec(heightSpec, parent.getPaddingTop() + parent.getPaddingBottom(), view.getLayoutParams().height);
view.measure(childWidthSpec, childHeightSpec);
view.layout(0, 0, view.getMeasuredWidth(), mStickyHeaderHeight = view.getMeasuredHeight());
}
public interface StickyHeaderInterface {
/**
* This method gets called by {@link StickHeaderItemDecoration} to fetch the position of the header item in the adapter
* that is used for (represents) item at specified position.
* @param itemPosition int. Adapter's position of the item for which to do the search of the position of the header item.
* @return int. Position of the header item in the adapter.
*/
int getHeaderPositionForItem(int itemPosition);
/**
* This method gets called by {@link StickHeaderItemDecoration} to get layout resource id for the header item at specified adapter's position.
* @param headerPosition int. Position of the header item in the adapter.
* @return int. Layout resource id.
*/
int getHeaderLayout(int headerPosition);
/**
* This method gets called by {@link StickHeaderItemDecoration} to setup the header View.
* @param header View. Header to set the data on.
* @param headerPosition int. Position of the header item in the adapter.
*/
void bindHeaderData(View header, int headerPosition);
/**
* This method gets called by {@link StickHeaderItemDecoration} to verify whether the item represents a header.
* @param itemPosition int.
* @return true, if item at the specified adapter's position represents a header.
*/
boolean isHeader(int itemPosition);
}
}
RecyclerView recyclerView = findViewById(R.id.recyclerView);
SimpleRecyclerView adapter = new SimpleRecyclerView();
LinearLayoutManager layoutManager = new LinearLayoutManager(this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(layoutManager);
recyclerView.addItemDecoration(new StickHeaderItemDecoration(adapter));
@krupinder955

This comment has been minimized.

Copy link

krupinder955 commented Feb 19, 2019

I am not able to get click event on the header. is it possible ?
As i have a image view in the header view i need to get the click event.

@Shusshu

This comment has been minimized.

Copy link

Shusshu commented Mar 20, 2019

You can't interact with ItemDecorators

@idrisbohra

This comment has been minimized.

Copy link

idrisbohra commented Jul 22, 2019

How to handle the pagination?

@palplaykimo

This comment has been minimized.

Copy link

palplaykimo commented Jul 24, 2019

this stick header very helpful me, thank you .

@ArunYogeshwaran

This comment has been minimized.

Copy link

ArunYogeshwaran commented Aug 7, 2019

Thanks for this

@Jaosrikate

This comment has been minimized.

Copy link

Jaosrikate commented Oct 31, 2019

This is helpful, Thank you
It's working!

@aruns2896

This comment has been minimized.

Copy link

aruns2896 commented Nov 18, 2019

@smuyyh what is paint???

@smuyyh

This comment has been minimized.

Copy link

smuyyh commented Nov 18, 2019

https://github.com/smuyyh/StickyHeaderRecyclerView

This allows sticky headers to be implemented through viewholder

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.