美文网首页Android知识
神奇的水滴效果导航栏-BezierIndicator

神奇的水滴效果导航栏-BezierIndicator

作者: 晨鸣code | 来源:发表于2017-07-20 14:31 被阅读34次

    晨鸣的博客--神奇的水滴效果导航栏-BezierIndicator

    很早之前就看见过这样一个特效


    心怡很久,却一直恐于自定义View这座大山。最近在突击自定义View的技能,学习贝塞尔曲线的绘制,前面搞了个很简单的MagicButton,甚是兴奋😄 所以斗胆来试试看实现这个特效。

    分析

    找了半天终于找到当初看见的这个特效的原博客 --三次贝塞尔曲线练习之弹性的圆

    另外在评论中发现竟然有人已经实现了这个自定义View了--自定义View之炫酷的水滴ViewPageIndicator,效果很不错,借鉴之😄

    关于最核心的贝塞尔小球动效的绘制,博主进行了很详细的解析及描述,并且提供了一个demo,万分感谢😄

    这里简单回顾一下这个小球的绘制过程:

    为了控制小球的不同形态,我们这里使用三阶贝塞尔曲线cubicTo来绘制小球。

    贝塞尔小球

    而小球一共可以分成5个状态来绘制

    状态1

    状态1

    状态2

    状态2

    状态3

    状态3

    状态4

    状态4

    状态5

    状态5

    绘制

    计算控件宽高

    作为一个导航控件,我暂时不考虑宽度设置为warp_content的状态,设置wrap_content一律计算为屏幕的最大宽高.

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    
            WindowManager wm = (WindowManager) getContext()
                    .getSystemService(Context.WINDOW_SERVICE);
            /**
             * 获得此ViewGroup上级容器为其推荐的宽和高,以及计算模式
             */
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
            int sizeWidth = MeasureSpec.getSize(widthMeasureSpec);
            int sizeHeight = MeasureSpec.getSize(heightMeasureSpec);
    
            if (widthMode == MeasureSpec.EXACTLY) {
                width = sizeWidth;
            } else {
                width = wm.getDefaultDisplay().getWidth();
            }
    
            if (heightMode == MeasureSpec.EXACTLY) {
                height = sizeHeight;
            } else {
                height = wm.getDefaultDisplay().getHeight();
            }
    
            if (getChildCount() != 0) {
                childSideLength = (width - getPaddingRight() - getPaddingLeft()) / getChildCount() > height - getPaddingBottom() - getPaddingTop() ? height - getPaddingBottom() - getPaddingTop() : (width - getPaddingLeft() - getPaddingRight()) / getChildCount();
    //        //计算出所有的ChildView的宽和高
    //            measureChildren(widthMeasureSpec, heightMeasureSpec);
                bezierCircular = new BezierCircular(childSideLength / 2);
            }
    
            setMeasuredDimension(width, height);
        }
    
    

    计算子控件的位置

    为了方便管理,子View的大小统一计算为一个正方形区域,设置一个子View的padding值childPadding,可以通过childPadding值控制我们添加的子view呈现出的大小,也就是效果图中小图标在白色圆环中的大小。

     @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            int childCount = getChildCount();
            if (childCount == 0) {
                return;
            }
            //相邻两个子View中心点的间距
            float childDis = (width - getPaddingLeft() - getPaddingRight() - 2 * defaultLeftRightGap - childSideLength) / (childCount - 1);
            float cWidth = childSideLength - 2 * childPadding;
            float cHeight = cWidth;
    
            anchorList.clear();
            //计算子控件的位置,强制将子View控制绘制在均分的几个锚点上
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);
                PointF anchorPoint = new PointF((childDis * i + defaultLeftRightGap + childSideLength / 2 + getPaddingLeft()), getPaddingTop() + childSideLength / 2);
                anchorList.add(anchorPoint);
                childView.layout((int) (anchorPoint.x - cWidth / 2), (int) (anchorPoint.y - cHeight / 2), (int) (anchorPoint.x + cWidth / 2), (int) (anchorPoint.y + cHeight / 2));
            }
            PointF pointF = anchorList.get(0);
            bezierCircular.setCenter(pointF.x, pointF.y);
            bezierCircular.initControlPoint();
        }
    

    绘制贝塞尔小球

    将贝塞尔小球的一些参数及计算封装成一个对象BezierCircular,因为刚开始只是看了原博客的思路就动手了,绘制贝塞尔小球使用了最原始的方法,定义了4个数据点和8个控制点,在进行五个状态的绘制计算的时候太麻烦了,后面看了博客中的Demo,发现自己的计算太原始笨重了,博客中的demo中关于小球的绘制更加面向对象,更加简洁。不过既然是原创,还是要贴出自己的代码,仅供参考😄

    public class BezierCircular {
        private static final String TAG = "BezierCircular";
    
        private static final float C = 0.551915024494f; //常量
    
    
        //圆中心坐标
        float centerX;
        float centerY;
    
        //圆半径
        float radius;
    
        private PointF currentPoint;
        private PointF targetPoint;
        private float mDifference;
    
        private float stretchDistance;
        private float cDistance;
    
        private float moveDistance;
    
        private float[] mData = new float[8];  //顺时针记录绘制圆形的四个数据点
        private float[] mCtrl = new float[16];  //顺时针记录绘制圆形的八个控制点
    
        public BezierCircular(float radius) {
            this.radius = radius;
            stretchDistance = radius / 3 * 2;
            mDifference = radius * C;
            cDistance = mDifference * 0.45f;
        }
    
        public void setCenter(float centerX, float centerY) {
            this.centerX = centerX;
            this.centerY = centerY;
    
        }
    
    
     
        public void initControlPoint() {
    
            //初始化数据点
            mData[0] = centerX;
            mData[1] = centerY + radius;
    
            mData[2] = centerX + radius;
            mData[3] = centerY;
    
            mData[4] = centerX;
            mData[5] = centerY - radius;
    
            mData[6] = centerX - radius;
            mData[7] = centerY;
    
            //初始化控制点
            mCtrl[0] = mData[0] + mDifference;
            mCtrl[1] = mData[1];
    
            mCtrl[2] = mData[2];
            mCtrl[3] = mData[3] + mDifference;
    
            mCtrl[4] = mData[2];
            mCtrl[5] = mData[3] - mDifference;
    
            mCtrl[6] = mData[4] + mDifference;
            mCtrl[7] = mData[5];
    
            mCtrl[8] = mData[4] - mDifference;
            mCtrl[9] = mData[5];
    
            mCtrl[10] = mData[6];
            mCtrl[11] = mData[7] - mDifference;
    
            mCtrl[12] = mData[6];
            mCtrl[13] = mData[7] + mDifference;
    
            mCtrl[14] = mData[0] - mDifference;
            mCtrl[15] = mData[1];
        }
    
    
        public void setCurrentAndTarget(PointF currentPoint, PointF targetPoint) {
            this.currentPoint = currentPoint;
            this.targetPoint = targetPoint;
            float distance = targetPoint.x - currentPoint.x;
            moveDistance = distance > 0 ? distance - 2 * stretchDistance : distance + 2 * stretchDistance;
        }
    
        public void setProgress(float progress) {
            if ((progress > 0 && progress <= 0.2) || (progress < 0 && progress >= -0.2)) {
                model1(progress);
            } else if ((progress > 0.2 && progress <= 0.5) || (progress < -0.2 && progress >= -0.5)) {
                model2(progress);
            } else if ((progress > 0.5 && progress <= 0.8) || (progress < -0.5 && progress >= -0.8)) {
                model3(progress);
            } else if ((progress > 0.8 && progress <= 0.9) || (progress < -0.8 && progress >= -0.9)) {
                model4(progress);
            } else if ((progress > 0.9 && progress < 1) || (progress < -0.9 && progress > -1)) {
                model5(progress);
            }
    //        } else if (progress >= 1 || progress <= -1) {
    //            Log.i(TAG,"-------------------------------------------");
    ////            centerX = targetPoint.x;
    ////            centerY = targetPoint.y;
    ////            initControlPoint();
    //        }
        }
    
    
        public void model1(float progress) {
            if (progress > 0)
                mData[2] = centerX + radius + stretchDistance * progress * 5;
    
            if (progress < 0)
                mData[6] = centerX - radius + stretchDistance * progress * 5;
    
            mCtrl[2] = mData[2];
            if (progress > 0)
                mCtrl[3] = mData[3] + mDifference + cDistance * progress * 5;
    
            mCtrl[4] = mData[2];
            if (progress > 0)
                mCtrl[5] = mData[3] - mDifference - cDistance * progress * 5;
    
            mCtrl[10] = mData[6];
            if (progress < 0)
                mCtrl[11] = mData[7] - mDifference + cDistance * progress * 5;
    
            mCtrl[12] = mData[6];
            if (progress < 0)
                mCtrl[13] = mData[7] + mDifference - cDistance * progress * 5;
        }
    
        public void model2(float progress) {
            model1(progress > 0 ? 0.2f : -0.2f);
    
            progress = progress > 0 ? (progress - 0.2f) * (10f / 3) : (progress + 0.2f) * (10f / 3);
            //初始化数据点
            mData[0] = centerX + stretchDistance * progress;
    
            if (progress > 0)
                mData[2] = centerX + radius + stretchDistance * (1 + progress);
            else
                mData[2] = centerX + radius;
    
    
            mData[4] = centerX + stretchDistance * progress;
    
            if (progress < 0)
                mData[6] = centerX - radius - stretchDistance + stretchDistance * progress;
            else
                mData[6] = centerX - radius;
    
    
            //初始化控制点
            mCtrl[0] = mData[0] + mDifference;
    
            mCtrl[2] = mData[2];
            if (progress > 0)
                mCtrl[3] = mData[3] + mDifference + cDistance;
            else
                mCtrl[3] = mData[3] + mDifference - cDistance * progress;
    
            mCtrl[4] = mData[2];
            if (progress > 0)
                mCtrl[5] = mData[3] - mDifference - cDistance;
            else
                mCtrl[5] = mData[3] - mDifference + cDistance * progress;
    
    
            mCtrl[6] = mData[4] + mDifference;
    
            mCtrl[8] = mData[4] - mDifference;
    
            mCtrl[10] = mData[6];
            if (progress > 0)
                mCtrl[11] = mData[7] - mDifference - cDistance * progress;
            else
                mCtrl[11] = mData[7] - mDifference - cDistance;
    
            mCtrl[12] = mData[6];
            if (progress > 0)
                mCtrl[13] = mData[7] + mDifference + cDistance * progress;
            else
                mCtrl[13] = mData[7] + mDifference + cDistance;
    
    
            mCtrl[14] = mData[0] - mDifference;
        }
    
        public void model3(float progress) {
            model2(progress > 0 ? 0.5f : -0.5f);
            progress = progress > 0 ? (progress - 0.5f) * (10f / 3) : (progress + 0.5f) * (10f / 3);
    
            //初始化数据点
            if (progress > 0)
                mData[0] = centerX + moveDistance * progress + stretchDistance;
            else
                mData[0] = centerX - moveDistance * progress - stretchDistance;
    
    
            if (progress > 0)
                mData[2] = centerX + moveDistance * progress + radius + 2 * stretchDistance;
            else
                mData[2] = centerX - moveDistance * progress + radius;
    
    
            if (progress > 0)
                mData[4] = centerX + moveDistance * progress + stretchDistance;
            else
                mData[4] = centerX - moveDistance * progress - stretchDistance;
    
    
            if (progress > 0)
                mData[6] = centerX + moveDistance * progress - radius;
            else
                mData[6] = centerX - moveDistance * progress - radius - 2 * stretchDistance;
    
            //初始化控制点
            mCtrl[0] = mData[0] + mDifference;
    
            mCtrl[2] = mData[2];
            mCtrl[3] = mData[3] + mDifference + cDistance;
    
            mCtrl[4] = mData[2];
            mCtrl[5] = mData[3] - mDifference - cDistance;
    
            mCtrl[6] = mData[4] + mDifference;
    
            mCtrl[8] = mData[4] - mDifference;
    
            mCtrl[10] = mData[6];
            mCtrl[11] = mData[7] - mDifference - cDistance;
    
            mCtrl[12] = mData[6];
            mCtrl[13] = mData[7] + mDifference + cDistance;
    
            mCtrl[14] = mData[0] - mDifference;
        }
    
        public void model4(float progress) {
    
    
            model3(progress > 0 ? 0.8f : -0.8f);
    
            progress = progress > 0 ? (progress - 0.8f) * 10 : (progress + 0.8f) * 10;
    
            //初始化数据点
            if (progress > 0)
                mData[0] = centerX + moveDistance + stretchDistance + stretchDistance * progress;
            else
                mData[0] = centerX + moveDistance - stretchDistance + stretchDistance * progress;
    
            if (progress > 0)
                mData[2] = centerX + moveDistance + radius + 2 * stretchDistance;
            else
                mData[2] = centerX + moveDistance + radius + stretchDistance * progress;
    
            if (progress > 0)
                mData[4] = centerX + moveDistance + stretchDistance + stretchDistance * progress;
            else
                mData[4] = centerX + moveDistance - stretchDistance + stretchDistance * progress;
    
            if (progress > 0)
                mData[6] = centerX + moveDistance - radius + stretchDistance * progress;
            else
                mData[6] = centerX + moveDistance - radius - 2 * stretchDistance;
    
            //初始化控制点
            mCtrl[0] = mData[0] + mDifference;
    
            mCtrl[2] = mData[2];
            if (progress > 0)
                mCtrl[3] = mData[3] + mDifference + cDistance - cDistance * progress;
            else
                mCtrl[3] = mData[3] + mDifference + cDistance;
    
            mCtrl[4] = mData[2];
            if (progress > 0)
                mCtrl[5] = mData[3] - mDifference - cDistance + cDistance * progress;
            else
                mCtrl[5] = mData[3] - mDifference - cDistance;
    
            mCtrl[6] = mData[4] + mDifference;
    
            mCtrl[8] = mData[4] - mDifference;
    
            mCtrl[10] = mData[6];
            if (progress > 0)
                mCtrl[11] = mData[7] - mDifference - cDistance;
            else
                mCtrl[11] = mData[7] - mDifference - cDistance - cDistance * progress;
    
            mCtrl[12] = mData[6];
            if (progress > 0)
                mCtrl[13] = mData[7] + mDifference + cDistance;
            else
                mCtrl[13] = mData[7] + mDifference + cDistance + cDistance * progress;
    
            mCtrl[14] = mData[0] - mDifference;
        }
    
        public void model5(float progress) {
            model4(progress > 0 ? 0.9f : -0.9f);
    
            progress = progress > 0 ? (progress - 0.9f) * 10 : (progress + 0.9f) * 10;
    
            //初始化数据点
            if (progress > 0)
                mData[0] = centerX + moveDistance + 2 * stretchDistance;
            else
                mData[0] = centerX + moveDistance - 2 * stretchDistance;
    
            if (progress > 0)
                mData[2] = centerX + moveDistance + radius + 2 * stretchDistance;
            else
                mData[2] = (float) (centerX + moveDistance + radius - stretchDistance - (Math.sin(Math.PI * 3 / 2 * Math.abs(progress) - Math.PI / 2) + 1) * stretchDistance);
    
    
            if (progress > 0)
                mData[4] = centerX + moveDistance + 2 * stretchDistance;
            else
                mData[4] = centerX + moveDistance - 2 * stretchDistance;
    
            if (progress > 0)
                mData[6] = (float) (centerX + moveDistance - radius + stretchDistance + (Math.sin(Math.PI * 3 / 2 * progress - Math.PI / 2) + 1) * stretchDistance);
            else
                mData[6] = centerX + moveDistance - radius - 2 * stretchDistance;
    
    
            //初始化控制点
            mCtrl[0] = mData[0] + mDifference;
    
            mCtrl[2] = mData[2];
            if (progress < 0)
                mCtrl[3] = mData[3] + mDifference + cDistance + cDistance * progress;
    
            mCtrl[4] = mData[2];
            if (progress < 0)
                mCtrl[5] = mData[3] - mDifference - cDistance - cDistance * progress;
    
            mCtrl[6] = mData[4] + mDifference;
    
            mCtrl[8] = mData[4] - mDifference;
    
            mCtrl[10] = mData[6];
            if (progress > 0)
                mCtrl[11] = mData[7] - mDifference - cDistance + cDistance * progress;
    
            mCtrl[12] = mData[6];
            if (progress > 0)
                mCtrl[13] = mData[7] + mDifference + cDistance - cDistance * progress;
    
            mCtrl[14] = mData[0] - mDifference;
        }
    
    
    
        public void drawCircle(Canvas canvas, Paint mPaint) {
            Path path = new Path();
            path.moveTo(mData[0], mData[1]);
    
            path.cubicTo(mCtrl[0], mCtrl[1], mCtrl[2], mCtrl[3], mData[2], mData[3]);
            path.cubicTo(mCtrl[4], mCtrl[5], mCtrl[6], mCtrl[7], mData[4], mData[5]);
            path.cubicTo(mCtrl[8], mCtrl[9], mCtrl[10], mCtrl[11], mData[6], mData[7]);
            path.cubicTo(mCtrl[12], mCtrl[13], mCtrl[14], mCtrl[15], mData[0], mData[1]);
    
            canvas.drawPath(path, mPaint);
        }
    
    
        public void resetCircular(PointF pointF) {
            setCenter(pointF.x, pointF.y);
            initControlPoint();
        }
    
    }
    

    确定子View点击位置

    通过OnTouchEvent 方法计算触摸点在哪个子View的绘制范围内,确定点击位置

        float touchX = 0;
        float touchY = 0;
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            switch (event.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    touchX = event.getX();
                    touchY = event.getY();
                    break;
    
                case MotionEvent.ACTION_UP:
                    Log.i(TAG, "touchX: " + touchX + "  touchY: " + touchY);
                    for (int i = 0; i < anchorList.size(); i++) {
                        PointF pointF = anchorList.get(i);
                        if (touchX > (pointF.x - childSideLength / 2) && touchX < (pointF.x + childSideLength / 2) && touchY > (pointF.y - childSideLength / 2) && touchY < (pointF.y + childSideLength / 2)) {
                            onClickIndex(i);
                        }
                    }
                    break;
            }
            return true;
        }
    
    
        private void onClickIndex(int position) {
            if (!isAnimatorStart && !isViewPagerScoll && position != currentPosition) {
                targetPosition = position;
                isAnimatorStart = true;
                startAnimator(); //开始动画
                clickAnimator(); //点击效果
                if (viewPager != null) {
                    viewPager.setCurrentItem(position);
                }
    //            currentPosition = position;
                Log.i(TAG, "点击了第 " + position + " 项!");
            }
        }
    
    

    点击切换动画

    通过ValueAnimator动态更改贝塞尔小球的绘制进度

      /**
         * 切换动画
         */
        private void startAnimator() {
            bezierCircular.setCurrentAndTarget(anchorList.get(currentPosition), anchorList.get(targetPosition));
            ValueAnimator valueAnimator = ValueAnimator.ofFloat(0, targetPosition > currentPosition ? 1 : -1);
            valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animation) {
                    bezierCircular.setProgress((Float) animation.getAnimatedValue());
                    bezierPaint.setColor(circularColors.size() > 0 ? setCircularColor(Math.abs((Float) animation.getAnimatedValue())) : circularColor);
                    postInvalidate();
                }
            });
    
            valueAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    currentPosition = targetPosition;
                    bezierPaint.setColor(circularColors.size() > 0 ? circularColors.get(currentPosition) : circularColor);
                    bezierCircular.resetCircular(anchorList.get(currentPosition));
                    isAnimatorStart = false;
                    postInvalidate();
                    super.onAnimationEnd(animation);
                }
            });
    
            int count = Math.abs(targetPosition - currentPosition);
            if (count == 0) {
                return;
            }
            int duration = 600;
            valueAnimator.setDuration(duration);
            valueAnimator.start();
        }
    

    与ViewPager联动

    与ViewPager的联动这一块挺头疼的,ViewPager滚动过程中设置滑动监听 void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) 回调方法中的 positionOffset 参数,在从左往右滑是0~1逐渐增大,但是最后又会突变到0。而且 void onPageSelected(int position)回调方法并不是在ViewPager滑动结束的时候调用,而是在你的手指离开时调用,有可能ViewPager还在惯性滑动的时候void onPageSelected(int position)方法已经调用了,所以也没办法通过这个回调来确定 currentPositontargetPosition

    通过观察,ViewPager的滑动监听 void onPageScrollStateChanged(int state)回调方法中有三个状态

    1. state == 1 表示正在滑动
    2. state == 2 表示滑动结束
    3. state == 0 表示什么都没有做

    这里的滑动指的是手指在屏幕上的滑动,而当ViewPager惯性滑动结束时 state == 0,所以最后决定在void onPageScrollStateChanged(int state)方法中进行相关处理。

     public void setViewPager(ViewPager viewPager) {
            this.viewPager = viewPager;
    
            viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
                @Override
                public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                    if (anchorList != null && anchorList.size() > 0 && !isAnimatorStart) {
                        isViewPagerScoll = true;
                        updateDrop(position, positionOffset, positionOffsetPixels);
                    }
                    // 页面正在滚动时不断调用
                    Log.d(TAG, "onPageScrolled————>" + "    position:" + position + "    positionOffest:" + positionOffset + "    positionOffsetPixels:" + positionOffsetPixels);
                }
    
                @Override
                public void onPageSelected(int position) {
                    Log.e(TAG, "onPagerSelected————>    position:" + position);
                    isSelected = true;
                }
    
                @Override
                public void onPageScrollStateChanged(int state) {
                    if (state == 0 && isSelected && !isAnimatorStart) {
    //                    Log.e(TAG, "onPageScrollStateChanged————>    设置状态:");
                        isSelected = false;
                        isViewPagerScoll = false;
                        bezierCircular.setProgress(direction ? 1.0f : -1.0f);
                        currentPosition = targetPosition;
    
    //                    Log.i(TAG, "currentPosition::::" + currentPosition);
                        bezierPaint.setColor(circularColors.size() > 0 ? circularColors.get(currentPosition) : circularColor);
                        bezierCircular.resetCircular(anchorList.get(currentPosition));
                        postInvalidate();
                    }
                    Log.i(TAG, "onPageScrollStateChanged————>    state:" + state);
                }
            });
        }
    
    
        float lastProgress = 0;
        float currentProgress = 0;
    
    
        //滑动ViewPager时更新指示器的动画
        private void updateDrop(int position, float positionOffset, int positionOffsetPixels) {
    
            if ((position + positionOffset) - currentPosition > 0) {
                direction = true;
            } else if ((position + positionOffset) - currentPosition < 0) {
                direction = false;
            }
    
            //防止数组越界
            if ((!direction && currentPosition - 1 < 0) || (direction && currentPosition + 1 > getChildCount() - 1)) {
                return;
            }
    
            if (direction) targetPosition = currentPosition + 1;
            else targetPosition = currentPosition - 1;
    
            currentProgress = positionOffset;
    
    //        Log.e(TAG, "direction:::" + direction + "     currentPosition:::" + currentPosition + "     targetPosition:::" + targetPosition);
            bezierCircular.setCurrentAndTarget(anchorList.get(currentPosition), anchorList.get(targetPosition));
    
            if (currentProgress == 0 && lastProgress > 0.9) {
                if (lastProgress > 0.9) {
                    currentProgress = 1;
                }
                if (lastProgress < 0.1) {
                    currentProgress = 0;
                }
            }
    
            bezierCircular.setProgress(direction ? currentProgress : currentProgress - 1);
            bezierPaint.setColor(circularColors.size() > 0 ? setCircularColor(direction ? currentProgress : 1 - currentProgress) : circularColor);
            invalidate();
            lastProgress = currentProgress;
        }
    
    

    onDraw(Canvas canvas)

    onDraw方法中代码就很少了

        @Override
        protected void onDraw(Canvas canvas) {
    
            drawChildBg(canvas);
            bezierCircular.drawCircle(canvas, bezierPaint);
            drawClick(canvas);
            super.onDraw(canvas);
        }
    

    附上子View背景绘制,及点击效果绘制代码

       //绘制子View的背景
        private void drawChildBg(Canvas canvas) {
            if (anchorList == null || anchorList.size() == 0) {
                Log.i(TAG, "锚点位置为空");
                return;
            }
    
            for (int i = 0; i < anchorList.size(); i++) {
                PointF pointF = anchorList.get(i);
                canvas.drawCircle(pointF.x, pointF.y, (childSideLength - 4) / 2, childBgPaint);
            }
        }
    
        //绘制点击效果
        private void drawClick(Canvas canvas) {
            PointF pointF = anchorList.get(targetPosition);
    
            canvas.drawCircle(pointF.x, pointF.y, clickRadius, clickPaint);
        }
    

    效果

    最终效果如下,可能与原概念图有些差距,但也算小有成就吧😄

    附上github地址:https://github.com/lichenming0516/BezierIndicator

    效果图

    小结

    通过这两次自定义View的学习尝试,让自己对自定义View的绘制流程有了更深刻的了解,一些常见方法onMeasure()onLayout()onDraw()以及自定义属性的解析理解的更清晰一点。对于自定义View这座大山应该能算的上爬上半山腰了吧 😄

    相关文章

      网友评论

        本文标题:神奇的水滴效果导航栏-BezierIndicator

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