欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

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

 更新時間:2018年01月02日 16:37:11   作者:鋸齒流沙  
本篇文章主要介紹了淺談Android PathMeasure詳解和應(yīng)用,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧

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

 

構(gòu)造方法

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

PathMeasure()

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

有參構(gòu)造方法

PathMeasure(Path path, boolean forceClosed)

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

這里需要說明以下forceClosed:

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

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

setPath

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

isClosed

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

getLength

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

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尺寸變化時被調(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 或者是其它方法,都只會在其中第一條線段上運行,而這個 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();
//獲取下一個路徑,有可能沒有多個路徑了,返回false
boolean nextContour = measure.nextContour();
float length2 = measure.getLength();
Log.i("damon", "length1:"+length);
Log.i("damon", "length2:"+length2);
canvas.drawPath(path, paint);

 

 

getSegment

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

解析:

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

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

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

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

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

注意:

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

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

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

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

false:保證存儲截取片段的 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", "length1:"+length);
canvas.drawPath(path, paint);
Path dst = new Path();
dst.lineTo(-300, -300);
//startWithMoveTo:false,代表該起始點是否位上一個的結(jié)束點(是否保持連續(xù)性)。
measure.getSegment(200, 600, dst , false);
paint.setColor(Color.RED);
canvas.drawPath(dst, paint);

 

getMatrix

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

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

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

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

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

POSITION_MATRIX_FLAG(位置)

ANGENT_MATRIX_FLAG(正切)

getPosTan

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

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

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

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

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

通過 三角函數(shù)tan 得值計算出圖片旋轉(zhuǎn)的角度,tan 是 tangent 的縮寫, 其中tan0是鄰邊邊長,tan1是對邊邊長,而Math中 atan2 方法是根據(jù)正切是數(shù)值計算出該角度的大小,得到的單位是弧度,所以上面又將弧度轉(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);

 

應(yīng)用

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

 

 

這樣一個自定義View,需要用到PathMeasure,動畫等知識配合來做。

public class SearchView extends View {

 // 畫筆
 private Paint mPaint;

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

 // 這個視圖擁有的狀態(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;

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

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

 // 控制各個過程的動畫
 private ValueAnimator mStartingAnimator;
 private ValueAnimator mSearchingAnimator;
 private ValueAnimator mEndingAnimator;

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

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

 // 用于控制動畫狀態(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();

  // 進入開始動畫
  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)部會自動優(yōu)化,測量不能取到需要的數(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ā)消息通知動畫狀態(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:
      // 從開始動畫轉(zhuǎn)換好搜索動畫
      isOver = false;
      mCurrentState = State.SEARCHING;
      mStartingAnimator.removeAllListeners();
      mSearchingAnimator.start();
      break;
     case SEARCHING:
      if (!isOver) { // 如果搜索未結(jié)束 則繼續(xù)執(zhí)行搜索動畫
       mSearchingAnimator.start();
       Log.e("Update", "RESTART");

       count++;
       if (count>2){  // count大于2則進入結(jié)束狀態(tài)
        isOver = true;
       }
      } else {  // 如果搜索已經(jīng)結(jié)束 則進入結(jié)束動畫
       mCurrentState = State.ENDING;
       mEndingAnimator.start();
      }
      break;
     case ENDING:
      // 從結(jié)束動畫轉(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)用,需要讀者去多點動手才能理解其中的精髓的地方。

相關(guān)文章

最新評論