Skip to content

Instantly share code, notes, and snippets.

@keiththompson
Created December 13, 2017 10:08
Show Gist options
  • Save keiththompson/6e6d5ee48b07096f26521dadf678fbc7 to your computer and use it in GitHub Desktop.
Save keiththompson/6e6d5ee48b07096f26521dadf678fbc7 to your computer and use it in GitHub Desktop.
An ItemDecoration that will draw a given Drawable as a header and or a footer on your RecyclerView.
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.annotation.IntDef;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.LinearLayout;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
public class HeaderFooterItemDecoration extends RecyclerView.ItemDecoration {
public static final int HORIZONTAL = LinearLayout.HORIZONTAL;
public static final int VERTICAL = LinearLayout.VERTICAL;
@Retention(RetentionPolicy.SOURCE) @IntDef({ HORIZONTAL, VERTICAL })
public @interface Orientation {
}
public static final int HEADER_ONLY = 0;
public static final int FOOTER_ONLY = 1;
public static final int HEADER_AND_FOOTER = 2;
@Retention(RetentionPolicy.SOURCE) @IntDef({ HEADER_ONLY, FOOTER_ONLY, HEADER_AND_FOOTER })
public @interface Mode {
}
public HeaderFooterItemDecoration(Drawable drawable, @Mode int mode,
@Orientation int orientation) {
this(drawable, new Rect(), mode, orientation);
}
HeaderFooterItemDecoration(Drawable drawable, Rect bounds, @Mode int mode,
@Orientation int orientation) {
this.drawable = drawable;
this.bounds = bounds;
this.mode = mode;
this.orientation = orientation;
}
private final Drawable drawable;
private final Rect bounds;
@Mode private final int mode;
@Orientation private final int orientation;
@Override public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
if (parent.getLayoutManager() == null || drawable == null) {
return;
}
if (orientation == VERTICAL) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
private void drawVertical(Canvas canvas, RecyclerView parent) {
canvas.save();
final int left;
final int right;
//noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
if (parent.getClipToPadding()) {
left = parent.getPaddingLeft();
right = parent.getWidth() - parent.getPaddingRight();
canvas.clipRect(left, parent.getPaddingTop(), right,
parent.getHeight() - parent.getPaddingBottom());
} else {
left = 0;
right = parent.getWidth();
}
final int childCount = parent.getChildCount();
final int adapterItemCount = parent.getAdapter().getItemCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
if (shouldDrawForPosition(parent.getChildAdapterPosition(child), adapterItemCount)) {
parent.getDecoratedBoundsWithMargins(child, bounds);
final int bottom = bounds.bottom + Math.round(child.getTranslationY());
final int top = bottom - drawable.getIntrinsicHeight();
drawable.setBounds(left, top, right, bottom);
drawable.draw(canvas);
}
}
canvas.restore();
}
private void drawHorizontal(Canvas canvas, RecyclerView parent) {
canvas.save();
final int top;
final int bottom;
//noinspection AndroidLintNewApi - NewApi lint fails to handle overrides.
if (parent.getClipToPadding()) {
top = parent.getPaddingTop();
bottom = parent.getHeight() - parent.getPaddingBottom();
canvas.clipRect(parent.getPaddingLeft(), top, parent.getWidth() - parent.getPaddingRight(),
bottom);
} else {
top = 0;
bottom = parent.getHeight();
}
final int childCount = parent.getChildCount();
final int adapterItemCount = parent.getAdapter().getItemCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
if (shouldDrawForPosition(parent.getChildAdapterPosition(child), adapterItemCount)) {
parent.getLayoutManager().getDecoratedBoundsWithMargins(child, bounds);
final int right = bounds.right + Math.round(child.getTranslationX());
final int left = right - drawable.getIntrinsicWidth();
drawable.setBounds(left, top, right, bottom);
drawable.draw(canvas);
}
}
canvas.restore();
}
@Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
if (drawable == null || !shouldDrawForPosition(position, state.getItemCount())) {
outRect.set(0, 0, 0, 0);
return;
}
if (orientation == VERTICAL) {
int top = isFirstItem(position) ? drawable.getIntrinsicHeight() : 0;
int bottom = isLastItem(position, state.getItemCount()) ? drawable.getIntrinsicHeight() : 0;
outRect.set(0, top, 0, bottom);
} else {
int left = isFirstItem(position) ? drawable.getIntrinsicWidth() : 0;
int right = isLastItem(position, state.getItemCount()) ? drawable.getIntrinsicWidth() : 0;
outRect.set(left, 0, right, 0);
}
}
private boolean isFirstItem(int position) {
return position == 0;
}
private boolean isLastItem(int position, int totalCount) {
return position == totalCount - 1;
}
private boolean shouldDrawForPosition(int position, int totalItems) {
switch (mode) {
case FOOTER_ONLY:
return isLastItem(position, totalItems);
case HEADER_AND_FOOTER:
return isLastItem(position, totalItems) || isFirstItem(position);
case HEADER_ONLY:
return isFirstItem(position);
default:
return false;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment