Skip to content

Instantly share code, notes, and snippets.

@shishirthedev
Created April 28, 2019 10:51
Show Gist options
  • Select an option

  • Save shishirthedev/a4ee4a6f51bccd786465e85d85026c49 to your computer and use it in GitHub Desktop.

Select an option

Save shishirthedev/a4ee4a6f51bccd786465e85d85026c49 to your computer and use it in GitHub Desktop.
public class SnStickyHeaderItemDecoration extends RecyclerView.ItemDecoration {
private SnStickyListener mListener;
private Integer mHeaderHeight;
public SnStickyHeaderItemDecoration(SnStickyListener listener) {
this.mListener = listener;
}
@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull 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 = mHeaderHeight - 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(), mHeaderHeight = view.getMeasuredHeight());
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
public interface SnStickyListener {
Integer getHeaderPositionForItem(Integer itemPosition);
Integer getHeaderLayout(Integer headerPosition);
void bindHeaderData(View header, Integer headerPosition);
Boolean isHeader(Integer itemPosition);
}
///////////////////////////////////////////////////////////////////////////////////////////////
public class DepositLogsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> implements SnStickyListener {
private Context context;
static final int TYPE_HEADER = 0;
static final int TYPE_ITEM = 1;
public DepositLogsAdapter(Context context) {
this.context = context;
}
@Override
public int getItemViewType(int position) {
return position % 5 == 0 ? TYPE_HEADER : TYPE_ITEM;
}
@Override
public Integer getHeaderPositionForItem(Integer itemPosition) {
Integer headerPosition = 0;
for (Integer i = itemPosition; i > 0; i--) {
if (isHeader(i)) {
headerPosition = i;
return headerPosition;
}
}
return headerPosition;
}
@Override
public Integer getHeaderLayout(Integer headerPosition) {
return R.layout.deposit_log_header_view;
}
@Override
public void bindHeaderData(View headerView, Integer headerPosition) {
// bind data to header view.....
}
@Override
public Boolean isHeader(Integer itemPosition) {
return itemPosition % 5 == 0;
}
class ItemView extends RecyclerView.ViewHolder {
ItemView(View itemView) {
super(itemView);
}
}
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == TYPE_ITEM) {
View view = LayoutInflater.from(context).inflate(R.layout.deposit_log_item_view, parent, false);
return new ItemView(view);
} else {
View view = LayoutInflater.from(context).inflate(R.layout.deposit_log_header_view, parent, false);
return new ItemView(view);
}
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
}
@Override
public int getItemCount() {
return 25;
}
}
// END
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment