Instantly share code, notes, and snippets.
Created
December 13, 2017 10:08
-
Star
0
(0)
You must be signed in to star a gist -
Fork
0
(0)
You must be signed in to fork a gist
-
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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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