美文网首页
RecyclerView.ItemDecoration实现指示器

RecyclerView.ItemDecoration实现指示器

作者: gu_jingli | 来源:发表于2020-03-16 14:07 被阅读0次

    参考:https://github.com/bleeding182/recyclerviewItemDecorations/blob/master/app/src/main/java/com/github/bleeding182/recyclerviewdecorations/viewpager/LinePagerIndicatorDecoration.java

    该效果是一个长线型的指示器,在此基础上修改定制成圆形指示器。


    Screenrecorder-2020-03-16-13-50-29-163.gif
    package com.gujingli.recycler.util;
    
    import android.content.res.Resources;
    import android.graphics.Canvas;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.PointF;
    import android.graphics.Rect;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.util.Log;
    import android.view.View;
    import android.view.animation.AccelerateDecelerateInterpolator;
    import android.view.animation.Interpolator;
    
    public class DropIndicator extends RecyclerView.ItemDecoration {
    
        private int mIndicatorHeight;//指示器所占高度
        private int indicatorCount;
        private final Interpolator mInterpolator = new AccelerateDecelerateInterpolator();//插值器:先加速再减速
        private static final float DP = Resources.getSystem().getDisplayMetrics().density;//屏幕密度
        private Paint indicatorPaint;//指示器画笔
        private Paint selectorPaint;//选中器画笔
        private Path mPath = new Path();//选中器的形状
        private float radius = DP * 3;//圆直径
        private final double c = 0.552284749831;//path画圆固定值
        private float startX;//记录选中器的中心点X坐标
        private float startY;//记录指示器及选中器的中心点Y坐标
        private float firstX;//记录指示器及选中器的起始中心点X坐标
        private XPoint p2, p4;//右左切线
        private YPoint p1, p3;//下上切线
        private float mc;//切线半长
        private float div = DP * 12;//指示器间距
        private int currentPos = 0;//当前
        private int toPos = -1;
        private boolean direction = true;//方向
        private float distance;//位移
        private float mCurrentTime;//当前状态
        private float lastCurrentTime = 0;//最后状态
        private static final String TAG = "DropIndicator";//日志
    
        public DropIndicator() {
            //初始化
            mc = (float) (c * radius);
            mIndicatorHeight = (int) (radius * 2 + div);
            //未选中
            indicatorPaint = new Paint();
            indicatorPaint.setColor(0xFFE5E5E5);
            indicatorPaint.setStyle(Paint.Style.FILL);
            indicatorPaint.setAntiAlias(true);
            indicatorPaint.setStrokeWidth(1);
            //选中
            selectorPaint = new Paint();
            selectorPaint.setColor(0xFF000000);
            selectorPaint.setStyle(Paint.Style.FILL);
            selectorPaint.setStrokeWidth(1);
            selectorPaint.setAntiAlias(true);
            //初始化选中点
            p1 = new YPoint(0, radius, mc);//下
            p3 = new YPoint(0, -radius, mc);//上
            p2 = new XPoint(radius, 0, mc);//右
            p4 = new XPoint(-radius, 0, mc);//左
        }
    
        //初始化选中点
        private void resetP() {
            p1.setY(radius);
            p1.setX(0);
            p1.setMc(mc);
    
            p3.setY(-radius);
            p3.setX(0);
            p3.setMc(mc);
    
            p2.setY(0);
            p2.setX(radius);
            p2.setMc(mc);
    
            p4.setY(0);
            p4.setX(-radius);
            p4.setMc(mc);
        }
    
        //绘制选中点
        protected void dispatchDraw(Canvas canvas) {
            mPath.reset();
            if (mCurrentTime == 0) {//圆
                resetP();
                canvas.translate(startX, startY);//设置圆点位置
                if (toPos > currentPos) {
                    p2.setX(radius);
                } else {
                    p4.setX(-radius);
                }
            }
            if (mCurrentTime > 0 && mCurrentTime <= 0.2) {//第一阶段,前端变尖
                direction = toPos > currentPos ? true : false;
                canvas.translate(startX, startY);//设置圆点位置
                if (toPos > currentPos) {
                    p2.setX(radius + 2 * 5 * mCurrentTime * radius / 2);//改变p2.x坐标
                } else {
                    p4.setX(-radius - 2 * 5 * mCurrentTime * radius / 2);//改变p4.x坐标
                }
            } else if (mCurrentTime > 0.2 && mCurrentTime <= 0.5) {//第二阶段,开始移动变形
                canvas.translate(startX + (mCurrentTime - 0.2f) * distance / 0.7f, startY);//设置圆点位置
                if (toPos > currentPos) {
                    p2.setX(2 * radius);//p2.x坐标达到最大值
                    p1.setX(0.5f * radius * (mCurrentTime - 0.2f) / 0.3f);//p1.x坐标移动
                    p3.setX(0.5f * radius * (mCurrentTime - 0.2f) / 0.3f);//p3.x坐标移动
                    p2.setMc(mc + (mCurrentTime - 0.2f) * mc / 4 / 0.3f);//p2.mc间距变大
                    p4.setMc(mc + (mCurrentTime - 0.2f) * mc / 4 / 0.3f);//p4.mc间距变大
                } else {
                    p4.setX(-2 * radius);//p4.x坐标达到最大值
                    p1.setX(-0.5f * radius * (mCurrentTime - 0.2f) / 0.3f);//p1.x坐标移动
                    p3.setX(-0.5f * radius * (mCurrentTime - 0.2f) / 0.3f);//p3.x坐标移动
                    p2.setMc(mc + (mCurrentTime - 0.2f) * mc / 4 / 0.3f);//p2.mc间距变大
                    p4.setMc(mc + (mCurrentTime - 0.2f) * mc / 4 / 0.3f);//p4.mc间距变大
                }
            } else if (mCurrentTime > 0.5 && mCurrentTime <= 0.8) {//第二阶段,继续移动变形,逐渐恢复
                canvas.translate(startX + (mCurrentTime - 0.2f) * distance / 0.7f, startY);//设置圆点位置
                if (toPos > currentPos) {
                    p1.setX(0.5f * radius + 0.5f * radius * (mCurrentTime - 0.5f) / 0.3f);
                    p3.setX(0.5f * radius + 0.5f * radius * (mCurrentTime - 0.5f) / 0.3f);
                    p2.setMc(mc + (mCurrentTime - 0.5f) * mc / 4 / 0.3f);//p2.mc间距变大
                    p4.setMc(mc + (mCurrentTime - 0.5f) * mc / 4 / 0.3f);//p4.mc间距变大
                } else {
                    p1.setX(-0.5f * radius - 0.5f * radius * (mCurrentTime - 0.5f) / 0.3f);
                    p3.setX(-0.5f * radius - 0.5f * radius * (mCurrentTime - 0.5f) / 0.3f);
                    p2.setMc(mc + (mCurrentTime - 0.5f) * mc / 4 / 0.3f);//p2.mc间距变大
                    p4.setMc(mc + (mCurrentTime - 0.5f) * mc / 4 / 0.3f);//p4.mc间距变大
                }
            } else if (mCurrentTime > 0.8 && mCurrentTime <= 0.9) {
                p2.setMc(mc);//mc回复呈圆形
                p4.setMc(mc);//mc回复呈圆形
                canvas.translate(startX + (mCurrentTime - 0.2f) * distance / 0.7f, startY);//设置圆点位置
                if (toPos > currentPos) {
                    p4.setX(-radius + 1.6f * radius * (mCurrentTime - 0.8f) / 0.1f);
                } else {
                    p2.setX(radius - 1.6f * radius * (mCurrentTime - 0.8f) / 0.1f);
                }
            } else if (mCurrentTime > 0.9 && mCurrentTime < 1) {
                canvas.translate(startX + distance, startY);//设置圆点位置
                if (toPos > currentPos) {
                    p1.setX(radius);
                    p3.setX(radius);
                    p4.setX(0.6f * radius - 0.6f * radius * (mCurrentTime - 0.9f) / 0.1f);
                } else {
                    p1.setX(-radius);
                    p3.setX(-radius);
                    p2.setX(-0.6f * radius + 0.6f * radius * (mCurrentTime - 0.9f) / 0.1f);
                }
            }
            //结束回复圆形状态
            if (mCurrentTime == 1) {
                lastCurrentTime = 0;
                canvas.translate(startX + distance, startY);
                if (direction) {
                    p1.setX(radius);
                    p3.setX(radius);
                    p4.setX(0);
                } else {
                    p1.setX(-radius);
                    p3.setX(-radius);
                    p2.setX(0);
                }
                currentPos = toPos;
                resetP();
                if (direction)
                    canvas.translate(radius, 0);
                else
                    canvas.translate(-radius, 0);
            }
            mPath.moveTo(p1.x, p1.y);//起始位置下中点
            mPath.cubicTo(p1.right.x, p1.right.y, p2.bottom.x, p2.bottom.y, p2.x, p2.y);//右下弧
            mPath.cubicTo(p2.top.x, p2.top.y, p3.right.x, p3.right.y, p3.x, p3.y);//右上弧
            mPath.cubicTo(p3.left.x, p3.left.y, p4.top.x, p4.top.y, p4.x, p4.y);//左上弧
            mPath.cubicTo(p4.bottom.x, p4.bottom.y, p1.left.x, p1.left.y, p1.x, p1.y);//左下弧
            canvas.drawPath(mPath, selectorPaint);//绘制选中器
        }
    
    
        //更新选中器状态
        private void updateDrop(Canvas c, int position, float positionOffset) {
            //判断滑动方向,确定移动位置toPos
            if ((position + positionOffset) - currentPos > 0)//正
                direction = true;
            else if ((position + positionOffset) - currentPos < 0)//反
                direction = false;
            if (direction)
                toPos = currentPos + 1;
            else
                toPos = currentPos - 1;
            //更新圆点
            startX = firstX + radius + (currentPos) * (div + 2 * radius);
            //更新位移
            distance = direction ? ((2 * radius + div) + (direction ? -radius : radius)) : (-(2 * radius + div) + (direction ? -radius : radius));
            //百分比
            mCurrentTime = position + positionOffset - (int) (position + positionOffset);
            if (!direction)
                mCurrentTime = 1 - mCurrentTime;
            if (Math.abs(lastCurrentTime - mCurrentTime) > 0.2) {
                if (lastCurrentTime < 0.1)
                    mCurrentTime = 0;
                else if (lastCurrentTime > 0.9)
                    mCurrentTime = 1;
            }
            lastCurrentTime = mCurrentTime;
            dispatchDraw(c);
        }
    
        @Override
        public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDrawOver(c, parent, state);
            //绘制选中器
            LinearLayoutManager layoutManager = (LinearLayoutManager) parent.getLayoutManager();
            int activePosition = layoutManager.findFirstVisibleItemPosition();
            if (activePosition == RecyclerView.NO_POSITION) {
                return;
            }
            // find offset of active page (if the user is scrolling)
            final View activeChild = layoutManager.findViewByPosition(activePosition);
            int left = activeChild.getLeft();
            int width = activeChild.getWidth();
            // on swipe the active item will be positioned from [-width, 0]
            // interpolate offset for smooth animation
            float progress = mInterpolator.getInterpolation(left * -1 / (float) width);
            //更新选中点
            updateDrop(c, activePosition, progress);
            Log.e(TAG, "方法:onDrawOver");
        }
    
        @Override
        public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
            super.onDraw(c, parent, state);
            //获取个数
            indicatorCount = parent.getAdapter().getItemCount();
            //指示圆点所占的长度
            float totalLength = radius * 2 * indicatorCount;
            //间距所占长度
            float paddingBetweenItems = Math.max(0, indicatorCount - 1) * div;
            //指示器总长度
            float indicatorTotalWidth = totalLength + paddingBetweenItems;
            //居中指示器开始位置
            firstX = (parent.getWidth() - indicatorTotalWidth) / 2F;
            //高度的中点
            startY = parent.getHeight() - mIndicatorHeight / 2;
            //绘制指示器
            drawInactiveIndicators(c);
            Log.e(TAG, "方法:onDraw");
        }
    
        //绘制指示器
        private void drawInactiveIndicators(Canvas c) {
            for (int i = 0; i < indicatorCount; i++) {
                c.drawCircle(firstX + radius + i * (div + 2 * radius), startY, radius, indicatorPaint);
            }
        }
    
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
            //预留出指示器高度
            outRect.bottom = mIndicatorHeight;
            Log.e(TAG, "方法:getItemOffsets");
        }
    
    
        class XPoint {//用来存储X轴相等的做坐标点,这里用来存p2,p4,其中p2代表左面3个点,p4代表右面三个点
            public float x;//中间点X坐标
            public float y;//中间点Y坐标
            public float mc;//上下到中间的间距
            public PointF bottom;//下面点
            public PointF top;//上面点
    
            public XPoint(float x, float y, float mc) {
                this.x = x;
                this.y = y;
                this.mc = mc;
                if (bottom == null)
                    bottom = new PointF();
                if (top == null)
                    top = new PointF();
                bottom.y = y + mc;
                top.y = y - mc;
                bottom.x = x;
                top.x = x;
            }
    
            public void setMc(float mc) {
                this.mc = mc;
                bottom.y = y + mc;
                top.y = y - mc;
            }
    
            public void setY(float y) {
                this.y = y;
                bottom.y = y + mc;
                top.y = y - mc;
            }
    
            public void setX(float x) {
                this.x = x;
                bottom.x = x;
                top.x = x;
            }
    
            @Override
            public String toString() {
                return "XPoint{" +
                        "x=" + x +
                        ", y=" + y +
                        ", mc=" + mc +
                        ", bottom=" + bottom +
                        ", top=" + top +
                        '}';
            }
        }
    
        class YPoint {//用来存储Y轴相等的做坐标点,这里用来存p1,p3,其中p1代表上面3个点,p3代表下面三个点
            public float x;//中间点X坐标
            public float y;//中间点坐标
            public float mc;//左右距中点间距
            public PointF left;//左点
            public PointF right;//右点
    
            public YPoint(float x, float y, float mc) {
                this.x = x;
                this.y = y;
                this.mc = mc;
                if (left == null)
                    left = new PointF();
                if (right == null)
                    right = new PointF();
                right.x = x + mc;
                left.x = x - mc;
                left.y = y;
                right.y = y;
            }
    
            public void setMc(float mc) {
                this.mc = mc;
                right.x = x + mc;
                left.x = x - mc;
            }
    
            public void setX(float x) {
                this.x = x;
                right.x = x + mc;
                left.x = x - mc;
            }
    
            public void setY(float y) {
                this.y = y;
                left.y = y;
                right.y = y;
            }
    
            @Override
            public String toString() {
                return "YPoint{" +
                        "x=" + x +
                        ", y=" + y +
                        ", mc=" + mc +
                        ", left=" + left +
                        ", right=" + right +
                        '}';
            }
        }
    }
    

    相关文章

      网友评论

          本文标题:RecyclerView.ItemDecoration实现指示器

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