ItemDecoration常见使用

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);

显示效果

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值