PathMeasure

PathMeasure是一個用來測量Path的類,主要有以下方法

構造方法

/**
 * 創建一個空的pathMeasure
 */
PathMeasure()
/**
 * 創建一個pathMeasure 並關聯一個指定的path(path歲要創建完成)
 */
PathMeasure(Path path, boolean forceClosed)

公共方法

/**
 * 關聯一個Path
 */
void setPath(Path path, boolean forceClosed)

/**
 * 是否閉合
 */
boolean isClosed()

/**
 * 獲取path長度
 */
float getLength()

/**
 * 跳轉到下一個輪廓
 */
boolean nextContour()

/**
 * 截取片段
 */
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)

/**
 * 獲取指定長度的位置座標及該店切線值tangle
 */
boolean getPosTan(float distance, float pos[], float tan[])

/**
 * 獲取指定長度的位置座標及該點Matrix(矩陣)
 */
boolean getMatrix(float distance, Matrix matrix, int flags)

PathMeasure的方法也不多,接下來我們逐一講解。

1.構造函數

  • 無參構造函

       PathMeasure() 

       用這個構造

       用這個構造函數可創建一個空的 PathMeasure,但是使用之前需要先調用 setPath 方法來與 Path 進行關聯。被關聯的 Path 必須是已經創建好的,如果關聯之後 Path 內容進行了更改,則需要使用 setPath 方法重新關聯。

  • 有參數的構造函數

        PathMeasure(Path path, boolean forceClosed)

       用這個構造函數是創建一個 PathMeasure 並關聯一個 Path 其實和創建一個空的 PathMeasure 後調用 setPath 進行關聯效果是一樣的,同樣,被關聯的 Path 也必須是已經創建好的,如果關聯之後 Path 內容進行了更改,則需要使用 setPath 方法重新關聯。

       該方法有兩個參數,第一個參數自然就是被關聯的 Path 了,第二個參數是用來確保 Path 閉合,如果設置爲 true 則不論之前Path是否閉合,都會自動閉合該 Path(如果Path可以閉合的話)

注意:1.不論 forceClosed 設置爲何種狀態(true 或者 false), 都不會影響原有Path的狀態,即 Path 與 PathMeasure 關聯之後,之前的的 Path 不會有任何改變。2.​​​​​​​forceClosed 的設置狀態可能會影響測量結果,如果 Path 未閉合但在與 PathMeasure 關聯的時候設置 forceClosed 爲 true 時,測量結果可能會比 Path 實際長度稍長一點,獲取到到是該 Path 閉合時的狀態。

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Path path1 = new Path();
        path1.moveTo(195,195);
        path1.lineTo(405,195);
        path1.lineTo(405,405);
        path1.lineTo(195,405);
        canvas.drawPath(path1,p1);

        Path path2 = new Path();
        path2.moveTo(200,200);
        path2.lineTo(400,200);
        path2.lineTo(400,400);
        path2.lineTo(200,400);

        PathMeasure pathMeasure = new PathMeasure();
        pathMeasure.setPath(path2,false);
        Path path3 = new Path();
        pathMeasure.getSegment(0,pathMeasure.getLength(),path3,true);
        canvas.drawPath(path3,p2);

    }

綠色線是模擬關聯的path畫出來的  紅色的線pathMeasure測量路徑

2.getSegment

用於獲取path的一個片段

boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)

返回值boolean 判斷是否截取成功 true表示截取成功,結果存入dst中,false截取失敗,不會改變dst內容

startD: 開始截取位置距離Path起點的長度

stopD: 結束截取位置距離Path起點的長度

dst:截取的Path會添加到dst中

startWithMoveTo: 起始點是否使用moveTo 用於保證截取的Path第一個點位置不變

注意:1.如果 startD、stopD 的數值不在取值範圍 [0, getLength] 內,或者 startD == stopD 則返回值爲 false,不會改變 dst 內容。2.如果在安卓4.4或者之前的版本,在默認開啓硬件加速的情況下,更改 dst 的內容後可能繪製會出現問題,請關閉硬件加速或者給 dst 添加一個單個操作,例如: dst.rLineTo(0, 0)

public class MyPathMeasure extends View {

    private final Paint p2;
    private final PathMeasure pathMeasure;
    private final Path path2;
    private final Path path;

    public MyPathMeasure(Context context) {
        super(context);
        p2 = new Paint();
        p2.setStyle(Paint.Style.STROKE);
        p2.setStrokeWidth(10);
        p2.setColor(Color.RED);
        path2 = new Path();
        path2.addCircle(300, 300, 105, Path.Direction.CW);
        pathMeasure = new PathMeasure();
        pathMeasure.setPath(path2, true);

        path = new Path();
        ss();
    }

    private boolean flag = false;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (flag) {
           path.reset();
            pathMeasure.getSegment(0,pathMeasure.getLength()*dx,path,true);
            canvas.drawPath(path,p2);
        }
    }
    float dx;
    @SuppressLint("WrongConstant")
    private void ss() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setDuration(3000);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (float) animation.getAnimatedValue();
                flag = true;
                invalidate();
            }
        });
        animator.start();
    }
}

​​​​​​​

3.nextContour

我們知道 Path 可以由多條曲線構成,但不論是 getLength , getgetSegment 或者是其它方法,都只會在其中第一條線段上運行,而這個 nextContour 就是用於跳轉到下一條曲線到方法,如果跳轉成功,則返回 true 如果跳轉失敗,則返回 false。

@Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        Path path1 = new Path();
        path1.addCircle(300,300,100,Path.Direction.CW);
        canvas.drawPath(path1,p1);
        Path path4 = new Path();
        path4.addRect(100,100,300,300,Path.Direction.CW);
        canvas.drawPath(path4,p1);

        Path path2 = new Path();
        path2.addCircle(300,300,95,Path.Direction.CW);
        path2.addRect(95,95,305,305,Path.Direction.CW);

        PathMeasure pathMeasure = new PathMeasure();
        pathMeasure.setPath(path2,true);
        Log.i("twy","nextContour之前length="+pathMeasure.getLength());
        pathMeasure.nextContour();
        Log.i("twy","nextContour之後length="+pathMeasure.getLength());
        Path path3 = new Path();
        pathMeasure.getSegment(0,pathMeasure.getLength(),path3,true);
        canvas.drawPath(path3,p2);

    }

註釋了pathMeasure.nextContour();

沒有註釋pathMeasure.nextContour();

4.getPosTan

這個方法是用於得到路徑上某一長度的位置以及位置的正切值

boolean getPosTan(float distance, float pos[], float tan[])

返回值boolean 判斷獲取是否成功 true表示成功,數據會存入 pos[] 和tan[]中

distance:距離path起點的長度 取值範圍 0<=distance<=getLength

pos: 該點的座標值 座標值(x==[0],y==[1])

tan: 該點的正切值 正切值(x==[0],y==[1]) 

通過 tan 得值計算出圖片旋轉的角度,tan tangent 的縮寫,即中學中常見的正切, 其中tan0是鄰邊邊長,tan1是對邊邊長,而Math atan2 方法是根據正切是數值計算出該角度的大小,得到的單位是弧度,所以上面又將弧度轉爲了角度。

public class MyPathMeasure extends View {

    private final Paint p2;
    private final PathMeasure pathMeasure;
    private final Path path2;

    public MyPathMeasure(Context context) {
        super(context);
        p2 = new Paint();
        p2.setStyle(Paint.Style.STROKE);
        p2.setStrokeWidth(3);
        p2.setColor(Color.RED);

        path2 = new Path();
        path2.addCircle(300, 300, 105, Path.Direction.CW);

        pathMeasure = new PathMeasure();
        pathMeasure.setPath(path2, true);
        ss();
    }

    private boolean flag = false;
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if(flag) {
            float[] pos = new float[2];
            float[] tan = new float[2];
            boolean posTan = pathMeasure.getPosTan(pathMeasure.getLength() * dx, pos, tan);
            if (posTan) {
                float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180f / Math.PI);//Math.atan2 API算切點的角度
                canvas.drawPath(path2,p2);
                canvas.save();
                canvas.rotate(degrees,pos[0],pos[1]);
                canvas.drawLine(pos[0], pos[1], pos[0]+500, pos[1], p2);
                canvas.restore();
            }
        }
    }
    float dx;
    @SuppressLint("WrongConstant")
    private void ss(){
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setDuration(1000);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (float) animation.getAnimatedValue();
                flag = true;
                invalidate();
            }
        });
        animator.start();
    }
}

直線和圓相切

5.getMatrix

這個方法是用於得到路徑上某一長度的位置以及該位置的正切值的矩陣:

boolean getMatrix(float distance, Matrix matrix, int flags)

返回值 boolean 判斷獲取是否成功 true表示成功,數據存入matrix中,false失敗matrix內不會改變

distance:距離Path起點的長度 取值範圍 0<=distance<=getLength

matrix:根據flags封裝好的matrix 會根據flags的設置而存入不同的內容

flags:規定哪些內容會存入到 matrix中 可選擇 POSITON_MATRIX_FLAG(位置)ANGENT_MATRIX_FLAG(正切)

public class MyPathMeasure extends View {

    private final Paint p2;
    private final PathMeasure pathMeasure;
    private final Path path2;
    private final Bitmap mBitMap;
    private final float[] pos;
    private final float[] tan;
    private final Matrix mMatrix;

    public MyPathMeasure(Context context) {
        super(context);
        p2 = new Paint();
        p2.setStyle(Paint.Style.STROKE);
        p2.setStrokeWidth(3);
        p2.setColor(Color.RED);

        path2 = new Path();
        path2.addCircle(300, 300, 105, Path.Direction.CW);

        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inSampleSize = 2;
        mBitMap = BitmapFactory.decodeResource(getResources(), R.drawable.timg, options);

        pos = new float[2];
        tan = new float[2];
        mMatrix = new Matrix();


        pathMeasure = new PathMeasure();
        pathMeasure.setPath(path2, true);
        ss();
    }

    private boolean flag = false;

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (flag) {
            canvas.drawPath(path2, p2);
            // 方案一 :自己計算
            // 將tan值通過反正切函數得到對應的弧度,在轉化成對應的角度度數
            boolean posTan = pathMeasure.getPosTan(pathMeasure.getLength() * dx, pos, tan);
            if (posTan) {
                float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180f / Math.PI);
                canvas.save();
                canvas.rotate(degrees,pos[0],pos[1]);
                canvas.drawBitmap(mBitMap,pos[0]-mBitMap.getWidth()/2,pos[1]-mBitMap.getHeight(),p2);
                canvas.restore();
            }
            // 方案二 :直接使用API
            /*pathMeasure.getMatrix(pathMeasure.getLength() * dx, mMatrix, PathMeasure.TANGENT_MATRIX_FLAG | PathMeasure.POSITION_MATRIX_FLAG);
            mMatrix.preTranslate(-mBitMap.getWidth() / 2, -mBitMap.getHeight());
            canvas.drawBitmap(mBitMap, mMatrix, p2);*/
        }
    }
    float dx;
    @SuppressLint("WrongConstant")
    private void ss() {
        ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.setDuration(3000);
        animator.setInterpolator(new LinearInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                dx = (float) animation.getAnimatedValue();
                flag = true;
                invalidate();
            }
        });
        animator.start();
    }
}

 

 

 

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章