Android繪圖之PathMeasure(16)

Android 繪圖學(xué)習(xí)

android繪圖之Paint(1)
android繪圖之Canvas基礎(chǔ)(2)
Android繪圖之Path(3)
Android繪圖之drawText繪制文本相關(guān)(4)
Android繪圖之Canvas概念理解(5)
Android繪圖之Canvas變換(6)
Android繪圖之Canvas狀態(tài)保存和恢復(fù)(7)
Android繪圖之PathEffect (8)
Android繪圖之LinearGradient線性漸變(9)
Android繪圖之SweepGradient(10)
Android繪圖之RadialGradient 放射漸變(11)
Android繪制之BitmapShader(12)
Android繪圖之ComposeShader,PorterDuff.mode及Xfermode(13)
Android繪圖之drawText,getTextBounds,measureText,FontMetrics,基線(14)
Android繪圖之貝塞爾曲線簡介(15)
Android繪圖之PathMeasure(16)
Android 動態(tài)修改漸變 GradientDrawable

1 PathMeasure概述

首先思考一個問題简肴,任意繪制一條曲線御雕,或者一個圓弧法褥,或者一個不規(guī)則圖形,又或者利用Path 同時繪制多條曲線究驴,如何獲取某個點的坐標(biāo)吹艇,曲線的方向诱建,對于簡單的圖形根據(jù)已知內(nèi)容很容易得到坐標(biāo)稀并,對于類似貝塞爾曲線或者不規(guī)則圖形想要或者坐標(biāo)仅颇,角度信息很困難,今天就來講解Path中用于獲取上述信息的一個工具類碘举。

PathMeasure是一個用來測量Path的工具類忘瓦,可以從Path中獲取固定位置的坐標(biāo),橫切信息引颈。

構(gòu)造函數(shù):

/**
 * Create an empty PathMeasure object. To uses this to measure the length
 * of a path, and/or to find the position and tangent along it, call
 * setPath.
  */
public PathMeasure() 耕皮;
/**
 * Create a PathMeasure object associated with the specified path object
 * (already created and specified). The measure object can now return the
 * path's length, and the position and tangent of any position along the
 * path.
 *
 * Note that once a path is associated with the measure object, it is
 * undefined if the path is subsequently modified and the the measure object
 * is used. If the path is modified, you must call setPath with the path.
 *
 * @param path The path that will be measured by this object
 * @param forceClosed If true, then the path will be considered as "closed"
 *        even if its contour was not explicitly closed.
 */
public PathMeasure(Path path, boolean forceClosed) ;

主要有兩個構(gòu)造函數(shù):
PathMeasure()
無參構(gòu)造函數(shù)蝙场,構(gòu)造一個空的PathMeasure凌停,想要使用必須和一個Path進行關(guān)聯(lián),可以利用setPath函數(shù)和Path設(shè)置關(guān)聯(lián)售滤。如果關(guān)聯(lián)的Path被修改了罚拟,也需要重新調(diào)用setPath進行關(guān)聯(lián)。
PathMeasure(Path path, boolean forceClosed)
參數(shù)說明:
path:關(guān)聯(lián)的Path完箩,如果想要重新關(guān)聯(lián)新的path,可以調(diào)用函數(shù)setPath
forceClosed:設(shè)置測量Path時是否強制閉合path(有的Path不是閉合的赐俗,如果為true,那么測量時就會測量閉合的path嗜憔,所以使用時要注意秃励,forceClose為true時測量的長度可能更大。如果關(guān)聯(lián)的Path被修改了吉捶,也需要重新調(diào)用setPath進行關(guān)聯(lián)薯鳍。

所以可以得出:
設(shè)置關(guān)聯(lián)的Path之后唤衫,Path就不會再修改,就算原Path修改了,引用的Path也不會修改梧奢,想要應(yīng)用原Path的修改就需要重新setPath。

forceClosed 代碼示例

public class ViewDemo25 extends View {
    private Path mPath;
    private Paint mPaint1;
    private Paint mPaint2;
    public ViewDemo25(Context context) {
        this(context,null,0);
    }

    public ViewDemo25(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs,0);
    }

    public ViewDemo25(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init() {
        mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint1.setStrokeWidth(7);
        mPaint1.setStyle(Paint.Style.STROKE);
        mPaint1.setColor(Color.RED);

        mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint2.setStrokeWidth(7);
        mPaint2.setColor(Color.RED);
        mPath = new Path();
        mPath.moveTo(100,100);
        mPath.lineTo(600,300);
        mPath.lineTo(300,500);
        PathMeasure pathMeasure1 = new PathMeasure(mPath, true);
        PathMeasure pathMeasure2 = new PathMeasure(mPath, false);
        System.out.println("===========pathMeasure1========"+pathMeasure1.getLength());
        System.out.println("===========pathMeasure2========"+pathMeasure2.getLength());

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(mPath,mPaint1);
    }
}

結(jié)果值:
===========pathMeasure1========1346.2852
===========pathMeasure2========899.0716
Path是個非閉合曲線扣泊,forceClose為true獲取到的Path長度明顯大于forceClose為flase時的值友雳。

2 PathMeasure 方法說明

  • getLength: 用于獲取 Path 的總長度,
  • getMatrix(float distance,Matrix matrix,int flags):獲取指定長度的位置(distance)坐標(biāo)及該點Matrix
  • getPosTan(float distance,float[] pos,float[] tan):獲取指定位置的坐標(biāo)和tan值澎现,tan值代表曲線方向仅胞,也就是切線值,
  • getSegment(float startD,float stopD,Path dst,boolean startWithMoveTo):獲取兩點之間的path片段剑辫,
  • setPath 設(shè)置 PathMeasure 與 Path 關(guān)聯(lián)干旧。
  • isClosed :用于判斷 Path 是否閉合,但需要注意一點妹蔽,它不單單指Path本身的閉合椎眯,還指如果 關(guān)聯(lián) Path 的時候設(shè)置 forceClosed 為 true 的話挠将,方法也返回true。
  • nextContour():獲取下一條曲線的Path编整,因為Path可能包含多個相互之間不連接的Path舔稀。

3 setpath(Path path ,boolean forceClosed)

設(shè)置PathMeasure關(guān)聯(lián)的Path,如果已設(shè)置的Path改變了掌测,需要重新調(diào)用setPath内贮,已設(shè)置的Path不會更新。

forceClosed:設(shè)置測量Path時是否強制閉合path(有的Path不是閉合的赏半,如果為true贺归,那么測量時就會測量閉合的path,所以使用時要注意断箫,forceClose為true時測量的長度可能更大拂酣。
無論傳入的forceClosed為true或者false,都不會影響原Path的繪制仲义。

代碼示例:

private void init() {
    mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint1.setStrokeWidth(7);
    mPaint1.setStyle(Paint.Style.STROKE);
    mPaint1.setColor(Color.RED);

    mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint2.setStrokeWidth(7);
    mPaint2.setColor(Color.RED);
    mPath = new Path();
    mPath.moveTo(100,100);
    mPath.lineTo(600,300);
    mPath.lineTo(300,500);
    PathMeasure pathMeasure1 = new PathMeasure();
    pathMeasure1.setPath(mPath, true);
    PathMeasure pathMeasure2 = new PathMeasure();
    pathMeasure2.setPath(mPath, false);
    System.out.println("===========pathMeasure1===1111====="+pathMeasure1.getLength());
    System.out.println("===========pathMeasure2====2222===="+pathMeasure2.getLength());

    mPath.lineTo(200,400);
    System.out.println("===========pathMeasure1===33333====="+pathMeasure1.getLength());
    System.out.println("===========pathMeasure2====4444===="+pathMeasure2.getLength());

    pathMeasure1.setPath(mPath, true);
    pathMeasure2.setPath(mPath, false);

    System.out.println("===========pathMeasure1===55555====="+pathMeasure1.getLength());
    System.out.println("===========pathMeasure2====66666===="+pathMeasure2.getLength());

}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(mPath,mPaint1);
}

結(jié)果:
===========pathMeasure1===1111=====1346.2852
===========pathMeasure2====2222====899.0716
修改path后沒有調(diào)用setPath的結(jié)果:
===========pathMeasure1===33333=====1346.2852
===========pathMeasure2====4444====899.0716
修改path后調(diào)用setPath的結(jié)果:
===========pathMeasure1===55555=====1356.7207
===========pathMeasure2====66666====1040.4929

上面的例子首先測試了forceClosed 為true和false時的不同婶熬,接著測試了在生成PathMeasure實例后,修改關(guān)聯(lián)的Path埃撵,再次獲取PathMeasure信息赵颅,信息沒有改變,之后重新調(diào)用setPath后暂刘,信息才改變饺谬。

所以如果測量過程中PathMeasure關(guān)聯(lián)的Path發(fā)生改變,需要重新調(diào)用setPath方法進行關(guān)聯(lián)谣拣。

4 nextContour

/**
 * Move to the next contour in the path. Return true if one exists, or
 * false if we're done with the path.
 */
public boolean nextContour();

由于后續(xù)需要募寨,先講解nextContour,看到這個函數(shù)取下一條曲線森缠,也就是說明了一個Path可以有多條曲線拔鹰。默認(rèn)情況 getLength , getSegment 等PathMeasure方法,都只會在其中第一條線段上運行贵涵, nextContour 會跳轉(zhuǎn)到下一條曲線到方法列肢,如果跳轉(zhuǎn)成功,則返回 true宾茂, 如果跳轉(zhuǎn)失敗瓷马,則返回 false。

代碼示例:

mPath = new Path();
mPath.moveTo(100,100);
mPath.lineTo(600,300);
mPath.lineTo(300,500);

mPath.addRect(400,400,900,900, Path.Direction.CW);
PathMeasure pathMeasure = new PathMeasure();
pathMeasure.setPath(mPath,false);
System.out.println("======getLength1111===="+pathMeasure.getLength());
if (pathMeasure.nextContour()){
    System.out.println("======getLength2222===="+pathMeasure.getLength());
}

輸出結(jié)果:
======getLength1111====899.0716
======getLength2222====2000.0
nextContour()返回true跨晴,移動到下一條曲線成功决采,使PathMeasure的方法作用于第二條曲線上。

5 getLength

/**
 * Return the total length of the current contour, or 0 if no path is
 * associated with this measure object.
 */
public float getLength() 

返回當(dāng)前輪廓的總長度坟奥,根據(jù)nextContour()的介紹树瞭,知道返回的長度默認(rèn)只是對第一條曲線的長度的計算。如果想要計算Path中所有曲線的總長度爱谁,需要循環(huán)調(diào)用nextContour,然后計算每條線段的長度晒喷,然后相加得到。

6 isClosed

用于判斷 Path 是否閉合访敌,但需要注意一點凉敲,它不單單指Path本身的閉合,還指如果 關(guān)聯(lián) Path 的時候設(shè)置 forceClosed 為 true 的話寺旺,方法也返回true爷抓。

mPath = new Path();
mPath.moveTo(100,100);
mPath.lineTo(600,300);
mPath.lineTo(300,500);

mPath.addRect(400,400,900,900, Path.Direction.CW);
PathMeasure pathMeasure = new PathMeasure();
pathMeasure.setPath(mPath,false);
System.out.println("======isclosed111===="+pathMeasure.isClosed());
if (pathMeasure.nextContour()){
    System.out.println("======isclosed222===="+pathMeasure.isClosed());
}

setPath是forceClosed傳入false,結(jié)果為:
======isclosed111====false
======isclosed222====true

如果forceClosed傳入true:
======isclosed111====true
======isclosed222====true

所以isClosed判斷曲線是否閉合受forceClosed的影響阻塑。

7 getSegment

getSegment獲取Path中的一段Path蓝撇。

/**
 * Given a start and stop distance, return in dst the intervening
 * segment(s). If the segment is zero-length, return false, else return
 * true. startD and stopD are pinned to legal values (0..getLength()).
 * If startD >= stopD then return false (and leave dst untouched).
 * Begin the segment with a moveTo if startWithMoveTo is true.
 *
 * <p>On {@link android.os.Build.VERSION_CODES#KITKAT} and earlier
 * releases, the resulting path may not display on a hardware-accelerated
 * Canvas. A simple workaround is to add a single operation to this path,
 * such as <code>dst.rLineTo(0, 0)</code>.</p>
 */
public boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) ;

參數(shù)說明:
startD,stopD:開始截取和結(jié)束截取的位置陈莽,兩個參數(shù)都是距離Path起點的長度渤昌。
如果startD 大于stopD直接返回false,如果截取的長度等于0直接返回false走搁,startD和stopD都大于0小于Path的總長度独柑。
dst:如果截取成功,截取到的Path將被加入到dst中私植,不會影響dst中原有的曲線忌栅。
startWithMoveTo:起始點是否使用moveTo,如果startWithMoveTo為true曲稼,截取的Path放入dst時將會使用moveto索绪,這樣可以保證截取的Path起始點不變化。

返回值:返回true表示截取片段成功躯肌,返回false表示不成功者春,不操作dst。

注意:如果系統(tǒng)版本低于等于4.4清女,默認(rèn)開啟硬件加速的情況下钱烟,更改 dst 的內(nèi)容后可能繪制會出現(xiàn)問題,請關(guān)閉硬件加速或者給 dst 添加一個單個操作嫡丙,例如: dst.rLineTo(0, 0)

首先測試截取成功拴袭,是替換dst,還是把截取到的Path添加到dst中曙博。

原始圖形:

private void init() {
    mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint1.setStrokeWidth(7);
    mPaint1.setStyle(Paint.Style.STROKE);
    mPaint1.setColor(Color.BLUE);

    mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint2.setStrokeWidth(7);
    mPaint2.setStyle(Paint.Style.STROKE);
    mPaint2.setColor(Color.RED);

    dst = new Path();
    dst.moveTo(300,500);
    dst.lineTo(700,700);

    mPath = new Path();
    mPath.addRect(200,200,900,900, Path.Direction.CW);
    PathMeasure pathMeasure = new PathMeasure();
    pathMeasure.setPath(mPath,true);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(mPath,mPaint1);
    canvas.drawPath(dst,mPaint2);
}

想要截取右下角的Path拥刻,示意圖如下:


上面的圖形是個正方形,開始繪制的點(200,200)父泳,每個邊長700般哼,所以截取的segment開始點距離Path的開始點為700+350=1050吴汪,結(jié)束點為1400+350=1750。

private void init() {
    mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint1.setStrokeWidth(7);
    mPaint1.setStyle(Paint.Style.STROKE);
    mPaint1.setColor(Color.BLUE);

    mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint2.setStrokeWidth(7);
    mPaint2.setStyle(Paint.Style.STROKE);
    mPaint2.setColor(Color.RED);

    dst = new Path();
    dst.moveTo(300,500);
    dst.lineTo(700,700);

    mPath = new Path();
    mPath.addRect(200,200,900,900, Path.Direction.CW);
    PathMeasure pathMeasure = new PathMeasure();
    pathMeasure.setPath(mPath,true);

    pathMeasure.getSegment(1050, 1750, dst, true);

}

截取成功蒸眠,然后添加截取到的Path到dst漾橙,dst中原來的Path完全沒有受影響。

如果設(shè)置startWithMoveTo為false:

可以看到如果startWithMoveTo 為 false楞卡,獲取到的segment片段霜运,加入到dst時,會將片段的起始點移動到 dst 的最后一個點蒋腮,以保證 dst 的連續(xù)性淘捡,簡單點說會忽略截取到的Path的第一個點替換成dst內(nèi)部已有的最后一個點。

總結(jié):
如果 startWithMoveTo 為 true, 獲取到的片段Path會因為調(diào)用了moveTo保持原狀池摧,如果 startWithMoveTo 為 false焦除,則會將截取出來的 Path 片段的起始點移動到 dst 的最后一個點,以保證 dst 的連續(xù)性险绘。

8 getPosTan

/**
 * Pins distance to 0 <= distance <= getLength(), and then computes the
 * corresponding position and tangent. Returns false if there is no path,
 * or a zero-length path was specified, in which case position and tangent
 * are unchanged.
 *
 * @param distance The distance along the current contour to sample
 * @param pos If not null, returns the sampled position (x==[0], y==[1])
 * @param tan If not null, returns the sampled tangent (x==[0], y==[1])
 * @return false if there was no path associated with this measure object
*/
public boolean getPosTan(float distance, float pos[], float tan[])

獲取Path路徑上距離起點位置distance距離的坐標(biāo)信息和tan值踢京。

參數(shù)說明:
distance:距離Path起點的位置長度,
pos:獲取該點的坐標(biāo)值
tan:改點的正切值,tan0是鄰邊邊長宦棺,tan1是對邊邊長

用ps鋼筆工具自己畫個箭頭瓣距。


代碼示例:

private void init() {
    mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint1.setStrokeWidth(7);
    mPaint1.setStyle(Paint.Style.STROKE);
    mPaint1.setColor(Color.BLUE);

    mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint2.setStrokeWidth(7);
    mPaint2.setStyle(Paint.Style.STROKE);
    mPaint2.setColor(Color.RED);

    dst = new Path();
    dst.moveTo(300,500);
    dst.lineTo(700,700);

    mPath = new Path();
    mPath.addCircle(500,500,300, Path.Direction.CW);
    PathMeasure pathMeasure = new PathMeasure();
    pathMeasure.setPath(mPath,false);
    float zhouchang = (float) (2*Math.PI*300);
    System.out.println("=======circle周長========"+ zhouchang);
    float[] pos = new float[2];
    float[] tan = new float[2] ;
    pathMeasure.getPosTan(zhouchang*3/4,pos,tan);
    System.out.println("=========pos==========="+pos[0]+"  "+pos[1]);
    System.out.println("=========tan==========="+tan[0]+"  "+tan[1]);
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(mPath,mPaint1);
    canvas.drawPoint(500,500,mPaint2);
}

輸出結(jié)果:有稍微的誤差是由于精度問題導(dǎo)致
=======circle周長========1884.9556
=========pos===========500.56152 200.00053
=========tan===========0.9999983 0.0018716537

tan的值有什么用呢,我們可以利用切線值得到當(dāng)前線段的方向代咸,也就是傾斜的角度蹈丸。

float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI);
Math中 atan2 可以根據(jù)正切值得到角度值(也可以自己計算)弧度,然后將弧度轉(zhuǎn)換為具體的度數(shù)。

如果不利用獲取到的tan呐芥,直接根據(jù)坐標(biāo)點繪制圖片

private void init() {
    mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint1.setStrokeWidth(7);
    mPaint1.setStyle(Paint.Style.STROKE);
    mPaint1.setColor(Color.BLUE);

    mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
    mPaint2.setStrokeWidth(7);
    mPaint2.setStyle(Paint.Style.STROKE);
    mPaint2.setColor(Color.RED);

    dst = new Path();
    dst.moveTo(300,500);
    dst.lineTo(700,700);
    jiantou = BitmapFactory.decodeResource(getResources(),R.drawable.jiantou);

    mPath = new Path();
    mPath.addCircle(500,500,300, Path.Direction.CW);
    final PathMeasure pathMeasure = new PathMeasure();
    pathMeasure.setPath(mPath,false);
    final float zhouchang = (float) (2*Math.PI*300);
    System.out.println("=======circle周長========"+ zhouchang);
    float[] pos = new float[2];
    float[] tan = new float[2] ;
    pathMeasure.getPosTan(zhouchang*3/4,pos,tan);
    System.out.println("=========pos==========="+pos[0]+"  "+pos[1]);
    System.out.println("=========tan==========="+tan[0]+"  "+tan[1]);

    ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,zhouchang);
    valueAnimator.setDuration(10000);
    valueAnimator.setInterpolator(new LinearInterpolator());
    valueAnimator.setRepeatCount(-1);
    valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {

            float correntLen = (float) animation.getAnimatedValue();
            float[] pos = new float[2];
            float[] tan = new float[2] ;
            pathMeasure.getPosTan(correntLen,pos,tan);
            posX = pos[0];
            posY = pos[1];
            invalidate();
        }
    });

    valueAnimator.start();
}

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawPath(mPath,mPaint1);
    canvas.drawPoint(500,500,mPaint2);
      canvas.drawBitmap(jiantou,posX,posY,mPaint2);
}

圖片從左上角開始繪制逻杖,左上角會沿著坐標(biāo)旋轉(zhuǎn),看著很不舒服思瘟。

對圖片進行操作:
利用獲取的tan值對圖片進行旋轉(zhuǎn)和平移操作荸百,注意這里的旋轉(zhuǎn)操作時建立在圖片的初始箭頭方向為水平向右的,如果水平向左滨攻,則計算圖片旋轉(zhuǎn)方向時時不相同的(和繪制方向無關(guān))够话。

  private void init() {
        mPaint1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint1.setStrokeWidth(7);
        mPaint1.setStyle(Paint.Style.STROKE);
        mPaint1.setColor(Color.BLUE);

        mPaint2 = new Paint(Paint.ANTI_ALIAS_FLAG);
        mPaint2.setStrokeWidth(7);
        mPaint2.setStyle(Paint.Style.STROKE);
        mPaint2.setColor(Color.RED);

        dst = new Path();
        dst.moveTo(300,500);
        dst.lineTo(700,700);
        mMatrix = new Matrix();

        jiantou = BitmapFactory.decodeResource(getResources(),R.drawable.jiantou);

        mPath = new Path();
        mPath.addCircle(500,500,300, Path.Direction.CCW);
        final PathMeasure pathMeasure = new PathMeasure();
        pathMeasure.setPath(mPath,false);
        final float zhouchang = (float) (2*Math.PI*300);
        System.out.println("=======circle周長========"+ zhouchang);
        float[] pos = new float[2];
        float[] tan = new float[2] ;
        pathMeasure.getPosTan(zhouchang*3/4,pos,tan);
        System.out.println("=========pos==========="+pos[0]+"  "+pos[1]);
        System.out.println("=========tan==========="+tan[0]+"  "+tan[1]);

        ValueAnimator valueAnimator = ValueAnimator.ofFloat(0,zhouchang);
        valueAnimator.setDuration(10000);
        valueAnimator.setInterpolator(new LinearInterpolator());
        valueAnimator.setRepeatCount(-1);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {

                float correntLen = (float) animation.getAnimatedValue();
                float[] pos = new float[2];
                float[] tan = new float[2] ;
                pathMeasure.getPosTan(correntLen,pos,tan);
                posX = pos[0];
                posY = pos[1];
                mMatrix.reset();
                //得到當(dāng)前坐標(biāo)的方向趨勢對應(yīng)的角度
                float degrees = (float) (Math.atan2(tan[1], tan[0]) * 180.0 / Math.PI);

                //下面兩個比較重要,以圖片中心為旋轉(zhuǎn)點對圖片進行旋轉(zhuǎn)光绕,把圖片中心平移到Path上
                mMatrix.postRotate(degrees, jiantou.getWidth() / 2, jiantou.getHeight() / 2);
                mMatrix.postTranslate(pos[0] - jiantou.getWidth() / 2, pos[1] - jiantou.getHeight() / 2);
                invalidate();
            }
        });

        valueAnimator.start();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.drawPath(mPath,mPaint1);
        canvas.drawPoint(500,500,mPaint2);
        canvas.drawBitmap(jiantou,mMatrix,mPaint2);
          }
}

9 getMatrix

/**
 * Pins distance to 0 <= distance <= getLength(), and then computes the
 * corresponding matrix. Returns false if there is no path, or a zero-length
 * path was specified, in which case matrix is unchanged.
 *
 * @param distance The distance along the associated path
 * @param matrix Allocated by the caller, this is set to the transformation
 *        associated with the position and tangent at the specified distance
 * @param flags Specified what aspects should be returned in the matrix.
 */
public boolean getMatrix(float distance, Matrix matrix, int flags) 

上面的例子利用getPosTan獲取正切值女嘲,然后計算位置和旋轉(zhuǎn)角度過程很麻煩,所以google提供了getmatrix直接獲取變換后的Matrix矩陣供我們使用诞帐。

參數(shù)說明:
distance:距離Path起始點的距離
matrix:得到的matrix矩陣
flags:規(guī)定哪些信息添加到Matrix中欣尼,flags有兩個值POSITION_MATRIX_FLAG,TANGENT_MATRIX_FLAG停蕉。POSITION_MATRIX_FLAG表示把位置信息添加到matrix中愕鼓,TANGENT_MATRIX_FLAG表示把正切信息添加到matrix中钙态。
可以分別添加POSITION_MATRIX_FLAG和TANGENT_MATRIX_FLAG,如果想同時添加這兩者需要POSITION_MATRIX_FLAG|TANGENT_MATRIX_FLAG拒啰。
利用getMatrix實現(xiàn)箭頭功能驯绎。

valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {

        float correntLen = (float) animation.getAnimatedValue();
        float[] pos = new float[2];
        float[] tan = new float[2] ;
        pathMeasure.getPosTan(correntLen,pos,tan);
        mMatrix.reset();
        pathMeasure.getMatrix(correntLen,mMatrix,PathMeasure.TANGENT_MATRIX_FLAG|PathMeasure.POSITION_MATRIX_FLAG);

        //矩陣對旋轉(zhuǎn)角度默認(rèn)為圖片的左上角,使用 preTranslate 調(diào)整原點為圖片中心
        mMatrix.preTranslate(-jiantou.getWidth() / 2, -jiantou.getHeight() / 2);

     invalidate();
    }
});

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末谋旦,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子屈尼,更是在濱河造成了極大的恐慌册着,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,682評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件脾歧,死亡現(xiàn)場離奇詭異甲捏,居然都是意外死亡,警方通過查閱死者的電腦和手機鞭执,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,277評論 3 395
  • 文/潘曉璐 我一進店門司顿,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人兄纺,你說我怎么就攤上這事大溜。” “怎么了估脆?”我有些...
    開封第一講書人閱讀 165,083評論 0 355
  • 文/不壞的土叔 我叫張陵钦奋,是天一觀的道長。 經(jīng)常有香客問我疙赠,道長付材,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,763評論 1 295
  • 正文 為了忘掉前任圃阳,我火速辦了婚禮厌衔,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘捍岳。我一直安慰自己富寿,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,785評論 6 392
  • 文/花漫 我一把揭開白布祟同。 她就那樣靜靜地躺著作喘,像睡著了一般。 火紅的嫁衣襯著肌膚如雪晕城。 梳的紋絲不亂的頭發(fā)上泞坦,一...
    開封第一講書人閱讀 51,624評論 1 305
  • 那天,我揣著相機與錄音砖顷,去河邊找鬼贰锁。 笑死赃梧,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的豌熄。 我是一名探鬼主播授嘀,決...
    沈念sama閱讀 40,358評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼锣险!你這毒婦竟也來了蹄皱?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 39,261評論 0 276
  • 序言:老撾萬榮一對情侶失蹤芯肤,失蹤者是張志新(化名)和其女友劉穎巷折,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體崖咨,經(jīng)...
    沈念sama閱讀 45,722評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡锻拘,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了击蹲。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片署拟。...
    茶點故事閱讀 40,030評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖歌豺,靈堂內(nèi)的尸體忽然破棺而出推穷,到底是詐尸還是另有隱情,我是刑警寧澤世曾,帶...
    沈念sama閱讀 35,737評論 5 346
  • 正文 年R本政府宣布缨恒,位于F島的核電站,受9級特大地震影響轮听,放射性物質(zhì)發(fā)生泄漏骗露。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,360評論 3 330
  • 文/蒙蒙 一血巍、第九天 我趴在偏房一處隱蔽的房頂上張望萧锉。 院中可真熱鬧,春花似錦述寡、人聲如沸柿隙。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,941評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽禀崖。三九已至,卻和暖如春螟炫,著一層夾襖步出監(jiān)牢的瞬間波附,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,057評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留掸屡,地道東北人封寞。 一個月前我還...
    沈念sama閱讀 48,237評論 3 371
  • 正文 我出身青樓,卻偏偏與公主長得像仅财,于是被迫代替她去往敵國和親狈究。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,976評論 2 355

推薦閱讀更多精彩內(nèi)容