1、实现分割线效果
适用于LinearLayoutManager 和 GridLayoutManager
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.annotation.DimenRes;
import android.support.v4.content.ContextCompat;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
/**
*
* @author A16543
* @date 2017/11/21
*/
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
private int dividerHeight = 1;//分割线高度
private Paint paint;
private RecyclerView.LayoutManager layoutManager;
/* 是否显示最后位置的下划线 */
private boolean mShowLastDivider = true;
/* 距离左右的距离 */
private int leftMargin = 0;
private int rightMargin = 0;
/*默认初始化*/
public DividerItemDecoration() {
initPaint();
paint.setColor(Color.GRAY);
}
private void initPaint() {
if (paint == null) {
paint = new Paint(Paint.ANTI_ALIAS_FLAG);
paint.setStyle(Paint.Style.FILL);
}
}
public DividerItemDecoration setDividerHeight(int dividerHeight) {
this.dividerHeight = dividerHeight;
return this;
}
public DividerItemDecoration setDividerColor(int color) {
initPaint();
paint.setColor(color);
return this;
}
public void setmShowLastDivider(boolean mShowLastDivider) {
this.mShowLastDivider = mShowLastDivider;
}
public void setLeftMargin(int leftMargin) {
this.leftMargin = leftMargin;
}
public void setRightMargin(int rightMargin) {
this.rightMargin = rightMargin;
}
/**
* 设置分割线线的宽度和高度
* @param outRect
* @param view
* @param parent
* @param state
*/
@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, view, parent, state);
if (layoutManager == null) {
layoutManager = parent.getLayoutManager();
}
// 适用 LinearLayoutManager 和 GridLayoutManager
if (layoutManager instanceof LinearLayoutManager) {
int orientation = ((LinearLayoutManager) layoutManager).getOrientation();
if (orientation == LinearLayoutManager.VERTICAL) {
// 水平分割线将绘制在item底部
outRect.bottom = dividerHeight;
} else if (orientation == LinearLayoutManager.HORIZONTAL) {
// 垂直分割线将绘制在item右侧
outRect.right = dividerHeight;
}
if (layoutManager instanceof GridLayoutManager) {
GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) view.getLayoutParams();
// 如果是 GridLayoutManager 则需要绘制另一个方向上的分割线
if (orientation == LinearLayoutManager.VERTICAL && lp != null && lp.getSpanIndex() > 0) {
// 如果列表是垂直方向,则最左边的一列略过
outRect.left = dividerHeight;
} else if (orientation == LinearLayoutManager.HORIZONTAL && lp != null && lp.getSpanIndex() > 0) {
// 如果列表是水平方向,则最上边的一列略过
outRect.top = dividerHeight;
}
}
}
}
/**
* itemView 绘制之前调用
* @param c
* @param parent
* @param state
*/
@Override
public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDraw(c, parent, state);
// 这个值是为了补偿横竖方向上分割线交叉处间隙
int offSet = (int) Math.ceil(dividerHeight * 1f / 2);
int childCount = parent.getChildCount();
if(!mShowLastDivider){
childCount--;
}
for (int i = 0; i < childCount; i++) {
View child = parent.getChildAt(i);
RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
int left1 = child.getRight() + params.rightMargin;
int right1 = left1 + dividerHeight;
int top1 = child.getTop() - offSet - params.topMargin;
int bottom1 = child.getBottom() + offSet + params.bottomMargin;
//绘制分割线(矩形)
c.drawRect(left1, top1, right1, bottom1, paint);
int left2 = child.getLeft() - offSet - params.leftMargin+leftMargin;
int right2 = child.getRight() + offSet + params.rightMargin-rightMargin;
int top2 = child.getBottom() + params.bottomMargin;
int bottom2 = top2 + dividerHeight;
//绘制分割线(矩形)
c.drawRect(left2, top2, right2, bottom2, paint);
}
}
/**
* itemView 绘制之后调用
* @param c
* @param parent
* @param state
*/
@Override
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
super.onDrawOver(c, parent, state);
}
public static class Builder {
private Context mContext;
protected Resources mResources;
private DividerItemDecoration itemDecoration;
public Builder(Context context) {
mContext = context;
mResources = context.getResources();
itemDecoration = new DividerItemDecoration();
}
public Builder color(int color){
itemDecoration.setDividerColor(color);
return this;
}
public Builder colorResId(int color){
itemDecoration.setDividerColor(ContextCompat.getColor(mContext,color));
return this;
}
public Builder heght(final int size) {
itemDecoration.setDividerHeight(size);
return this;
}
public Builder heightResId(@DimenRes int id) {
itemDecoration.setDividerHeight(mResources.getDimensionPixelSize(id));
return this;
}
public DividerItemDecoration create(){
return itemDecoration;
}
public Builder showLastDivider(boolean isShowLastDivider) {
itemDecoration.setmShowLastDivider(isShowLastDivider);
return this;
}
public Builder leftMargin(int leftMargin){
itemDecoration.setLeftMargin(leftMargin);
return this;
}
public Builder rightMargin(int rightMargin){
itemDecoration.setRightMargin(rightMargin);
return this;
}
public Builder leftMarginRes(int id){
itemDecoration.setRightMargin(mResources.getDimensionPixelSize(id));
return this;
}
public Builder rightMarginRes(int id){
itemDecoration.setRightMargin(mResources.getDimensionPixelSize(id));
return this;
}
}
}
使用方式
DividerItemDecoration itemDecoration = new DividerItemDecoration.Builder(this).colorResId(R.color.grey_335).heightResId(R.dimen.dimen_0_5dp).create();
recyclerView.addItemDecoration();
2、分组置顶效果
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.RecyclerView;
import android.util.SparseArray;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import java.util.HashMap;
import java.util.Map;
public abstract class NormalDecoration extends RecyclerView.ItemDecoration {
private Paint mHeaderTxtPaint;
private Paint mHeaderContentPaint;
protected int headerHeight = 120;//头部高度
private int textPaddingLeft = 50;//头部文字左边距
private int textSize = 48;
private int textColor = 0xffffffff;
private int headerContentColor = 0xff7F92A6;
private final float txtYAxis;
private RecyclerView mRecyclerView;
public NormalDecoration() {
mHeaderTxtPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHeaderTxtPaint.setColor(textColor);
mHeaderTxtPaint.setTextSize(textSize);
mHeaderTxtPaint.setTextAlign(Paint.Align.LEFT);
mHeaderContentPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mHeaderContentPaint.setColor(headerContentColor);
Paint.FontMetrics fontMetrics = mHeaderTxtPaint.getFontMetrics();
float total = -fontMetrics.ascent + fontMetrics.descent;
txtYAxis = total / 2 - fontMetrics.descent;
}
private boolean isInitHeight = false;
/**
* 先调用getItemOffsets再调用onDraw
*/
@Override
public void getItemOffsets(Rect outRect, View itemView, RecyclerView parent, RecyclerView.State state) {
super.getItemOffsets(outRect, itemView, parent, state);
if (mRecyclerView == null) {
mRecyclerView = parent;
}
if (headerDrawEvent != null && !isInitHeight) {
View headerView = headerDrawEvent.getHeaderView(0);
headerView
.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
headerHeight = headerView.getMeasuredHeight();
isInitHeight = true;
}
/*我们为每个不同头部名称的第一个item设置头部高度*/
int pos = parent.getChildAdapterPosition(itemView); //获取当前itemView的位置
String curHeaderName = getHeaderName(pos); //根据pos获取要悬浮的头部名
if (curHeaderName == null) {
return;
}
if (pos == 0 || !curHeaderName.equals(getHeaderName(pos - 1))) {//如果当前位置为0,或者与上一个item头部名不同的,都腾出头部空间
outRect.top = headerHeight; //设置itemView PaddingTop的距离
}
}
public abstract String getHeaderName(int pos);
private SparseArray<Integer> stickyHeaderPosArray = new SparseArray<>();//记录每个头部和悬浮头部的坐标信息【用于点击事件】
private GestureDetector gestureDetector;
@Override
public void onDrawOver(Canvas canvas, RecyclerView recyclerView, RecyclerView.State state) {
super.onDrawOver(canvas, recyclerView, state);
if (mRecyclerView == null) {
mRecyclerView = recyclerView;
}
if (gestureDetector == null) {
gestureDetector = new GestureDetector(recyclerView.getContext(), gestureListener);
recyclerView.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return gestureDetector.onTouchEvent(event);
}
});
}
stickyHeaderPosArray.clear();
int childCount = recyclerView.getChildCount();//获取屏幕上可见的item数量
int left = recyclerView.getLeft() + recyclerView.getPaddingLeft();
int right = recyclerView.getRight() - recyclerView.getPaddingRight();
String firstHeaderName = null;
int firstPos = 0;
int translateTop = 0;//绘制悬浮头部的偏移量
/*for循环里面绘制每个分组的头部*/
for (int i = 0; i < childCount; i++) {
View childView = recyclerView.getChildAt(i);
int pos = recyclerView.getChildAdapterPosition(childView); //获取当前view在Adapter里的pos
String curHeaderName = getHeaderName(pos); //根据pos获取要悬浮的头部名
if (i == 0) {
firstHeaderName = curHeaderName;
firstPos = pos;
}
if (curHeaderName == null) {
continue;//如果headerName为空,跳过此次循环
}
int viewTop = childView.getTop() + recyclerView.getPaddingTop();
if (pos == 0 || !curHeaderName.equals(getHeaderName(pos - 1))) {//如果当前位置为0,或者与上一个item头部名不同的,都腾出头部空间
if (headerDrawEvent != null) {
View headerView;
if (headViewMap.get(pos) == null) {
headerView = headerDrawEvent.getHeaderView(pos);
headerView
.measure(View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
headerView.setDrawingCacheEnabled(true);
headerView.layout(0, 0, right, headerHeight);//布局layout
headViewMap.put(pos, headerView);
canvas.drawBitmap(headerView.getDrawingCache(), left, viewTop - headerHeight, null);
} else {
headerView = headViewMap.get(pos);
canvas.drawBitmap(headerView.getDrawingCache(), left, viewTop - headerHeight, null);
}
} else {
canvas.drawRect(left, viewTop - headerHeight, right, viewTop, mHeaderContentPaint);
canvas.drawText(curHeaderName, left + textPaddingLeft, viewTop - headerHeight / 2 + txtYAxis, mHeaderTxtPaint);
}
if (headerHeight < viewTop && viewTop <= 2 * headerHeight) { //此判断是刚好2个头部碰撞,悬浮头部就要偏移
translateTop = viewTop - 2 * headerHeight;
}
stickyHeaderPosArray.put(pos, viewTop);//将头部信息放进array
}
}
if (firstHeaderName == null) {
return;
}
canvas.save();
canvas.translate(0, translateTop);
if (headerDrawEvent != null) {//inflater
View headerView;
if (headViewMap.get(firstPos) == null) {
headerView = headerDrawEvent.getHeaderView(firstPos);
headerView.measure(
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED),
View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
headerView.setDrawingCacheEnabled(true);
headerView.layout(0, 0, right, headerHeight);//布局layout
headViewMap.put(firstPos, headerView);
canvas.drawBitmap(headerView.getDrawingCache(), left, 0, null);
} else {
headerView = headViewMap.get(firstPos);
canvas.drawBitmap(headerView.getDrawingCache(), left, 0, null);
}
} else {
/*绘制悬浮的头部*/
canvas.drawRect(left, 0, right, headerHeight, mHeaderContentPaint);
canvas.drawText(firstHeaderName, left + textPaddingLeft, headerHeight / 2 + txtYAxis, mHeaderTxtPaint);
}
canvas.restore();
}
private Map<Integer, View> headViewMap = new HashMap<>();
public interface OnHeaderClickListener {
void headerClick(int pos);
}
private OnHeaderClickListener headerClickEvent;
public void setOnHeaderClickListener(OnHeaderClickListener headerClickListener) {
this.headerClickEvent = headerClickListener;
}
private GestureDetector.OnGestureListener gestureListener = new GestureDetector.OnGestureListener() {
@Override
public boolean onDown(MotionEvent e) {
return false;
}
@Override
public void onShowPress(MotionEvent e) {
}
@Override
public boolean onSingleTapUp(MotionEvent e) {
for (int i = 0; i < stickyHeaderPosArray.size(); i++) {
int value = stickyHeaderPosArray.valueAt(i);
float y = e.getY();
if (value - headerHeight <= y && y <= value) {//如果点击到分组头
if (headerClickEvent != null) {
headerClickEvent.headerClick(stickyHeaderPosArray.keyAt(i));
}
return true;
}
}
return false;
}
@Override
public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
return false;
}
@Override
public void onLongPress(MotionEvent e) {
}
@Override
public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {
return false;
}
};
private OnDecorationHeadDraw headerDrawEvent;
public interface OnDecorationHeadDraw {
View getHeaderView(int pos);
}
/**
* 只是用来绘制,不能做其他处理/点击事件等
*/
public void setOnDecorationHeadDraw(OnDecorationHeadDraw decorationHeadDraw) {
this.headerDrawEvent = decorationHeadDraw;
}
// public void loadImage(final String url, final int pos, ImageView imageView) {
//
// if (getImg(url) != null) {
// imageView.setImageDrawable(getImg(url));
//
// } else {
// Glide.with(mRecyclerView.getContext()).load(url).into(new SimpleTarget<GlideDrawable>() {
// @Override
// public void onResourceReady(GlideDrawable glideDrawable, GlideAnimation<? super GlideDrawable> glideAnimation) {
// headViewMap.remove(pos);//删除,重新更新
// imgDrawableMap.put(url, glideDrawable);
// mRecyclerView.postInvalidate();
// }
// });
// }
// }
private Map<String, Drawable> imgDrawableMap = new HashMap<>();
private Drawable getImg(String url) {
return imgDrawableMap.get(url);
}
public void onDestory() {
headViewMap.clear();
imgDrawableMap.clear();
stickyHeaderPosArray.clear();
mRecyclerView = null;
setOnHeaderClickListener(null);
setOnDecorationHeadDraw(null);
}
public void setHeaderHeight(int headerHeight) {
this.headerHeight = headerHeight;
}
public void setTextPaddingLeft(int textPaddingLeft) {
this.textPaddingLeft = textPaddingLeft;
}
public void setTextSize(int textSize) {
this.textSize = textSize;
this.mHeaderTxtPaint.setTextSize(textSize);
}
public void setTextColor(int textColor) {
this.textColor = textColor;
this.mHeaderTxtPaint.setColor(textColor);
}
public void setHeaderContentColor(int headerContentColor) {
this.headerContentColor = headerContentColor;
this.mHeaderContentPaint.setColor(headerContentColor);
}
}
使用方式
NormalDecoration normalDecoration = new NormalDecoration() {
@Override
public String getHeaderName(int pos) {
return recogResults.get(pos).getGroup(); //返回每个数据结构的组名,用于分组
}
};
recyclerView.addItemDecoration(normalDecoration);