自定義View之Path測量工具PathMeasure的詳解

PathMeasure是什麼?
PathMeasure是用來對Path進行測量的工具,一般來說PathMeasure是和Path配合着使用的。通過PathMeasure,我們可以知道Path路徑上某謳歌點的座標、Path的長度等的。

PathMeasure有兩個構造函數:

//構建一個空的PathMeasure
PathMeasure() 
//構建一個PathMeasure並關聯一個指定的創建好的Path
PathMeasure(Path path, boolean forceClosed) 

無參構造函數PathMeasure()在使用前必須調用setPath(Path path, boolean forceClosed)來關聯一個Path。其實就是等價於有參數的構造函數PathMeasure(Path path, boolean forceClosed)了。如果關聯之後的Path有所更改,那麼就需要調用setPath(Path path, boolean forceClosed)重新關聯。

PathMeasure常用的API:

setPath(Path path, boolean forceClosed) 
isClosed()
getLength()
getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo)
getMatrix(float distance, Matrix matrix, int flags)
getPosTan(float distance, float pos[], float tan[])
nextContour()

setPath(Path path, boolean forceClosed)

此方法是關聯一個預先創建好的Path。第二個參數forceClosed如果爲true,並且關聯的Path未閉合時,測量的Path長度可能比Path實際長度長一點,因爲測量的是Path閉合的長度。但是關聯的Path不會有任何變化。

isClosed()

判斷關聯的Path是否是閉合狀態。如果forceClosed爲true,那麼此方法一定返回true。

getLength()

返回已關聯的Path總長度。如果setPath()時設置的forceClosed爲true,則返回的值可能會比實際長度的長。

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

截取Path的一段。如果截取成功,則返回true,反之則返回false。

參數 備註
startD 起點在Path的位置,取值範圍0<=startD < stopD<=getLength()
stopD 終點在Path的位置,取值範圍0<=startD < stopD<=getLength()
dst 將截取的path的片段添加到dst中
startWithMoveTo 起點是否使用MoveTo,如果爲true,則截取的path的第一個點不會變化,截取的path也不會改變,如果爲false,則截取的path可能會發生形變。

注:
1.如果截取Path的長度爲0,則返回false,大於0則返回true;
2.startD、stopD必須爲和法制(0,getLength()),如果startD>=stopD,則返回false;
3.在Android 4.4或之前的版本在開啓硬件加速時,繪製可能會不顯示,請關閉硬件加速或者給dst添加一個簡單的操作,如:dst.rLineTo(0,0)

演示一下:

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE); //只描邊
        paint.setColor(Color.BLUE);
        paint.setStrokeWidth(10f);
        paint.setAntiAlias(true); //設置抗鋸齒
        paint.setDither(true); //設置防抖動

        //初始化Path並順時針繪製一個矩形
        Path sourcePath = new Path();
        sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
        PathMeasure measure = new PathMeasure();
        measure.setPath(sourcePath, false);
        Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
        canvas.drawPath(sourcePath, paint);

結果:
這裏寫圖片描述

這裏寫圖片描述

現在要截取上圖中的下半部分,總長度是2000,中間的部分就是[750,1750]。演示一下:

        //初始化Path並順時針繪製一個矩形
        Path sourcePath = new Path();
        sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
        PathMeasure measure = new PathMeasure();
        measure.setPath(sourcePath, false);
//        Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
//        canvas.drawPath(sourcePath, paint);

        //初始化一個空的Path
        Path dstPath = new Path();
        //截取sourcePath的一部分添加到dstPath中
        measure.getSegment(750, 1750, dstPath, true);
        dstPath.rLineTo(0, 0);//真機剛剛好是4.4版本的,加個簡單的操作
        canvas.drawPath(dstPath, paint);

結果:
這裏寫圖片描述

上面代碼中的dstPath初始化完之後,並沒有內容的,試試有內容的情況:

        //初始化Path並順時針繪製一個矩形
        Path sourcePath = new Path();
        sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
        PathMeasure measure = new PathMeasure();
        measure.setPath(sourcePath, false);
//        Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
//        canvas.drawPath(sourcePath, paint);

        //初始化一個空的Path
        Path dstPath = new Path();
        dstPath.rLineTo(200, 200);
        dstPath.lineTo(800, 200);
        //截取sourcePath的一部分添加到dstPath中
        measure.getSegment(750, 1750, dstPath, true);
        canvas.drawPath(dstPath, paint);

結果:
這裏寫圖片描述

有意思的是,將dstPath的rLineTo()和lineTo()放在getSegment()之後,後面內容的起點就成了getSegment()的終點了。演示一下:

        //初始化Path並順時針繪製一個矩形
        Path sourcePath = new Path();
        sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
        PathMeasure measure = new PathMeasure();
        measure.setPath(sourcePath, false);
//        Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
//        canvas.drawPath(sourcePath, paint);

        //初始化一個空的Path
        Path dstPath = new Path();
        //截取sourcePath的一部分添加到dstPath中
        measure.getSegment(750, 1750, dstPath, true);
        dstPath.rLineTo(200, 200);
        dstPath.lineTo(800, 200);
        canvas.drawPath(dstPath, paint);

結果:
這裏寫圖片描述

再把上面代碼中的getSegment()的startWithMoveTo參數改成false會變成什麼樣呢?演示一下:

          //初始化Path並順時針繪製一個矩形
        Path sourcePath = new Path();
        sourcePath.addRect(300, 300, 800, 800, Path.Direction.CW);
        PathMeasure measure = new PathMeasure();
        measure.setPath(sourcePath, false);
//        Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
//        canvas.drawPath(sourcePath, paint);

        //初始化一個空的Path
        Path dstPath = new Path();
		//爲了易於理解,把這兩個方法放回getSegment()前面
        dstPath.rLineTo(200, 200);
        dstPath.lineTo(800, 200);
        //截取sourcePath的一部分添加到dstPath中
        measure.getSegment(750, 1750, dstPath, false);

        canvas.drawPath(dstPath, paint);

結果:
這裏寫圖片描述

和上上結果對比可得出:startWithMoveTo參數爲true時,被截取的path片段會保持原狀;startWithMoveTo參數爲false時,會將截取的path片段的起始點移動到dstPath的終點,以保持dstPath的連續性。

getMatrix(float distance, Matrix matrix, int flags)

距離Path起始點的一段長度distance,通過計算得到該位置座標並返回一個處理好的矩陣,該矩陣以左上角爲旋轉點,如果Path不存在或者長度爲0,該方法返回false。

參數 備註
distance 距離Path起始點的距離,取值範圍0 <= distance <= getLength()
matrix 根據 flags封裝matrix,flags不同,存入matrix的就不同
flags PathMeasure.POSITION_MATRIX_FLAG:位置信息 ,PathMeasure.TANGENT_MATRIX_FLAG:切邊信息,方位角信息,使得圖片按path旋轉。

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

距離Path起始點的長度distance,通過計算返回該長度在Path上的座標及該座標的正切值分別複製給pos[]、tan[]

參數 備註
distance 距離Path起始點的距離,取值範圍0 <= distance <= getLength()
pos[] distance在path上的座標,即pos[]存的該點的座標x,y值
tan[] distance在path上對應座標點在path上的方向,tan[0]是鄰邊邊長,tan[1]是對邊邊長。通過Math.atan2(tan[1], tan[0])*180.0/Math.PI 可以得到正切角的弧度值。

nextContour()

如果Path有多條曲線組成,且彼此不連接,那麼getLength()、getSegment()、getMatrix()和getPosTan()這些方法,都只是針對當前正在操作的。舉個例子,Path由多條曲線組成,且彼此不連接,那麼getLength()返回的只是當前操作曲線的長度,並不是所有曲線的長度。那麼怎麼獲取下一條曲線的長度呢?這時就得用nextContour()跳轉到下一條曲線了,跳轉成功返回true,失敗就返回false。演示一下:

        Paint paint = new Paint();
        paint.setStyle(Paint.Style.STROKE); //只描邊
        paint.setColor(Color.BLUE);
        paint.setStrokeWidth(10f);
        paint.setAntiAlias(true); //設置抗鋸齒
        paint.setDither(true); //設置防抖動

        Path path = new Path();
        PathMeasure measure = new PathMeasure();
        //繪製一條從(100,100)到(900,100)的直線,長度爲800
        path.moveTo(100, 100);
        path.lineTo(900, 100);
        //繪製一條從(100,200)到(500,100)的直線,長度爲400
        path.moveTo(100, 200);
        path.lineTo(500, 200);
        measure.setPath(path, false);
        //輸出第一條曲線的長度
        Log.e("PathMeasure", "measure.getLength():" + measure.getLength());
        measure.nextContour(); //跳轉到下一條曲線
        //輸出第二條曲線的長度
        Log.e("PathMeasure", "next measure.getLength():" + measure.getLength());
        canvas.drawPath(path, paint);

輸出的結果:
這裏寫圖片描述

掃碼關注個人公衆號「Android 零零柒」:

在這裏插入圖片描述

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