溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

淺談Android PathMeasure詳解和應(yīng)用

發(fā)布時(shí)間:2020-09-05 00:15:45 來源:腳本之家 閱讀:180 作者:鋸齒流沙 欄目:移動(dòng)開發(fā)

PathMeasure,顧名思義,就是一個(gè)用來測(cè)量Path的類,主要有以下方法:

淺談Android PathMeasure詳解和應(yīng)用 

構(gòu)造方法

無參構(gòu)造方法:

PathMeasure()

創(chuàng)建一個(gè)空的PathMeasure,用這個(gè)構(gòu)造函數(shù)可創(chuàng)建一個(gè)空的 PathMeasure,但是使用之前需要先調(diào)用 setPath 方法來與 Path 進(jìn)行關(guān)聯(lián)。被關(guān)聯(lián)的 Path 必須是已經(jīng)創(chuàng)建好的,如果關(guān)聯(lián)之后 Path 內(nèi)容進(jìn)行了更改,則需要使用 setPath 方法重新關(guān)聯(lián)。

有參構(gòu)造方法

PathMeasure(Path path, boolean forceClosed)

該構(gòu)造函數(shù)是創(chuàng)建一個(gè) PathMeasure 并關(guān)聯(lián)一個(gè) Path, 其實(shí)和創(chuàng)建一個(gè)空的 PathMeasure 后調(diào)用 setPath 進(jìn)行關(guān)聯(lián)效果是一樣的,同樣,被關(guān)聯(lián)的 Path 也必須是已經(jīng)創(chuàng)建好的,如果關(guān)聯(lián)之后 Path 內(nèi)容進(jìn)行了更改,則需要使用 setPath 方法重新關(guān)聯(lián)。 該方法有兩個(gè)參數(shù),第一個(gè)參數(shù)自然就是被關(guān)聯(lián)的 Path 了,第二個(gè)參數(shù)是用來確保 Path 閉合,如果設(shè)置為 true, 則不論之前Path是否閉合,都會(huì)自動(dòng)閉合該 Path(如果Path可以閉合的話)。

這里需要說明以下forceClosed:

1)不論 forceClosed 設(shè)置為何種狀態(tài)(true 或者 false), 都不會(huì)影響原有Path的狀態(tài),即 Path 與 PathMeasure 關(guān)聯(lián)之后,之前的的 Path 不會(huì)有任何改變。

2)forceClosed 的設(shè)置狀態(tài)可能會(huì)影響測(cè)量結(jié)果,如果 Path 沒有閉合但在與 PathMeasure 關(guān)聯(lián)的時(shí)候設(shè)置 forceClosed 為 true 時(shí),測(cè)量結(jié)果可能會(huì)比 Path 實(shí)際長(zhǎng)度稍長(zhǎng)一點(diǎn),獲取得到的是該 Path 閉合時(shí)的狀態(tài)。

setPath

setPath(Path path, boolean forceClosed)方法就是關(guān)聯(lián)一個(gè)Path,需要預(yù)先創(chuàng)建好。

isClosed

isClosed方法用于判斷 Path 是否閉合,但是如果你在關(guān)聯(lián) Path 的時(shí)候設(shè)置 forceClosed 為 true 的話,這個(gè)方法的返回值則一定為true。

getLength

getLength()方法用于獲取Path的長(zhǎng)度。

public class PathMeasureView extends View {

 private static final String TAG = "lwj";
 private int mViewHeight;
 private int mViewWidth;
 private Paint paint;

 public PathMeasureView(Context context) {
  super(context);
  init(context);
 }

 private void init(Context context) {
  paint = new Paint();
  paint.setColor(Color.RED);
  paint.setStyle(Paint.Style.STROKE);
  paint.setStrokeWidth(10);
 }


 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  canvas.translate(mViewWidth/2, mViewHeight/2);

  Path path = new Path();
		path.lineTo(0, 300);
		path.lineTo(300, 300);
		path.lineTo(300, 0);

		PathMeasure measure = new PathMeasure(path, false);
		PathMeasure measure2 = new PathMeasure(path, true);
		Log.i(TAG, "length:"+measure.getLength());//900
		Log.i(TAG,"length:"+ measure2.getLength());//1200
  canvas.drawPath(path, paint);

 }

 //該方法在當(dāng)前View尺寸變化時(shí)被調(diào)用
 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  mViewHeight = h;
  mViewWidth = w;
 }
}

nextContour

我們知道 Path 可以由多條曲線構(gòu)成,但不論是 getLength , getgetSegment 或者是其它方法,都只會(huì)在其中第一條線段上運(yùn)行,而這個(gè) nextContour 就是用于跳轉(zhuǎn)到下一條曲線到方法,如果跳轉(zhuǎn)成功,則返回 true, 如果跳轉(zhuǎn)失敗,則返回 false。 注意:使用多路徑的效果需要關(guān)閉硬件加速。

setLayerType(View.LAYER_TYPE_SOFTWARE, null);
Path path = new Path();
path.addRect(-200, -200, 200, 200, Path.Direction.CW);
path.addRect(-100, -100, 100, 100, Path.Direction.CW);
PathMeasure measure = new PathMeasure(path, false);
float length = measure.getLength();
//獲取下一個(gè)路徑,有可能沒有多個(gè)路徑了,返回false
boolean nextContour = measure.nextContour();
float length3 = measure.getLength();
Log.i("damon", "length2:"+length);
Log.i("damon", "length3:"+length3);
canvas.drawPath(path, paint);

淺談Android PathMeasure詳解和應(yīng)用 

淺談Android PathMeasure詳解和應(yīng)用 

getSegment

boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo):用于獲取Path的一個(gè)片段。

解析:

1)返回值(boolean):判斷截取是否成功,true 表示截取成功,結(jié)果存入dst中,false 截取失敗,不會(huì)改變dst中內(nèi)容。

2)startD:開始截取位置距離 Path 起點(diǎn)的長(zhǎng)度 取值范圍: 0 <= startD < stopD <= Path總長(zhǎng)度;

3)stopD:結(jié)束截取位置距離 Path 起點(diǎn)的長(zhǎng)度 取值范圍: 0 <= startD < stopD <= Path總長(zhǎng)度;

4)dst:截取的 Path 將會(huì)添加到 dst 中 注意: 是添加,而不是替換;

5)startWithMoveTo:起始點(diǎn)是否使用 moveTo,用于保證截取的 Path 第一個(gè)點(diǎn)位置不變。

注意:

• 如果 startD、stopD 的數(shù)值不在取值范圍 [0, getLength] 內(nèi),或者 startD == stopD 則返回值為 false,不會(huì)改變 dst 內(nèi)容。

• 如果在安卓4.4或者之前的版本,在默認(rèn)開啟硬件加速的情況下,更改 dst 的內(nèi)容后可能繪制會(huì)出現(xiàn)問題,請(qǐng)關(guān)閉硬件加速或者給 dst 添加一個(gè)單個(gè)操作,例如: dst.rLineTo(0, 0)

• 可以用以下規(guī)則來判斷 startWithMoveTo 的取值:

true:保證截取得到的 Path 片段不會(huì)發(fā)生形變;

false:保證存儲(chǔ)截取片段的 Path(dst) 的連續(xù)性。

Path path = new Path();
//多路徑的效果需要關(guān)閉硬件加速??!
path.addRect(-200, -200, 200, 200, Path.Direction.CW);
PathMeasure measure = new PathMeasure(path, false);
float length = measure.getLength();
Log.i("damon", "length2:"+length);
canvas.drawPath(path, paint);
Path dst = new Path();
dst.lineTo(-300, -300);
//startWithMoveTo:false,代表該起始點(diǎn)是否位上一個(gè)的結(jié)束點(diǎn)(是否保持連續(xù)性)。
measure.getSegment(200, 600, dst , false);
paint.setColor(Color.RED);
canvas.drawPath(dst, paint);

淺談Android PathMeasure詳解和應(yīng)用 

getMatrix

getMatrix(float distance, Matrix matrix, int flags):獲取路徑上某一長(zhǎng)度的位置以及該位置的正切值的矩陣。

返回值(boolean):判斷獲取是否成功,true表示成功,數(shù)據(jù)會(huì)存入matrix中,false 失敗,matrix內(nèi)容不會(huì)改變;

distance:距離 Path 起點(diǎn)的長(zhǎng)度,取值范圍: 0 <= distance <= getLength;

matrix:根據(jù) falgs 封裝好的matrix 會(huì)根據(jù) flags 的設(shè)置而存入不同的內(nèi)容;

flags:規(guī)定哪些內(nèi)容會(huì)存入到matrix中,可選擇:

POSITION_MATRIX_FLAG(位置)

ANGENT_MATRIX_FLAG(正切)

getPosTan

getPosTan(float distance, float[] pos, float[] tan):獲取指定長(zhǎng)度的位置坐標(biāo)及該點(diǎn)切線值tangle。

返回值(boolean):判斷獲取是否成功,true表示成功,數(shù)據(jù)會(huì)存入 pos 和 tan 中, false 表示失敗,pos 和 tan 不會(huì)改變;

distance:距離 Path 起點(diǎn)的長(zhǎng)度,取值范圍: 0 <= distance <= getLength;

pos:該點(diǎn)的坐標(biāo)值,坐標(biāo)值: (x==[0], y==[1]);

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

通過 三角函數(shù)tan 得值計(jì)算出圖片旋轉(zhuǎn)的角度,tan 是 tangent 的縮寫, 其中tan0是鄰邊邊長(zhǎng),tan1是對(duì)邊邊長(zhǎng),而Math中 atan2 方法是根據(jù)正切是數(shù)值計(jì)算出該角度的大小,得到的單位是弧度,所以上面又將弧度轉(zhuǎn)為了角度。

Path path = new Path();
path.addCircle(0, 0, 300, Path.Direction.CW);
PathMeasure measure = new PathMeasure(path, false);
float[] pos = new float[2];
float[] tan = new float[2];//tan=y/x
measure.getPosTan(measure.getLength()/4, pos , tan );
Log.i("damon", "position:x-"+pos[0]+", y-"+pos[1]);
Log.i("damon", "tan:x-"+tan[0]+", y-"+tan[1]);
canvas.drawPath(path, paint);

淺談Android PathMeasure詳解和應(yīng)用 

淺談Android PathMeasure詳解和應(yīng)用 應(yīng)用

繪制一個(gè)放大鏡,然后慢慢沿著放大鏡的路徑慢慢撤退消失,變成圓形搜索的loading,接著loading完成之后,沿著路徑繪制出放大鏡。 如效果圖所示:

淺談Android PathMeasure詳解和應(yīng)用 

淺談Android PathMeasure詳解和應(yīng)用 

淺談Android PathMeasure詳解和應(yīng)用

這樣一個(gè)自定義View,需要用到PathMeasure,動(dòng)畫等知識(shí)配合來做。

public class SearchView extends View {

 // 畫筆
 private Paint mPaint;

 // View 寬高
 private int mViewWidth;
 private int mViewHeight;

 // 這個(gè)視圖擁有的狀態(tài)
 public static enum State {
  NONE,
  STARTING,
  SEARCHING,
  ENDING
 }

 // 當(dāng)前的狀態(tài)(非常重要)
 private State mCurrentState = State.NONE;

 // 放大鏡與外部圓環(huán)
 private Path path_srarch;
 private Path path_circle;

 // 測(cè)量Path 并截取部分的工具
 private PathMeasure mMeasure;

 // 默認(rèn)的動(dòng)效周期 2s
 private int defaultDuration = 2000;

 // 控制各個(gè)過程的動(dòng)畫
 private ValueAnimator mStartingAnimator;
 private ValueAnimator mSearchingAnimator;
 private ValueAnimator mEndingAnimator;

 // 動(dòng)畫數(shù)值(用于控制動(dòng)畫狀態(tài),因?yàn)橥粫r(shí)間內(nèi)只允許有一種狀態(tài)出現(xiàn),具體數(shù)值處理取決于當(dāng)前狀態(tài))
 private float mAnimatorValue = 0;

 // 動(dòng)效過程監(jiān)聽器
 private ValueAnimator.AnimatorUpdateListener mUpdateListener;
 private Animator.AnimatorListener mAnimatorListener;

 // 用于控制動(dòng)畫狀態(tài)轉(zhuǎn)換
 private Handler mAnimatorHandler;

 // 判斷是否已經(jīng)搜索結(jié)束
 private boolean isOver = false;

 private int count = 0;

 public SearchView(Context context) {
  super(context);

  initPaint();

  initPath();

  initListener();

  initHandler();

  initAnimator();

  // 進(jìn)入開始動(dòng)畫
  mCurrentState = State.STARTING;
  mStartingAnimator.start();

 }

 private void initPaint() {
  mPaint = new Paint();
  mPaint.setStyle(Paint.Style.STROKE);
  mPaint.setColor(Color.WHITE);
  mPaint.setStrokeWidth(15);
  mPaint.setStrokeCap(Paint.Cap.ROUND);
  mPaint.setAntiAlias(true);
 }

 private void initPath() {
  path_srarch = new Path();
  path_circle = new Path();

  mMeasure = new PathMeasure();

  // 注意,不要到360度,否則內(nèi)部會(huì)自動(dòng)優(yōu)化,測(cè)量不能取到需要的數(shù)值
  RectF oval1 = new RectF(-50, -50, 50, 50);   // 放大鏡圓環(huán)
  path_srarch.addArc(oval1, 45, 359.9f);

  RectF oval2 = new RectF(-100, -100, 100, 100);  // 外部圓環(huán)
  path_circle.addArc(oval2, 45, -359.9f);

  float[] pos = new float[2];

  mMeasure.setPath(path_circle, false);    // 放大鏡把手的位置
  mMeasure.getPosTan(0, pos, null);

  path_srarch.lineTo(pos[0], pos[1]);     // 放大鏡把手

  Log.i("TAG", "pos=" + pos[0] + ":" + pos[1]);
 }

 private void initListener() {
  mUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
   @Override
   public void onAnimationUpdate(ValueAnimator animation) {
    mAnimatorValue = (float) animation.getAnimatedValue();
    invalidate();
   }
  };

  mAnimatorListener = new Animator.AnimatorListener() {
   @Override
   public void onAnimationStart(Animator animation) {

   }

   @Override
   public void onAnimationEnd(Animator animation) {
    // getHandle發(fā)消息通知?jiǎng)赢嫚顟B(tài)更新
    mAnimatorHandler.sendEmptyMessage(0);
   }

   @Override
   public void onAnimationCancel(Animator animation) {

   }

   @Override
   public void onAnimationRepeat(Animator animation) {

   }
  };
 }

 private void initHandler() {
  mAnimatorHandler = new Handler() {
   @Override
   public void handleMessage(Message msg) {
    super.handleMessage(msg);
    switch (mCurrentState) {
     case STARTING:
      // 從開始動(dòng)畫轉(zhuǎn)換好搜索動(dòng)畫
      isOver = false;
      mCurrentState = State.SEARCHING;
      mStartingAnimator.removeAllListeners();
      mSearchingAnimator.start();
      break;
     case SEARCHING:
      if (!isOver) { // 如果搜索未結(jié)束 則繼續(xù)執(zhí)行搜索動(dòng)畫
       mSearchingAnimator.start();
       Log.e("Update", "RESTART");

       count++;
       if (count>2){  // count大于2則進(jìn)入結(jié)束狀態(tài)
        isOver = true;
       }
      } else {  // 如果搜索已經(jīng)結(jié)束 則進(jìn)入結(jié)束動(dòng)畫
       mCurrentState = State.ENDING;
       mEndingAnimator.start();
      }
      break;
     case ENDING:
      // 從結(jié)束動(dòng)畫轉(zhuǎn)變?yōu)闊o狀態(tài)
      mCurrentState = State.NONE;
      break;
    }
   }
  };
 }

 private void initAnimator() {
  mStartingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(defaultDuration);
  mSearchingAnimator = ValueAnimator.ofFloat(0, 1).setDuration(defaultDuration);
  mEndingAnimator = ValueAnimator.ofFloat(1, 0).setDuration(defaultDuration);

  mStartingAnimator.addUpdateListener(mUpdateListener);
  mSearchingAnimator.addUpdateListener(mUpdateListener);
  mEndingAnimator.addUpdateListener(mUpdateListener);

  mStartingAnimator.addListener(mAnimatorListener);
  mSearchingAnimator.addListener(mAnimatorListener);
  mEndingAnimator.addListener(mAnimatorListener);
 }


 @Override
 protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  mViewWidth = w;
  mViewHeight = h;
 }

 @Override
 protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);

  drawSearch(canvas);
 }

 private void drawSearch(Canvas canvas) {

  mPaint.setColor(Color.WHITE);


  canvas.translate(mViewWidth / 2, mViewHeight / 2);

  canvas.drawColor(Color.parseColor("#0082D7"));

  switch (mCurrentState) {
   case NONE:
    canvas.drawPath(path_srarch, mPaint);
    break;
   case STARTING:
    mMeasure.setPath(path_srarch, false);
    Path dst = new Path();
    mMeasure.getSegment(mMeasure.getLength() * mAnimatorValue, mMeasure.getLength(), dst, true);
    canvas.drawPath(dst, mPaint);
    break;
   case SEARCHING:
    mMeasure.setPath(path_circle, false);
    Path dst2 = new Path();
    float stop = mMeasure.getLength() * mAnimatorValue;
    float start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * 200f));
//    float start = stop-50;
    mMeasure.getSegment(start, stop, dst2, true);
    canvas.drawPath(dst2, mPaint);
    break;
   case ENDING:
    mMeasure.setPath(path_srarch, false);
    Path dst3 = new Path();
    mMeasure.getSegment(mMeasure.getLength() * mAnimatorValue, mMeasure.getLength(), dst3, true);
    canvas.drawPath(dst3, mPaint);
    break;
  }
 }
}

以上就是關(guān)于PathMeasure的詳解和應(yīng)用,需要讀者去多點(diǎn)動(dòng)手才能理解其中的精髓的地方。

向AI問一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI