美文网首页
自定义View-2Path贝塞尔曲线

自定义View-2Path贝塞尔曲线

作者: 玄策 | 来源:发表于2017-05-26 11:31 被阅读24次

    Path贝塞尔曲线

    贝塞尔曲线用途广泛

    QQ消息小红点拖拽效果
    炫酷的下拉控件
    翻书效果

    类型 作用
    数据点 确定曲线的起始和结束为止
    控制点 确定曲线的弯曲程度

    一阶曲线是没有控制点的,只有两个数据点连城一个线段。即lineTo()

    二阶曲线对应方法为quadTo()

    二阶曲线动效演示
    AD/AB = BE/BC

    代码:

    public class Bazier2View extends View{
    
        private PointF start,end,control;
        private int centerX,centerY;
        private Paint mPaint = new Paint();
        public Bazier2View(Context context, AttributeSet attrs) {
            super(context, attrs);
            mPaint.setStyle(Paint.Style.STROKE);
            start = new PointF(0,0);
            end = new PointF(0,0);
            control = new PointF();
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            centerX = w/2;
            centerY = h/2;
    
            start.x = centerX - 100;
            start.y = centerY;
            end.x = centerX + 100;
            end.y = centerY;
            control.x = centerX;
            control.y = centerY - 100;
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
    //        return super.onTouchEvent(event);
            control.x = event.getX();
            control.y = event.getY();
            invalidate();
            return true;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
    
            mPaint.setStrokeWidth(12);
            mPaint.setColor(Color.BLACK);
            canvas.drawPoint(start.x,start.y,mPaint);
            canvas.drawPoint(end.x,end.y,mPaint);
            canvas.drawPoint(control.x,control.y,mPaint);
    
            mPaint.setStrokeWidth(2);
            mPaint.setColor(Color.GRAY);
            //绘制辅助线
            canvas.drawLine(start.x,start.y,control.x,control.y,mPaint);
            canvas.drawLine(end.x,end.y,control.x,control.y,mPaint);
            mPaint.setStrokeWidth(4);
            mPaint.setColor(Color.RED);
            Path path = new Path();
            path.moveTo(start.x,start.y);
            path.quadTo(control.x,control.y,end.x,end.y);
            canvas.drawPath(path,mPaint);
        }
    }
    

    结果:


    quadTo()

    三阶曲线对应的方法为cubicTo()。由两个数据点A和D,两个控制点B和C



    代码:

    public class Bazier3View extends View {
        private static final String TAG = "Bazier3View";
        private Paint mPaint;
        private boolean mode;
        private PointF start,end,control1,control2;
        public Bazier3View(Context context, AttributeSet attrs) {
            super(context, attrs);
            mPaint = new Paint();
            mPaint.setStyle(Paint.Style.STROKE);
            start = new PointF();
            end = new PointF();
            control1 = new PointF();
            control2 = new PointF();
        }
    
        public void setMode(boolean mode){
            this.mode = mode;
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            Log.i(TAG, "onSizeChanged: ");
            int centerX = w/2;
            int centerY = h/2;
            start.x = centerX-100;
            start.y = centerY;
            end.x = centerX+100;
            end.y = centerY;
            control1.x = centerX-110;
            control1.y = centerY-100;
            control2.x = centerX+110;
            control2.y = centerY-100;
    
        }
    
        @Override
        public boolean onTouchEvent(MotionEvent event) {
            if (mode){
                control1.x = event.getX();
                control1.y = event.getY();
            }else{
                control2.x = event.getX();
                control2.y = event.getY();
            }
            invalidate();
            return true;
    //        return super.onTouchEvent(event);
        }
    
        @Override
        public boolean dispatchTouchEvent(MotionEvent event) {
            //因为实际布局中,此view被包含在ScrollView中,
            // 调用此方法告知父View,由我本身处理touch事件
            //可对比二阶贝塞尔View(Bazier2View)在ScrollView中的效果
            getParent().requestDisallowInterceptTouchEvent(true);
            return super.dispatchTouchEvent(event);
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            mPaint.setColor(Color.BLACK);
            mPaint.setStrokeWidth(12);
            canvas.drawPoint(start.x,start.y,mPaint);
            canvas.drawPoint(end.x,end.y,mPaint);
            canvas.drawPoint(control1.x,control1.y,mPaint);
            canvas.drawPoint(control2.x,control2.y,mPaint);
    
            mPaint.setColor(Color.WHITE);
            mPaint.setStrokeWidth(4);
            canvas.drawLine(start.x,start.y,control1.x,control1.y,mPaint);
            canvas.drawLine(control1.x,control1.y,control2.x,control2.y,mPaint);
            canvas.drawLine(control2.x,control2.y,end.x,end.y,mPaint);
    
            mPaint.setColor(Color.RED);
            Path path = new Path();
            path.moveTo(start.x,start.y);
            path.cubicTo(control1.x,control1.y,control2.x,control2.y,end.x,end.y);
            canvas.drawPath(path,mPaint);
        }
    }
    

    结果:


    实例

    贝塞尔曲线的优点是可是实时控制曲线状态,并可以通过改变控制点的状态实时让曲线进行平滑的状态变化


    核心难点:
    1.如何得到数据点和控制点的位置?
    分析:关于使用绘制圆形的数据点与控制点早就已经有人详细的计算好了,可以参考下图
    2.如何达到渐变效果?
    分析:渐变其实就是每次对数据点和控制点稍微移动一点,然后重绘界面,在短时间多次的调整数据点与控制点,使其逐渐接近目标值,通过不断的重绘界面达到一种渐变的效果。过程可以参照下图动态效果:


    就是所需要的数值c约等于0.551915024494f

    图中假设1为圆的半径,那么对应的M值就应该是半径乘以0.551915024494f

    代码:

    public class BazierCircleView extends View {
        private static final String TAG = "BazierCircleView";
        private static final float C = 0.551915024494f;
        private Paint mPaint = new Paint();
        private PointF p0,p1,p2,p3,p4,p5,p6,p7,p8,p9,p10,p11;
        private int centerX,centerY;
        private int radius = 100;
        private float M = radius*C;
        private float mCurrent = 0;
        private float mCount = 10;
        public BazierCircleView(Context context, AttributeSet attrs) {
            super(context, attrs);
            mPaint.setStrokeWidth(6);
            mPaint.setStyle(Paint.Style.STROKE);
            p0 = new PointF(0,radius);
            p1 = new PointF(M,radius);
            p2 = new PointF(radius,M);
            p3 = new PointF(radius,0);
            p4 = new PointF(radius,-M);
            p5 = new PointF(M,-radius);
            p6 = new PointF(0,-radius);
            p7 = new PointF(-M,-radius);
            p8 = new PointF(-radius,-M);
            p9 = new PointF(-radius,0);
            p10 = new PointF(-radius,M);
            p11 = new PointF(-M,radius);
        }
    
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            centerX = w/2;
            centerY = h/2;
        }
    
        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            canvas.translate(getWidth()/2,getHeight()/2);//设置中心点
    
            drawPoint(canvas);
            drawLine(canvas);
            drawCoordinateSystem(canvas);
    
            mPaint.setColor(Color.BLUE);
            Path path = new Path();
            path.moveTo(p0.x,p0.y);
            path.cubicTo(p1.x,p1.y,p2.x,p2.y,p3.x,p3.y);
            path.cubicTo(p4.x,p4.y,p5.x,p5.y,p6.x,p6.y);
            path.cubicTo(p7.x,p7.y,p8.x,p8.y,p9.x,p9.y);
            path.cubicTo(p10.x,p10.y,p11.x,p11.y,p0.x,p0.y);
            canvas.drawPath(path,mPaint);
    
            mCurrent++;
            if (mCurrent < mCount){
                Log.i(TAG, "onDraw: ");
                p6.y += 5;
                p11.y -=5;
                p1.y -=5;
                p10.x += 2;
                p2.x -= 2;
                postInvalidateDelayed(20);
            }
        }
    
        private void drawPoint(Canvas canvas){
            mPaint.setStrokeWidth(6);
            mPaint.setColor(Color.RED);
            canvas.drawPoint(p0.x,p0.y,mPaint);
            canvas.drawPoint(p1.x,p1.y,mPaint);
            canvas.drawPoint(p2.x,p2.y,mPaint);
            canvas.drawPoint(p3.x,p3.y,mPaint);
            canvas.drawPoint(p4.x,p4.y,mPaint);
            canvas.drawPoint(p5.x,p5.y,mPaint);
            canvas.drawPoint(p6.x,p6.y,mPaint);
            canvas.drawPoint(p7.x,p7.y,mPaint);
            canvas.drawPoint(p8.x,p8.y,mPaint);
            canvas.drawPoint(p9.x,p9.y,mPaint);
            canvas.drawPoint(p10.x,p10.y,mPaint);
            canvas.drawPoint(p11.x,p11.y,mPaint);
        }
    
        private void drawLine(Canvas canvas){
            mPaint.setStrokeWidth(2);
            mPaint.setColor(Color.CYAN);
            canvas.drawLine(p11.x,p11.y,p1.x,p1.y,mPaint);
            canvas.drawLine(p2.x,p2.y,p4.x,p4.y,mPaint);
            canvas.drawLine(p5.x,p5.y,p7.x,p7.y,mPaint);
            canvas.drawLine(p8.x,p8.y,p10.x,p10.y,mPaint);
        }
    
        private void drawCoordinateSystem(Canvas canvas){
            mPaint.setColor(Color.RED);
            canvas.drawLine(0,-getHeight(),0,getHeight(),mPaint);
            canvas.drawLine(-getWidth(),0,getWidth(),0,mPaint);
        }
    

    相关文章

      网友评论

          本文标题:自定义View-2Path贝塞尔曲线

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