自定義View-2Path貝塞爾曲線

Path貝塞爾曲線

貝塞爾曲線用途廣泛

QQ消息小紅點拖拽效果
炫酷的下拉控件
翻書效果

類型 作用
數(shù)據(jù)點 確定曲線的起始和結(jié)束為止
控制點 確定曲線的彎曲程度

一階曲線是沒有控制點的,只有兩個數(shù)據(jù)點連城一個線段。即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);
    }
}

結(jié)果:


quadTo()

三階曲線對應的方法為cubicTo()。由兩個數(shù)據(jù)點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中,
        // 調(diào)用此方法告知父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);
    }
}

結(jié)果:


實例

貝塞爾曲線的優(yōu)點是可是實時控制曲線狀態(tài),并可以通過改變控制點的狀態(tài)實時讓曲線進行平滑的狀態(tài)變化


核心難點:
1.如何得到數(shù)據(jù)點和控制點的位置?
分析:關(guān)于使用繪制圓形的數(shù)據(jù)點與控制點早就已經(jīng)有人詳細的計算好了,可以參考下圖
2.如何達到漸變效果?
分析:漸變其實就是每次對數(shù)據(jù)點和控制點稍微移動一點,然后重繪界面,在短時間多次的調(diào)整數(shù)據(jù)點與控制點,使其逐漸接近目標值,通過不斷的重繪界面達到一種漸變的效果。過程可以參照下圖動態(tài)效果:


就是所需要的數(shù)值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);
    }
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容