貝塞爾曲線學(xué)習(xí)

貝塞爾曲線學(xué)習(xí)

APK下載地址

1.貝塞爾曲線

以下公式中:
B(t)為t時間下 點(diǎn)的坐標(biāo);
P0為起點(diǎn),Pn為終點(diǎn),Pi為控制點(diǎn)

一階貝塞爾曲線(線段)

一階貝塞爾曲線公式
一階貝塞爾曲線演示

意義:由 P0 至 P1 的連續(xù)點(diǎn), 描述的一條線段

二階貝塞爾曲線(拋物線)

二階貝塞爾曲線公式
二階貝塞爾曲線演示

原理:由 P0 至 P1 的連續(xù)點(diǎn) Q0,描述一條線段。

由 P1 至 P2 的連續(xù)點(diǎn) Q1,描述一條線段。 由 Q0 至 Q1 的連續(xù)點(diǎn) B(t),描述一條二次貝塞爾曲線。 經(jīng)驗(yàn):P1-P0為曲線在P0處的切線。

三階貝塞爾曲線:

三階貝塞爾曲線公式
三階貝塞爾曲線演示

通用公式:


通用貝塞爾曲線公式

高階貝塞爾曲線
4階曲線:

四階貝塞爾曲線演示

5階曲線:
五階貝塞爾曲線演示

1.1.生成貝塞爾曲線-迭代法

下面我們用代碼來生成貝塞爾曲線,來展示如何貝塞爾曲線的生成原理:

package com.che.chechengwang.support.view.bezier;

import android.animation.FloatEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;

/**
 * 生成貝塞爾曲線-迭代法
 * <p/>
 * 作者:余天然 on 16/6/14 下午2:10
 */
public class BezierGenerater1 extends View {

    private Paint paint;
    private int centerX, centerY;
    private List<PointF> points;
    private FloatEvaluator evaluator;
    private float fraction;
    private Map<Integer, Integer> colors;
    private List<PointF> destPoints;

    public BezierGenerater1(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
        evaluator = new FloatEvaluator();
        startAnim();
    }

    //初始化數(shù)據(jù)點(diǎn)和控制點(diǎn)的位置
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w / 2;
        centerY = h / 2;
        points = new ArrayList<>();
        points.add(new PointF(centerX - 150, centerY));
        points.add(new PointF(centerX - 50, centerY - 300));
        points.add(new PointF(centerX + 50, centerY + 300));
        points.add(new PointF(centerX + 150, centerY));
        colors = new HashMap<>();
        for (int i = 0; i < points.size(); i++) {
            colors.put(i, getRanColor());
        }
        destPoints = new ArrayList<>();
        destPoints.add(points.get(0));
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //靜態(tài)的
        drawPoint(canvas, points, Color.BLACK);
        drawLine(canvas, points, Color.GRAY);
        //動態(tài)的
        List<PointF> subData = getSubData(points, fraction);
        drawData(canvas, subData, fraction);
    }

    // 繪制數(shù)據(jù)點(diǎn)
    private void drawPoint(Canvas canvas, List<PointF> data, int color) {
        paint.setColor(color);
        paint.setStrokeWidth(20);
        for (int i = 0; i < data.size(); i++) {
            PointF pointF = data.get(i);
            canvas.drawPoint(pointF.x, pointF.y, paint);
        }
    }

    //繪制基準(zhǔn)線
    private void drawLine(Canvas canvas, List<PointF> data, int color) {
        paint.setColor(color);
        paint.setStrokeWidth(4);
        for (int i = 0; i < data.size() - 1; i++) {
            PointF start = data.get(i);
            PointF end = data.get(i + 1);
            canvas.drawLine(start.x, start.y, end.x, end.y, paint);
        }
    }

    //繪制路徑
    private void drawPath(Canvas canvas, List<PointF> data) {
        Path path = new Path();
        PointF start = data.get(0);
        path.moveTo(start.x, start.y);
        for (int i = 1; i < data.size() - 1; i++) {
            PointF point = data.get(i);
            path.lineTo(point.x, point.y);
        }
        paint.setColor(Color.RED);
        paint.setStrokeWidth(4);
        canvas.drawPath(path, paint);
    }

    //迭代繪制集合
    private void drawData(Canvas canvas, List<PointF> data, float fraction) {
        if (data.size() == 1) {
            drawPoint(canvas, data, Color.BLACK);
            destPoints.add(data.get(0));
            drawPath(canvas, destPoints);
        } else {
            drawLine(canvas, data, colors.get(data.size() - 1));
            //迭代
            List<PointF> subData = getSubData(data, fraction);
            drawData(canvas, subData, fraction);
        }
    }

    private void startAnim() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(5000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fraction = animation.getAnimatedFraction();
                invalidate();
            }

        });
        animator.start();
    }

    //生成隨機(jī)顏色
    private int getRanColor() {
        return 0xff000000 | new Random().nextInt(0x00ffffff);
    }

    //獲取子數(shù)據(jù)源
    private List<PointF> getSubData(List<PointF> data, float fraction) {
        List<PointF> subData = new ArrayList<>();
        for (int i = 0; i < data.size() - 1; i++) {
            PointF start = data.get(i);
            PointF end = data.get(i + 1);
            float x = evaluator.evaluate(fraction, start.x, end.x);
            float y = evaluator.evaluate(fraction, start.y, end.y);
            subData.add(new PointF(x, y));
        }
        return subData;
    }
}

1.2.生成貝塞爾曲線-De Casteljau算法

package com.che.chechengwang.support.view.bezier;

import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.LinearInterpolator;

import java.util.ArrayList;
import java.util.List;

/**
 * 生成貝塞爾曲線-De Casteljau算法
 * <p/>
 * 作者:余天然 on 16/6/14 下午2:10
 */
public class BezierGenerater2 extends View {

    private Paint paint;
    private int centerX, centerY;
    private List<PointF> points;
    private float fraction;
    private List<PointF> destPoints;

    public BezierGenerater2(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
    }

    //初始化數(shù)據(jù)點(diǎn)和控制點(diǎn)的位置
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w / 2;
        centerY = h / 2;
        points = new ArrayList<>();
        points.add(new PointF(centerX - 150, centerY));
        points.add(new PointF(centerX - 50, centerY - 300));
        points.add(new PointF(centerX + 50, centerY + 300));
        points.add(new PointF(centerX + 150, centerY));
        destPoints = new ArrayList<>();
        destPoints.add(points.get(0));
        startAnim();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //靜態(tài)的
        drawPoint(canvas, points, Color.BLACK);
        drawLine(canvas, points, Color.GRAY);
        //動態(tài)的
        drawPath(canvas, destPoints);
    }

    // 繪制數(shù)據(jù)點(diǎn)
    private void drawPoint(Canvas canvas, List<PointF> data, int color) {
        paint.setColor(color);
        paint.setStrokeWidth(20);
        for (int i = 0; i < data.size(); i++) {
            PointF pointF = data.get(i);
            canvas.drawPoint(pointF.x, pointF.y, paint);
        }
    }

    //繪制基準(zhǔn)線
    private void drawLine(Canvas canvas, List<PointF> data, int color) {
        paint.setColor(color);
        paint.setStrokeWidth(4);
        for (int i = 0; i < data.size() - 1; i++) {
            PointF start = data.get(i);
            PointF end = data.get(i + 1);
            canvas.drawLine(start.x, start.y, end.x, end.y, paint);
        }
    }

    //繪制路徑
    private void drawPath(Canvas canvas, List<PointF> data) {
        Path path = new Path();
        PointF start = data.get(0);
        path.moveTo(start.x, start.y);
        for (int i = 1; i < data.size() - 1; i++) {
            PointF point = data.get(i);
            path.lineTo(point.x, point.y);
        }
        paint.setColor(Color.RED);
        paint.setStrokeWidth(4);
        canvas.drawPath(path, paint);
    }

    private void startAnim() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
        animator.setInterpolator(new LinearInterpolator());
        animator.setDuration(5000);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                fraction = animation.getAnimatedFraction();
                PointF pointF = deCasteljau(fraction, points);
                destPoints.add(pointF);
                invalidate();
            }
        });
        animator.start();
    }

    //深復(fù)制
    private List<PointF> copyData(List<PointF> points) {
        List<PointF> data = new ArrayList<>();
        for (int i = 0; i < points.size(); i++) {
            PointF point = points.get(i);
            data.add(new PointF(point.x, point.y));
        }
        return data;
    }

    //De Casteljau算法
    public PointF deCasteljau(float fraction, List<PointF> points) {
        List<PointF> data = copyData(points);
        final int n = data.size();
        for (int i = 1; i <= n; i++) {
            for (int j = 0; j < n - i; j++) {
                data.get(j).x = (1 - fraction) * data.get(j).x + fraction * data.get(j + 1).x;
                data.get(j).y = (1 - fraction) * data.get(j).y + fraction * data.get(j + 1).y;
            }
        }
        return data.get(0);
    }

}

1.3.二階貝塞爾曲線

package com.che.chechengwang.support.view.bezier;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

/**
 * 二階貝塞爾曲線
 * <p/>
 * 兩個數(shù)據(jù)點(diǎn)和一個控制點(diǎn)
 * 公式:B(t) = (1 - t)^2 * P0 + 2t * (1 - t) * P1 + t^2 * P2, t ∈ [0,1]
 * 作者:余天然 on 16/6/14 上午10:27
 */
public class Bezier2 extends View {
    private Paint paint;
    private int centerX, centerY;
    private PointF start, end, control;

    public Bezier2(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);

        start = new PointF(0, 0);
        end = new PointF(0, 0);
        control = new PointF(0, 0);
    }

    //初始化數(shù)據(jù)點(diǎn)和控制點(diǎn)的位置
    @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 - 200;
        start.y = centerY;
        end.x = centerX + 200;
        end.y = centerY;
        control.x = centerX;
        control.y = centerY - 100;
    }

    //根據(jù)觸摸位置更新控制點(diǎn),并提示重繪
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        control.x = event.getX();
        control.y = event.getY();
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawPoint(canvas);
        drawGuideLine(canvas);
        drawBezierCurve(canvas);
    }

    // 繪制數(shù)據(jù)點(diǎn)和控制點(diǎn)
    private void drawPoint(Canvas canvas) {
        paint.setColor(Color.GRAY);
        paint.setStrokeWidth(20);
        canvas.drawPoint(start.x, start.y, paint);
        canvas.drawPoint(end.x, end.y, paint);
        canvas.drawPoint(control.x, control.y, paint);
    }

    //繪制輔助線
    private void drawGuideLine(Canvas canvas) {
        paint.setStrokeWidth(4);
        canvas.drawLine(start.x, start.y, control.x, control.y, paint);
        canvas.drawLine(control.x, control.y, end.x, end.y, paint);
    }

    //繪制貝塞爾曲線
    private void drawBezierCurve(Canvas canvas) {
        paint.setColor(Color.RED);
        paint.setStrokeWidth(4);
        Path path = new Path();
        path.moveTo(start.x, start.y);
        path.quadTo(control.x, control.y, end.x, end.y);
        canvas.drawPath(path, paint);
    }
}

1.4.三階貝塞爾曲線

package com.che.chechengwang.support.view.bezier;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

import java.util.ArrayList;
import java.util.List;

/**
 * 三階貝塞爾曲線
 * <p/>
 * 2個數(shù)據(jù)點(diǎn)和2個控制點(diǎn)
 * 公式:B(t) = P0 * (1-t)^3 + 3 * P1 * t * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3 代
 * 作者:余天然 on 16/6/14 上午10:27
 */
public class Bezier3 extends View {

    private Paint paint;
    private int centerX, centerY;
    private List<PointF> points;

    public Bezier3(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);
        paint.setStyle(Paint.Style.STROKE);
    }

    //初始化數(shù)據(jù)點(diǎn)和控制點(diǎn)的位置
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        centerX = w / 2;
        centerY = h / 2;
        points = new ArrayList<>();
        points.add(new PointF(centerX - 150, centerY));
        points.add(new PointF(centerX - 50, centerY - 300));
        points.add(new PointF(centerX + 50, centerY + 300));
        points.add(new PointF(centerX + 150, centerY));
    }

    //根據(jù)觸摸位置更新控制點(diǎn),并提示重繪
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        PointF control = new PointF(0, 0);
        control.x = event.getX();
        control.y = event.getY();

        double distance1 = getDistance(control, points.get(1));
        double distance2 = getDistance(control, points.get(2));

        if (distance1 < distance2) {
            points.set(1, control);
        } else {
            points.set(2, control);
        }
        invalidate();
        return true;
    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        drawPoint(canvas, points, Color.BLACK);
        drawLine(canvas, points, Color.GRAY);
        drawBezierCurve(canvas);
    }

    // 繪制數(shù)據(jù)點(diǎn)
    private void drawPoint(Canvas canvas, List<PointF> data, int color) {
        paint.setColor(color);
        paint.setStrokeWidth(20);
        for (int i = 0; i < data.size(); i++) {
            PointF pointF = data.get(i);
            canvas.drawPoint(pointF.x, pointF.y, paint);
        }
    }

    //繪制基準(zhǔn)線
    private void drawLine(Canvas canvas, List<PointF> data, int color) {
        paint.setColor(color);
        paint.setStrokeWidth(4);
        for (int i = 0; i < data.size() - 1; i++) {
            PointF start = data.get(i);
            PointF end = data.get(i + 1);
            canvas.drawLine(start.x, start.y, end.x, end.y, paint);
        }
    }

    //繪制貝塞爾曲線
    private void drawBezierCurve(Canvas canvas) {
        paint.setColor(Color.RED);
        paint.setStrokeWidth(4);
        Path path = new Path();
        path.moveTo(points.get(0).x, points.get(0).y);
        path.cubicTo(points.get(1).x, points.get(1).y, points.get(2).x, points.get(2).y, points.get(3).x, points.get(3).y);
        canvas.drawPath(path, paint);
    }

    //獲取兩點(diǎn)的距離
    private double getDistance(PointF start, PointF end) {
        return Math.sqrt((end.y - start.y) * (end.y - start.y) + (end.x - start.x) * (end.x - start.x));
    }
}

1.5.用貝塞爾曲線擬合圓形

分成四段3階貝塞爾曲線
每一段擬合90度扇形


貝塞爾曲線擬合90圓度
計(jì)算x1的值

計(jì)算過程:
由公式:B(t) = P0 * (1-t)^3 + 3 * P1 * t * (1-t)^2 + 3 * P2 * t^2 * (1-t) + P3 * t^3
顯然,當(dāng)t=0.5的時候,應(yīng)該正好在45度的位置上,
于是: sin(pi/4) = 0.125(3x1 - 2) + 0.25(3 - 6x1 ) + 0.5(3x1 )
解出x1=0.55228475

也就是:

根據(jù)對稱性,可得y1=x1;

2.自定義視圖

貝塞爾曲線就是PS里面的魔棒工具,UI上的很多曲線,其實(shí)都是用貝塞爾曲線畫的,既然如此,我們只需要知道UI是怎么畫的,讓她標(biāo)注好數(shù)據(jù)點(diǎn)的位置,那我們就可以原樣畫出UI效果了。

貝塞爾曲線的用處,就是動態(tài)繪制平滑曲線!并且可以實(shí)時控制曲線狀態(tài),并可以通過改變控制點(diǎn)的狀態(tài)實(shí)時讓曲線進(jìn)行平滑的狀態(tài)變化。

2.1.氣泡拖拽效果效果圖:

這里寫圖片描述
這里寫圖片描述

上面的圖是盜來的,因?yàn)槲疫€不會在手機(jī)上錄制gif,下面的我的demo的截圖:
這里寫圖片描述

這里:
p1是初始圓的圓心,p2是拖動圓的圓心
X是p1p2線段的中點(diǎn)
AB:與p1p2線段垂直,長度為初始圓的直徑
CD:與p1p2線段垂直,長度為拖動圓的直徑

繪制流程:
1.繪制p1和p2的圓形
2.根據(jù)A,X,D繪制一條2階貝塞爾曲線
3.根據(jù)B,X,C繪制另一條2階貝塞爾曲線
4.設(shè)置path的模式為填充模式

package com.che.chechengwang.support.view.bezier;

import android.animation.FloatEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.animation.OvershootInterpolator;

import com.che.chechengwang.support.view.bezier.PointUtil;

/**
 * 貝塞爾曲線-氣泡拖拽
 * <p/>
 * 二條2階貝塞爾曲線
 * 作者:余天然 on 16/6/14 上午10:27
 */
public class BezierDraggableFlag extends View {
    private Paint paint;
    private int raduis = 40;//氣泡的半徑
    private double distance;//記錄拉動的距離
    private double raduisTmp;//臨時的半徑
    private int maxDistance = 800;//拉斷的距離
    private PointF p1, p2;//圓心
    private PointF pA, pB, pC, pD, pX;//控制點(diǎn)

    private double distanceBackup;
    private PointF p2Backup;

    public BezierDraggableFlag(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    //初始化圓心的位置
    private void init() {
        paint = new Paint();
        paint.setAntiAlias(true);

        p1 = new PointF(600, 400);
        p2 = new PointF(600, 400);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_MOVE:
                p2.x = event.getX();
                p2.y = event.getY();
                distance = PointUtil.getDistance(p1, p2);
                invalidate();
                break;
            case MotionEvent.ACTION_UP:
                if (distance < maxDistance) {
                    p2Backup = new PointF(p2.x, p2.y);
                    distanceBackup = distance;
                    startAnim();
                }
                break;
        }
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (distance < maxDistance) {
            calculatePoint();
            drawBefore(canvas);
        } else {
            drawAfter(canvas);
        }
    }

    //計(jì)算控制點(diǎn)的位置
    private void calculatePoint() {
        double raduisScale = 1 - distance / maxDistance;
        raduisTmp = raduis * raduisScale;

        pA = PointUtil.getRotatePoint(p1, p2, raduisTmp, Math.PI / 2);
        pB = PointUtil.getRotatePoint(p1, p2, raduisTmp, -Math.PI / 2);
        pC = PointUtil.getRotatePoint(p2, p1, raduis, -Math.PI / 2);
        pD = PointUtil.getRotatePoint(p2, p1, raduis, Math.PI / 2);
        pX = PointUtil.getCenterPoint(p1, p2);
    }

    //拉斷之后的繪制
    private void drawAfter(Canvas canvas) {
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(4);
        canvas.drawCircle(p2.x, p2.y, raduis, paint);
    }

    //拉斷之前的繪制
    private void drawBefore(Canvas canvas) {
        //端點(diǎn)
//        paint.setColor(Color.GRAY);
//        paint.setStyle(Paint.Style.STROKE);
//        paint.setStrokeWidth(20);
//        canvas.drawPoint(p1.x, p1.y, paint);
//        canvas.drawPoint(p2.x, p2.y, paint);
//        canvas.drawPoint(pX.x, pX.y, paint);

        //輔助線
        paint.setColor(Color.GRAY);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeWidth(4);
        canvas.drawLine(p1.x, p1.y, p2.x, p2.y, paint);

        //文字
//        paint.setColor(Color.BLACK);
//        paint.setStyle(Paint.Style.FILL);
//        paint.setStrokeWidth(2);
//        paint.setTextSize(30);
//        canvas.drawText("A", pA.x, pA.y, paint);
//        canvas.drawText("B", pB.x, pB.y, paint);
//        canvas.drawText("C", pC.x, pC.y, paint);
//        canvas.drawText("D", pD.x, pD.y, paint);
//        canvas.drawText("X", pX.x, pX.y, paint);
//        canvas.drawText("p1", p1.x, p1.y, paint);
//        canvas.drawText("p2", p2.x, p2.y, paint);

        //圓形
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(4);
        canvas.drawCircle(p1.x, p1.y, (float) raduisTmp, paint);
        canvas.drawCircle(p2.x, p2.y, raduis, paint);

        //貝塞爾曲線
        paint.setColor(Color.RED);
        paint.setStyle(Paint.Style.FILL);
        paint.setStrokeWidth(4);
        Path path = new Path();
        path.moveTo(pA.x, pA.y);
        path.quadTo(pX.x, pX.y, pD.x, pD.y);
        path.lineTo(pC.x, pC.y);
        path.quadTo(pX.x, pX.y, pB.x, pB.y);
        path.lineTo(pA.x, pA.y);
        canvas.drawPath(path, paint);
    }


    //放開之后,如果沒有拉斷,就恢復(fù)初始狀態(tài)
    private void startAnim() {
        final FloatEvaluator evaluator = new FloatEvaluator();
        ValueAnimator animator = ValueAnimator.ofFloat(0, 100);
        animator.setInterpolator(new OvershootInterpolator());
        animator.setDuration(100);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction();
                p2.x = evaluator.evaluate(fraction, p2Backup.x, p1.x);
                p2.y = evaluator.evaluate(fraction, p2Backup.y, p1.y);
                distance = evaluator.evaluate(fraction, distance, 0);
                invalidate();
            }

        });
        animator.start();
    }


}

這里用到了一個工具類:

package com.che.chechengwang.support.view.bezier;

import android.graphics.PointF;

/**
 * 點(diǎn)計(jì)算工具類
 * <p/>
 * 作者:余天然 on 16/6/15 下午2:33
 */
public class PointUtil {

    //獲取旋轉(zhuǎn)后的點(diǎn)
    public static PointF getRotatePoint(PointF p1, PointF p2, double raduis, double radians) {
        double oldRadians = getPointDegree(p1, p2);
        double newRadians = oldRadians + radians;
        float x = (float) (raduis * Math.cos(newRadians));
        float y = (float) (raduis * Math.sin(newRadians));
        return new PointF(p1.x + x, p1.y + y);
    }

    //獲取中間的點(diǎn)
    public static PointF getCenterPoint(PointF p1, PointF p2) {
        float x = (p1.x + p2.x) / 2;
        float y = (p1.y + p2.y) / 2;
        return new PointF(x, y);
    }

    //獲取兩點(diǎn)的角度-返回的是弧度制
    public static double getPointDegree(PointF p1, PointF p2) {
        double scale = (p2.y - p1.y) / (p2.x - p1.x);
        return Math.atan(scale);
    }

    //獲取兩點(diǎn)的距離
    public static double getDistance(PointF start, PointF end) {
        return Math.sqrt((end.y - start.y) * (end.y - start.y) + (end.x - start.x) * (end.x - start.x));
    }
}

2.2.指示器效果目標(biāo)效果:

這里寫圖片描述

這個以后再來玩,其實(shí)有了上面的氣泡拖拽效果,實(shí)現(xiàn)這個效果很簡單的,有興趣的朋友完全可以根據(jù)我上面的效果來實(shí)現(xiàn)它。

隨便再給大家分享一個學(xué)習(xí)android自定義View相關(guān)知識的好網(wǎng)站:有心課堂-三桿火槍干掉自定義View,傳遞給你的不僅僅是技術(shù)!

菜鳥一枚,水平有限,歡迎大家指出博文中的不足之處,小魚將不勝感激!@qq:630709658

參考目錄:


  1. 貝塞爾曲線 總結(jié)
  2. BezierDemo源碼解析-實(shí)現(xiàn)qq消息氣泡拖拽消失的效果
  3. 貝塞爾曲線掃盲
  4. Path之貝塞爾曲線
  5. 使用貝塞爾曲線擬合圓
  6. 基于貝塞爾曲線的Android動畫差值器
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

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

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