-
-
Save saber-solooki/edeb57be63d2a60ef551676067c66c71 to your computer and use it in GitHub Desktop.
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)); |
You can't interact with ItemDecorators
How to handle the pagination?
this stick header very helpful me, thank you .
Thanks for this
This is helpful, Thank you
It's working!
@smuyyh what is paint???
https://github.com/smuyyh/StickyHeaderRecyclerView
This allows sticky headers to be implemented through viewholder
If you scroll immediately after setting response then it is crashing
java.lang.IndexOutOfBoundsException: Index: 7, Size: 7
at java.util.ArrayList.get(ArrayList.java:437)
at com.test.ui.alerts.adapters.HeaderChildAdapter.isHeader(HeaderChildAdapter.kt:56)
have you handle this crash?
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.
You can use this approach if it suits your requirements it can support clicks aswell for sticky headers, https://gist.github.com/preetham1316/d21be9e6987be601a7d5507081c127a8
The header will duplicate when the view overlay. How to solve it?
https://stackoverflow.com/questions/76903052/android-recyclerview-sticky-header-alpha-view
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.