Skip to content

Instantly share code, notes, and snippets.

@Anrimian
Created March 6, 2018 14:19
Show Gist options
  • Save Anrimian/08eff1269559fe191014cff50d210dd2 to your computer and use it in GitHub Desktop.
Save Anrimian/08eff1269559fe191014cff50d210dd2 to your computer and use it in GitHub Desktop.
public class StickyHeaderViewManager<T> {
@Nonnull
private View headerView;
@Nonnull
private RecyclerView recyclerView;
@Nonnull
private StickyHeaderViewWrapper<T> viewWrapper;
@Nonnull
private Class<T> headerDataClass;
private List<?> items;
public StickyHeaderViewManager(@Nonnull View headerView,
@Nonnull RecyclerView recyclerView,
@Nonnull Class<T> headerDataClass,
@Nonnull StickyHeaderViewWrapper<T> viewWrapper) {
this.headerView = headerView;
this.viewWrapper = viewWrapper;
this.recyclerView = recyclerView;
this.headerDataClass = headerDataClass;
}
public void attach(@Nonnull List<?> items) {
this.items = items;
if (ViewCompat.isLaidOut(headerView)) {
bindHeader(recyclerView);
} else {
headerView.post(() -> bindHeader(recyclerView));
}
recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
bindHeader(recyclerView);
}
});
}
private void bindHeader(RecyclerView recyclerView) {
if (items.isEmpty()) {
headerView.setVisibility(View.GONE);
return;
} else {
headerView.setVisibility(View.VISIBLE);
}
View topView = recyclerView.getChildAt(0);
if (topView == null) {
return;
}
int topPosition = recyclerView.getChildAdapterPosition(topView);
if (!isValidPosition(topPosition)) {
return;
}
if (topPosition == 0 && topView.getTop() == recyclerView.getTop()) {
headerView.setVisibility(View.GONE);
return;
} else {
headerView.setVisibility(View.VISIBLE);
}
T stickyItem;
Object firstItem = items.get(topPosition);
if (headerDataClass.isInstance(firstItem)) {
stickyItem = headerDataClass.cast(firstItem);
headerView.setTranslationY(0);
} else {
stickyItem = findNearestHeader(topPosition);
int secondPosition = topPosition + 1;
if (isValidPosition(secondPosition)) {
Object secondItem = items.get(secondPosition);
if (headerDataClass.isInstance(secondItem)) {
View secondView = recyclerView.getChildAt(1);
if (secondView != null) {
moveViewFor(secondView);
}
} else {
headerView.setTranslationY(0);
}
}
}
if (stickyItem != null) {
viewWrapper.bindView(stickyItem);
}
}
private void moveViewFor(View secondView) {
if (secondView.getTop() <= headerView.getBottom()) {
headerView.setTranslationY(secondView.getTop() - headerView.getHeight());
} else {
headerView.setTranslationY(0);
}
}
private T findNearestHeader(int position) {
for (int i = position; position >= 0; i--) {
Object item = items.get(i);
if (headerDataClass.isInstance(item)) {
return headerDataClass.cast(item);
}
}
return null;
}
private boolean isValidPosition(int position) {
return !(position == RecyclerView.NO_POSITION || items.isEmpty() || position >= items.size());
}
}
public interface StickyHeaderViewWrapper<T> {
void bindView(T data);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment