美文网首页
CustomRecycleView:主要用于实现布局,分割线

CustomRecycleView:主要用于实现布局,分割线

作者: db87ce2992ef | 来源:发表于2021-01-06 20:07 被阅读0次

    CustomRecycleView

    /**
     * 自定义recycleview,主要用于实现布局,分割线
     */
    
    public class CustomRecycleView extends RecyclerView {
    
        //流式布局LayoutManager
        private FlowLayoutManager mFlowLayoutManager;
    
        public CustomRecycleView(Context context) {
            this(context, null);
        }
    
        public CustomRecycleView(Context context, @Nullable AttributeSet attrs) {
            this(context, attrs, 0);
        }
    
        public CustomRecycleView(Context context, @Nullable AttributeSet attrs, int defStyle) {
            super(context, attrs, defStyle);
            init(context, attrs);
        }
    
        private void init(Context context, @Nullable AttributeSet attrs) {
            TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomRecycleView);
            int layoutManagerType = a.getInteger(R.styleable.CustomRecycleView_layoutManagerType, 0);
            int spanCount = a.getInteger(R.styleable.CustomRecycleView_spanCount, 3);
            boolean canScroll = a.getBoolean(R.styleable.CustomRecycleView_canScroll, true);
            boolean divider = a.getBoolean(R.styleable.CustomRecycleView_useDivider, false);
            int dividerColor = a.getColor(R.styleable.CustomRecycleView_dividerColor, -1);
            int dividerHeight = (int) (a.getDimension(R.styleable.CustomRecycleView_dividerHeight, 3));
            int dividerResId = a.getResourceId(R.styleable.CustomRecycleView_dividerResId, -1);
            //int dividerMarginStart = (int)(a.getDimension(R.styleable.CustomRecycleView_dividerResId,0));
            //int dividerMarginEnd = (int)(a.getDimension(R.styleable.CustomRecycleView_dividerResId,0));
    
    
            DividerItemDecoration.DividerDec dividerDec = new DividerItemDecoration.DividerDec(
                    a.getBoolean(R.styleable.CustomRecycleView_useStartDivider, false),
                    a.getBoolean(R.styleable.CustomRecycleView_useTopDivider, false),
                    a.getBoolean(R.styleable.CustomRecycleView_useEndDivider, false),
                    a.getBoolean(R.styleable.CustomRecycleView_useBottomDivider, false)
            );
    
            if (layoutManagerType == 3) {
                mFlowLayoutManager = new FlowLayoutManager();
            }
    
            initLayoutManager(layoutManagerType, canScroll, spanCount);
    
            initDivider(divider, layoutManagerType, dividerColor, dividerHeight, dividerResId, dividerDec);
    
            a.recycle();
        }
    
    
        private void initLayoutManager(int layoutManagerType, boolean canScroll, int spanCount) {
            setNestedScrollingEnabled(canScroll);
            switch (layoutManagerType) {
                case 0:
                    setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false) {
    
                        @Override
                        public boolean canScrollVertically() {
                            return canScroll && super.canScrollVertically();
                        }
    
                        @Override
                        public void onLayoutChildren(Recycler recycler, State state) {
                            //解决java.lang.IndexOutOfBoundsException: Inconsistency detected.的bug
                            try {
                                super.onLayoutChildren(recycler, state);
                            } catch (IndexOutOfBoundsException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    break;
                case 1:
                    setLayoutManager(new LinearLayoutManager(getContext(), LinearLayoutManager.HORIZONTAL, false) {
    
                        @Override
                        public boolean canScrollHorizontally() {
                            return canScroll && super.canScrollHorizontally();
                        }
    
                        @Override
                        public void onLayoutChildren(Recycler recycler, State state) {
                            try {
                                super.onLayoutChildren(recycler, state);
                            } catch (IndexOutOfBoundsException e) {
                                e.printStackTrace();
                            }
                        }
    
                        @Override
                        public void smoothScrollToPosition(RecyclerView recyclerView,
                                                           State state, final int position) {
    
                            LinearSmoothScroller smoothScroller =
                                    new LinearSmoothScroller(recyclerView.getContext()) {
                                        // 返回:滑过1px时经历的时间(ms)。
                                        @Override
                                        protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
                                            return 150f / displayMetrics.densityDpi;
                                        }
                                    };
    
                            smoothScroller.setTargetPosition(position);
                            startSmoothScroll(smoothScroller);
                        }
    
                    });
                    break;
                case 2:
                    setLayoutManager(new GridLayoutManager(getContext(), spanCount) {
    
                        @Override
                        public void onLayoutChildren(Recycler recycler, State state) {
                            try {
                                super.onLayoutChildren(recycler, state);
                            } catch (IndexOutOfBoundsException e) {
                                e.printStackTrace();
                            }
                        }
                    });
                    break;
                case 3:
                    setLayoutManager(mFlowLayoutManager);
                    break;
                case 4:
                    //不设置任何适配器,留给外部自行设置
                    break;
            }
        }
    
        /***
         * 在setLayoutManager后面调用
         * @param divider 是否需要分割线
         * @param layoutManagerType 分割线方向
         */
        private void initDivider(boolean divider, int layoutManagerType, int dividerColor, int dividerHeight, int dividerResId, DividerItemDecoration.DividerDec dividerDec) {
            if (!divider) {
                return;
            }
            DividerItemDecoration dividerItemDecoration = null;
            switch (layoutManagerType) {
                case 0:
                    dividerItemDecoration = getDividerItemDecoration(DividerItemDecoration.HORIZONTAL_LIST, dividerColor, dividerHeight, dividerResId, dividerDec);
                    break;
                case 1:
                    dividerItemDecoration = getDividerItemDecoration(DividerItemDecoration.VERTICAL_LIST, dividerColor, dividerHeight, dividerResId, dividerDec);
                    break;
                case 2:
                case 3:
                    dividerItemDecoration = getDividerItemDecoration(DividerItemDecoration.BOTH_SET, dividerColor, dividerHeight, dividerResId, dividerDec);
                    break;
                default:
                    break;
            }
    
            if (dividerItemDecoration != null) {
                addItemDecoration(dividerItemDecoration);
            }
        }
    
        private DividerItemDecoration getDividerItemDecoration(int orientation, int dividerColor, int dividerHeight, int dividerResId, DividerItemDecoration.DividerDec dividerDec) {
            DividerItemDecoration dividerItemDecoration;
            if (dividerResId > 0) {
                dividerItemDecoration = new DividerItemDecoration(getContext(), orientation, dividerResId, dividerDec);
            } else if (dividerHeight > 0) {
                dividerItemDecoration = new DividerItemDecoration(getContext(), orientation, dividerHeight, dividerColor, dividerDec);
            } else {
                dividerItemDecoration = new DividerItemDecoration(getContext(), orientation, dividerDec);
            }
            return dividerItemDecoration;
        }
    
    
        /***
         * 水平平滑滚动
         */
        public void moveToPosition(int position) {
            if (!(getLayoutManager() instanceof LinearLayoutManager)) {
                return;
            }
            LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
            if (position > -1 && getAdapter() != null && position < getAdapter().getItemCount()) {
                scrollToPosition(position);
                layoutManager.scrollToPositionWithOffset(position, 0);
            }
        }
    
        /***
         * 还有问题
         * 水平平滑滚动
         * @param targetPos 目标位置
         */
        public boolean moveToPositionH(int targetPos) {
            boolean move = false;
            if (!(getLayoutManager() instanceof LinearLayoutManager)) {
                return move;
            }
            LinearLayoutManager layoutManager = (LinearLayoutManager) getLayoutManager();
    
            //先从RecyclerView的LayoutManager中获取第一项和最后一项的Position
            int firstItem = layoutManager.findFirstVisibleItemPosition();
            int lastItem = layoutManager.findLastVisibleItemPosition();
            //然后区分情况
            if (targetPos <= firstItem) {
                //当要置顶的项在当前显示的第一个项的前面时
                smoothScrollToPosition(targetPos);
            } else if (targetPos <= lastItem) {
                //当要置顶的项已经在屏幕上显示时
                int left = getChildAt(targetPos - firstItem).getLeft();
                smoothScrollBy(left, 0);
            } else {
                //当要置顶的项在当前显示的最后一项的后面时
                smoothScrollToPosition(targetPos);
                //这里这个变量是用在RecyclerView滚动监听里面的
                move = true;
            }
            return move;
    
        }
    }
    

    自定义属性

        <declare-styleable name="CustomRecycleView">
            <attr name="layoutManagerType" format="enum">
                <enum name="linerV" value="0" />
                <enum name="linerH" value="1" />
                <enum name="grid" value="2" />
                <enum name="flow" value="3" />
                <enum name="empty" value="4" />
            </attr>
    
            <attr name="spanCount" format="integer" />
            <attr name="canScroll" format="boolean" />
            <attr name="useDivider" format="boolean" />
            <attr name="dividerHeight" format="dimension|reference" />
            <attr name="dividerColor" format="color" />
            <attr name="dividerResId" format="reference" />
            <attr name="dividerMarginStart" format="dimension|reference" />
            <attr name="dividerMarginEnd" format="dimension|reference" />
            <attr name="useTopDivider" format="boolean" />
            <attr name="useStartDivider" format="boolean" />
            <attr name="useEndDivider" format="boolean" />
            <attr name="useBottomDivider" format="boolean" />
        </declare-styleable>
    

    DividerItemDecoration :分割线

    public class DividerItemDecoration extends RecyclerView.ItemDecoration {
        private Paint mPaint;
        //取名mDivider似乎更恰当
        private Drawable mDrawable;
        //分割线高度,默认为1px
        private int mDividerHeight = 2;
        //列表的方向
        private int mOrientation;
        //系统自带的参数
        private static final int[] ATTRS = new int[]{android.R.attr.listDivider};
        //水平
        public static final int HORIZONTAL_LIST = RecyclerView.HORIZONTAL;
        //垂直
        public static final int VERTICAL_LIST = RecyclerView.VERTICAL;
        //水平+垂直
        public static final int BOTH_SET = 2;
    
        private DividerDec mUseDivDec;
    
        public static class DividerDec{
            public boolean useTopDiv = false;
            public boolean useStartDiv = false;
            public boolean useEndDiv = false;
            public boolean useBottomDiv = false;
    
            public DividerDec(boolean useStartDiv, boolean useTopDiv, boolean useEndDiv, boolean useBottomDiv) {
                this.useStartDiv = useStartDiv;
                this.useTopDiv = useTopDiv;
                this.useEndDiv = useEndDiv;
                this.useBottomDiv = useBottomDiv;
            }
        }
    
    
        /**
         * 默认分割线:高度为2px,颜色为灰色
         *
         * @param context     上下文
         * @param orientation 列表方向
         */
        public DividerItemDecoration(Context context, int orientation, DividerDec dividerDec) {
            this.setOrientation(orientation);
            //获取xml配置的参数
            final TypedArray a = context.obtainStyledAttributes(ATTRS);
            //typedArray.getDrawable(attr)这句是说我们可以通过我们的资源获得资源,使用我们的资源名attr去获得资源id
            //看不懂就用自己写一个分割线的图片吧,方法:ContextCompat.getDrawable(context, drawableId);
            mDrawable = a.getDrawable(0);
            //官方的解释是:回收TypedArray,以便后面重用。在调用这个函数后,你就不能再使用这个TypedArray。
            //在TypedArray后调用recycle主要是为了缓存。
            mUseDivDec = dividerDec;
            a.recycle();
        }
    
        /**
         * 自定义分割线
         *
         * @param context     上下文
         * @param orientation 列表方向
         * @param drawableId  分割线图片
         */
        public DividerItemDecoration(Context context, int orientation, int drawableId, DividerDec dividerDec) {
            this.setOrientation(orientation);
            //旧的getDrawable方法弃用了,这个是新的
            mDrawable = ContextCompat.getDrawable(context, drawableId);
            mDividerHeight = mDrawable.getIntrinsicHeight();
            mUseDivDec = dividerDec;
        }
    
        /**
         * 自定义分割线
         *
         * @param context       上下文
         * @param orientation   列表方向
         * @param dividerHeight 分割线高度
         * @param dividerColor  分割线颜色
         */
        public DividerItemDecoration(Context context, int orientation,
                                     int dividerHeight, int dividerColor, DividerDec dividerDec) {
            this.setOrientation(orientation);
            mDividerHeight = dividerHeight;
            //抗锯齿画笔
            mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            mPaint.setColor(dividerColor);
            //填满颜色
            mPaint.setStyle(Paint.Style.FILL);
            mUseDivDec = dividerDec;
        }
    
        /**
         * 设置方向
         *
         * @param orientation
         */
        public void setOrientation(int orientation) {
            if (orientation < 0 || orientation > 2)
                throw new IllegalArgumentException("invalid orientation");
            mOrientation = orientation;
        }
    
    
        /**
         * 绘制分割线之后,需要留出一个外边框,就是说item之间的间距要换一下
         *
         * @param outRect outRect.set(0, 0, 0, 0);的四个参数理解成margin就好了
         * @param view    视图
         * @param parent  父级view
         * @param state
         */
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            //下面super...代码其实调用的就是那个过时的getItemOffsets,也就是说这个方法体内容也可以通通移到那个过时的getItemOffsets中
            super.getItemOffsets(outRect, view, parent, state);
            //获取layoutParams参数
            RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
            //当前位置
            int itemPosition = layoutParams.getViewLayoutPosition();
            //ItemView数量
            int childCount = parent.getAdapter().getItemCount();
            switch (mOrientation) {
                case BOTH_SET:
                    //获取Layout的相关参数
                    int spanCount = this.getSpanCount(parent);
                    if (isLastRaw(parent, itemPosition, spanCount, childCount)) {
                        // 如果是最后一行,则不需要绘制底部
                        outRect.set(0, 0, mDividerHeight, mUseDivDec.useBottomDiv ? mDividerHeight : 0);
                    } else if (isLastColum(parent, itemPosition, spanCount, childCount)) {
                        // 如果是最后一列,则不需要绘制右边
                        outRect.set(0, 0, mUseDivDec.useEndDiv ? mDividerHeight : 0, mDividerHeight);
                    } else {
                        if (itemPosition % spanCount == 0) {
                            outRect.set(mUseDivDec.useStartDiv ? mDividerHeight : 0, 0, mDividerHeight / 2, mDividerHeight);
                        } else {
                            outRect.set(mDividerHeight / 2, 0, mDividerHeight / 2, mDividerHeight);
                        }
    
                    }
                    break;
                case VERTICAL_LIST:
                    childCount -= 1;
                    //水平布局右侧留Margin,如果是最后一列,就不要留Margin了
                    if(itemPosition == 0 && childCount == 0){
                        //只有一列
                        outRect.set(mUseDivDec.useStartDiv ? mDividerHeight : 0,
                                0,  mUseDivDec.useBottomDiv ? mDividerHeight :0,0);
                    }else if(itemPosition == 0){
                        //第一列
                        outRect.set(mUseDivDec.useStartDiv ? mDividerHeight : 0,
                                0, mDividerHeight /2, 0);
                    }else if(itemPosition == childCount ){
                        //最后一列
                        outRect.set(mDividerHeight/2,
                                0, mUseDivDec.useEndDiv ? mDividerHeight : 0, 0);
                    }else{
                        outRect.set(mDividerHeight/2,
                                0, mDividerHeight/2, 0);
                    }
                    break;
                case HORIZONTAL_LIST:
                    childCount -= 1;
                    //垂直布局底部留边,最后一行不留
                    if(itemPosition == 0 && childCount == 0){
                        //只有一行
                        outRect.set( 0,
                                mUseDivDec.useTopDiv ? mDividerHeight : 0, 0, ( mUseDivDec.useBottomDiv ? mDividerHeight :0));
                    }else if(itemPosition == 0){
                        //第一行
                        outRect.set( 0,
                                mUseDivDec.useTopDiv ? mDividerHeight : 0, 0, mDividerHeight/2);
                    }else if(itemPosition == childCount ){
                        //最后一行
                        outRect.set(0,
                                mDividerHeight/2,  0, mUseDivDec.useBottomDiv ? mDividerHeight : 0);
                    }else{
                        outRect.set(0,
                                mDividerHeight/2, 0, mDividerHeight/2);
                    }
                    break;
                default:
                    break;
            }
        }
    
        /**
         * 绘制分割线
         *
         * @param c
         * @param parent
         * @param state
         */
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
            if (mOrientation == VERTICAL_LIST) {
                drawVertical(c, parent);
            } else if (mOrientation == HORIZONTAL_LIST) {
                drawHorizontal(c, parent);
            } else {
                drawHorizontal(c, parent);
                drawVertical(c, parent);
            }
        }
    
        /**
         * 绘制横向 item 分割线
         *
         * @param canvas 画布
         * @param parent 父容器
         */
        private void drawHorizontal(Canvas canvas, RecyclerView parent) {
            final int x = parent.getPaddingLeft();
            final int width = parent.getMeasuredWidth() - parent.getPaddingRight();
            //getChildCount()(ViewGroup.getChildCount) 返回的是显示层面上的“所包含的子 View 个数”。
            final int childSize = parent.getChildCount();
            for (int i = 0; i < childSize; i++) {
                final View child = parent.getChildAt(i);
                RecyclerView.LayoutParams layoutParams =
                        (RecyclerView.LayoutParams) child.getLayoutParams();
                //item底部的Y轴坐标+margin值
                final int y = child.getBottom() + layoutParams.bottomMargin;
                final int height = y + mDividerHeight;
                if (mDrawable != null) {
                    //setBounds(x,y,width,height); x:组件在容器X轴上的起点 y:组件在容器Y轴上的起点
                    // width:组件的长度 height:组件的高度
                    mDrawable.setBounds(x, y, width, height);
                    mDrawable.draw(canvas);
                }
                if (mPaint != null) {
                    canvas.drawRect(x, y, width, height, mPaint);
                }
            }
        }
    
        /**
         * 绘制纵向 item 分割线
         *
         * @param canvas
         * @param parent
         */
        private void drawVertical(Canvas canvas, RecyclerView parent) {
            final int top = parent.getPaddingTop();
            final int bottom = parent.getMeasuredHeight() - parent.getPaddingBottom();
            final int childSize = parent.getChildCount();
            for (int i = 0; i < childSize; i++) {
                final View child = parent.getChildAt(i);
                RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) child.getLayoutParams();
                final int left = child.getRight() + layoutParams.rightMargin;
                final int right = left + mDividerHeight;
                if (mDrawable != null) {
                    mDrawable.setBounds(left, top, right, bottom);
                    mDrawable.draw(canvas);
                }
                if (mPaint != null) {
                    canvas.drawRect(left, top, right, bottom, mPaint);
                }
            }
        }
    
    
        /**
         * 获取列数
         *
         * @param parent
         * @return
         */
        private int getSpanCount(RecyclerView parent) {
            int spanCount = -1;
            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager) {
                spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                spanCount = ((StaggeredGridLayoutManager) layoutManager)
                        .getSpanCount();
            }
            return spanCount;
        }
    
    
        private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
                                    int childCount) {
            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager) {
                int orientation = ((GridLayoutManager) layoutManager)
                        .getOrientation();
                if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                    // 如果是最后一列,则不需要绘制右边
                    if ((pos + 1) % spanCount == 0)
                        return true;
                } else {
                    childCount = childCount - childCount % spanCount;
                    // 如果是最后一列,则不需要绘制右边
                    if (pos >= childCount)
                        return true;
                }
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                int orientation = ((StaggeredGridLayoutManager) layoutManager)
                        .getOrientation();
                if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                    // 如果是最后一列,则不需要绘制右边
                    if ((pos + 1) % spanCount == 0)
                        return true;
                } else {
                    childCount = childCount - childCount % spanCount;
                    // 如果是最后一列,则不需要绘制右边
                    if (pos >= childCount)
                        return true;
                }
            }
            return false;
        }
    
        private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
                                  int childCount) {
            int orientation;
            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            if (layoutManager instanceof GridLayoutManager) {
                childCount = childCount - childCount % spanCount;
                orientation = ((GridLayoutManager) layoutManager)
                        .getOrientation();
                if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                    // 如果是最后一行,则不需要绘制底部
                    childCount = childCount - childCount % spanCount;
                    if (pos >= childCount)
                        return true;
                } else {// StaggeredGridLayoutManager 横向滚动
                    // 如果是最后一行,则不需要绘制底部
                    if ((pos + 1) % spanCount == 0)
                        return true;
                }
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                orientation = ((StaggeredGridLayoutManager) layoutManager)
                        .getOrientation();
                if (orientation == StaggeredGridLayoutManager.VERTICAL) {
                    // 如果是最后一行,则不需要绘制底部
                    childCount = childCount - childCount % spanCount;
                    if (pos >= childCount)
                        return true;
                } else {// StaggeredGridLayoutManager 横向滚动
                    // 如果是最后一行,则不需要绘制底部
                    if ((pos + 1) % spanCount == 0)
                        return true;
                }
            }
            return false;
        }
    }
    

    FlowLayoutManager :流式布局管理器

    public class FlowLayoutManager extends RecyclerView.LayoutManager {
    
        private static final String TAG = FlowLayoutManager.class.getSimpleName();
        final FlowLayoutManager self = this;
    
        protected int width, height;
        private int left, top, right;
        //最大容器的宽度
        private int usedMaxWidth;
        //竖直方向上的偏移量
        private int verticalScrollOffset = 0;
    
        public int getTotalHeight() {
            return totalHeight;
        }
    
        //计算显示的内容的高度
        protected int totalHeight = 0;
        private Row row = new Row();
        private List<Row> lineRows = new ArrayList<>();
    
        //保存所有的Item的上下左右的偏移量信息
        private SparseArray<Rect> allItemFrames = new SparseArray<>();
    
        public FlowLayoutManager() {
        }
    
        //设置主动测量规则,适应recyclerView高度为wrap_content
        @Override
        public boolean isAutoMeasureEnabled() {
            return true;
        }
    
        public int getRowCounts() {
            return lineRows.size();
        }
    
        //每个item的定义
        public class Item {
            int useHeight;
            View view;
    
            public void setRect(Rect rect) {
                this.rect = rect;
            }
    
            Rect rect;
    
            public Item(int useHeight, View view, Rect rect) {
                this.useHeight = useHeight;
                this.view = view;
                this.rect = rect;
            }
        }
    
        //行信息的定义
        public class Row {
            public void setCuTop(float cuTop) {
                this.cuTop = cuTop;
            }
    
            public void setMaxHeight(float maxHeight) {
                this.maxHeight = maxHeight;
            }
    
            //每一行的头部坐标
            float cuTop;
            //每一行需要占据的最大高度
            float maxHeight;
            //每一行存储的item
            List<Item> views = new ArrayList<>();
    
            public void addViews(Item view) {
                views.add(view);
            }
        }
    
        @Override
        public RecyclerView.LayoutParams generateDefaultLayoutParams() {
            return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
        }
    
        //该方法主要用来获取每一个item在屏幕上占据的位置
        @Override
        public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
            Log.d(TAG, "onLayoutChildren");
            totalHeight = 0;
            int cuLineTop = top;
            //当前行使用的宽度
            int cuLineWidth = 0;
            int itemLeft;
            int itemTop;
            int maxHeightItem = 0;
            row = new Row();
            lineRows.clear();
            allItemFrames.clear();
            removeAllViews();
            if (getItemCount() == 0) {
                detachAndScrapAttachedViews(recycler);
                verticalScrollOffset = 0;
                return;
            }
            if (getChildCount() == 0 && state.isPreLayout()) {
                return;
            }
            //onLayoutChildren方法在RecyclerView 初始化时 会执行两遍
            detachAndScrapAttachedViews(recycler);
            if (getChildCount() == 0) {
                width = getWidth();
                height = getHeight();
                left = getPaddingLeft();
                right = getPaddingRight();
                top = getPaddingTop();
                usedMaxWidth = width - left - right;
            }
    
            for (int i = 0; i < getItemCount(); i++) {
                Log.d(TAG, "index:" + i);
                View childAt = recycler.getViewForPosition(i);
                if (View.GONE == childAt.getVisibility()) {
                    continue;
                }
                measureChildWithMargins(childAt, 0, 0);
                int childWidth = getDecoratedMeasuredWidth(childAt);
                int childHeight = getDecoratedMeasuredHeight(childAt);
                int childUseWidth = childWidth;
                int childUseHeight = childHeight;
                //如果加上当前的item还小于最大的宽度的话
                if (cuLineWidth + childUseWidth <= usedMaxWidth) {
                    itemLeft = left + cuLineWidth;
                    itemTop = cuLineTop;
                    Rect frame = allItemFrames.get(i);
                    if (frame == null) {
                        frame = new Rect();
                    }
                    frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);
                    allItemFrames.put(i, frame);
                    cuLineWidth += childUseWidth;
                    maxHeightItem = Math.max(maxHeightItem, childUseHeight);
                    row.addViews(new Item(childUseHeight, childAt, frame));
                    row.setCuTop(cuLineTop);
                    row.setMaxHeight(maxHeightItem);
                } else {
                    //换行
                    formatAboveRow();
                    cuLineTop += maxHeightItem;
                    totalHeight += maxHeightItem;
                    itemTop = cuLineTop;
                    itemLeft = left;
                    Rect frame = allItemFrames.get(i);
                    if (frame == null) {
                        frame = new Rect();
                    }
                    frame.set(itemLeft, itemTop, itemLeft + childWidth, itemTop + childHeight);
                    allItemFrames.put(i, frame);
                    cuLineWidth = childUseWidth;
                    maxHeightItem = childUseHeight;
                    row.addViews(new Item(childUseHeight, childAt, frame));
                    row.setCuTop(cuLineTop);
                    row.setMaxHeight(maxHeightItem);
                }
                //不要忘了最后一行进行刷新下布局
                if (i == getItemCount() - 1) {
                    formatAboveRow();
                    totalHeight += maxHeightItem;
                }
    
            }
            totalHeight = Math.max(totalHeight, getVerticalSpace());
            Log.d(TAG, "onLayoutChildren totalHeight:" + totalHeight);
            fillLayout(recycler, state);
        }
    
        //对出现在屏幕上的item进行展示,超出屏幕的item回收到缓存中
        private void fillLayout(RecyclerView.Recycler recycler, RecyclerView.State state) {
            if (state.isPreLayout() || getItemCount() == 0) { // 跳过preLayout,preLayout主要用于支持动画
                return;
            }
    
            // 当前scroll offset状态下的显示区域
            Rect displayFrame = new Rect(getPaddingLeft(), getPaddingTop() + verticalScrollOffset,
                    getWidth() - getPaddingRight(), verticalScrollOffset + (getHeight() - getPaddingBottom()));
    
            //对所有的行信息进行遍历
            for (int j = 0; j < lineRows.size(); j++) {
                Row row = lineRows.get(j);
                float lineTop = row.cuTop;
                float lineBottom = lineTop + row.maxHeight;
                //如果该行在屏幕中,进行放置item
                //            if (lineTop < displayFrame.bottom && displayFrame.top < lineBottom) {
                List<Item> views = row.views;
                for (int i = 0; i < views.size(); i++) {
                    View scrap = views.get(i).view;
                    measureChildWithMargins(scrap, 0, 0);
                    addView(scrap);
                    Rect frame = views.get(i).rect;
                    //将这个item布局出来
                    layoutDecoratedWithMargins(scrap,
                            frame.left,
                            frame.top - verticalScrollOffset,
                            frame.right,
                            frame.bottom - verticalScrollOffset);
                }
                //            } else {
                //                //将不在屏幕中的item放到缓存中
                //                List<Item> views = row.views;
                //                for (int i = 0; i < views.size(); i++) {
                //                    View scrap = views.get(i).view;
                //                    removeAndRecycleView(scrap, recycler);
                //                }
                //            }
            }
        }
    
        /**
         * 计算每一行没有居中的viewgroup,让居中显示
         */
        private void formatAboveRow() {
            List<Item> views = row.views;
            for (int i = 0; i < views.size(); i++) {
                Item item = views.get(i);
                View view = item.view;
                int position = getPosition(view);
                //如果该item的位置不在该行中间位置的话,进行重新放置
                if (allItemFrames.get(position).top < row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2) {
                    Rect frame = allItemFrames.get(position);
                    if (frame == null) {
                        frame = new Rect();
                    }
                    frame.set(allItemFrames.get(position).left, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2),
                            allItemFrames.get(position).right, (int) (row.cuTop + (row.maxHeight - views.get(i).useHeight) / 2 + getDecoratedMeasuredHeight(view)));
                    allItemFrames.put(position, frame);
                    item.setRect(frame);
                    views.set(i, item);
                }
            }
            row.views = views;
            lineRows.add(row);
            row = new Row();
        }
    
        /**
         * 竖直方向需要滑动的条件
         *
         * @return
         */
        @Override
        public boolean canScrollVertically() {
            return true;
        }
    
        //监听竖直方向滑动的偏移量
        @Override
        public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler,
                                      RecyclerView.State state) {
    
            Log.d("TAG", "totalHeight:" + totalHeight);
            //实际要滑动的距离
            int travel = dy;
    
            //如果滑动到最顶部
            if (verticalScrollOffset + dy < 0) {//限制滑动到顶部之后,不让继续向上滑动了
                travel = -verticalScrollOffset;//verticalScrollOffset=0
            } else if (verticalScrollOffset + dy > totalHeight - getVerticalSpace()) {//如果滑动到最底部
                travel = totalHeight - getVerticalSpace() - verticalScrollOffset;//verticalScrollOffset=totalHeight - getVerticalSpace()
            }
    
            //将竖直方向的偏移量+travel
            verticalScrollOffset += travel;
    
            // 平移容器内的item
            offsetChildrenVertical(-travel);
            fillLayout(recycler, state);
            return travel;
        }
    
        private int getVerticalSpace() {
            return self.getHeight() - self.getPaddingBottom() - self.getPaddingTop();
        }
    
        public int getHorizontalSpace() {
            return self.getWidth() - self.getPaddingLeft() - self.getPaddingRight();
        }
    
    }
    

    使用LinearLayoutManager

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".rv_custom.CustomRecycleViewActivity">
    
        <com.zly.mvvmhabit_demo.widget.CustomRecycleView
            android:id="@+id/crv"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:dividerColor="#eeeeee"
            app:dividerHeight="0.5dp"
            app:layoutManagerType="linerV"
            app:useDivider="true" />
    
    </LinearLayout>
    
    public class CustomRecycleViewActivity extends AppCompatActivity {
    
        private CustomRecycleView mCustomRecycleView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_custom_recyclerview);
    
            mCustomRecycleView = (CustomRecycleView) findViewById(R.id.crv);
    
            List<String> datas = new ArrayList<>();
            for (int i = 0; i < 50; i++) {
                datas.add("赵丽颖" + i);
            }
    
            mCustomRecycleView.setAdapter(new CommomRvAdapter<String>(this, datas, R.layout.item_common_rv) {
                @Override
                protected void fillData(CommomRvViewHolder holder, int position, String s) {
                    holder.setText(R.id.tv, s);
                }
            });
        }
    }
    

    使用FlowLayoutManager

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".rv_custom.CustomRvFlowLayoutActivity">
    
    
        <com.zly.mvvmhabit_demo.widget.CustomRecycleView
            android:id="@+id/crv"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="10dp"
            android:paddingStart="10dp"
            android:paddingEnd="10dp"
            app:dividerColor="#00000000"
            app:dividerHeight="10dp"
            app:layoutManagerType="flow"
            app:useDivider="true" />
    
    </LinearLayout>
    
    
    public class CustomRvFlowLayoutActivity extends AppCompatActivity {
    
        private CustomRecycleView mCustomRecycleView;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_custom_rv_flow);
    
            mCustomRecycleView = (CustomRecycleView) findViewById(R.id.crv);
    
            List<String> datas = new ArrayList<>();
            for (int i = 0; i < 20; i++) {
                datas.add("赵丽颖" + i);
            }
    
            mCustomRecycleView.setAdapter(new CommomRvAdapter<String>(this, datas, R.layout.item_common_rv_flow) {
                @Override
                protected void fillData(CommomRvViewHolder holder, int position, String s) {
                    holder.setText(R.id.tv_lable, s);
                }
            });
        }
    }
    

    相关文章

      网友评论

          本文标题:CustomRecycleView:主要用于实现布局,分割线

          本文链接:https://www.haomeiwen.com/subject/payooktx.html