国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統 > Android > 正文

Android中PathMeasure仿支付寶支付動畫

2019-12-12 02:18:38
字體:
來源:轉載
供稿:網友

前言

在 Android 自定義 View 中,Path 可能用的比較多,PathMeasure 可能用的比較少,就我而言,以前也沒有使用過 PathMeasure 這個 api,看到別人用 PathMeasure 和 ValueAnimator 結合在一起完成了很好的動畫效果,于是我也學習下 PathMeasure ,此處記錄下。

PathMeasure

構造器:

forceClosed 含義:

// 創建一個 Path 對象path = new Path();path.moveTo(20, 20);path.lineTo(200, 20);path.lineTo(200, 400);

在onDraw(Canvas canvas) 中繪制 path

@Override protected void onDraw(Canvas canvas) {  destPath.reset();  destPath.lineTo(0, 0);  pathMeasure.setPath(path, true);  Log.e("debug", "PathMeasure.getLength() = " + pathMeasure.getLength());  pathMeasure.getSegment(0, pathMeasure.getLength() * curValue, destPath, true);canvas.drawPath(destPath, paint); // 繪制線段路徑 }

當 pathMeasure.setPath(path,false) 時:

這里寫圖片描述

這里寫圖片描述

當 pathMeasure.setPath(path,true) 時:

這里寫圖片描述

這里寫圖片描述

可以看到:當 forceClosed = true 時, path 進行了閉合,相應的 path 長度也變長了,即 算上了斜邊的長度。

仿支付寶支付動畫 View - LoadingView

效果:

這里寫圖片描述

思路:

繪制對號,叉號,主要是通過 ValueAnimator 結合 getSegment() 不斷繪制新的弧形段,其中,叉號由兩個 path 組成,在第一個 path 繪制完成時,需要調用 pathMeasure.nextContour() 跳轉到另一個 path。

getSegment() 將獲取的片段填充到 destPath 中,在 Android 4.4 及以下版本中,不能繪制,需要調用 destPath.reset(),destPath.line(0,0)

LoadingView 完整代碼:

public class LoadingView extends View {  private final int DEFAULT_COLOR = Color.BLACK; // 默認圓弧顏色  private final int DEFAULT_STROKE_WIDTH = dp2Px(2); // 默認圓弧寬度  private final boolean DEFAULT_IS_SHOW_RESULT = false; // 默認不顯示加載結果  private final int DEFAULT_VIEW_WIDTH = dp2Px(50); // 控件默認寬度  private final int DEFAULT_VIEW_HEIGHT = dp2Px(50); // 控件默認高度  private int color; // 圓弧顏色  private int strokeWidth;  // 圓弧寬度  private boolean isShowResult;  // 是否顯示加載結果狀態  private Paint paint; // 畫筆  private int mWidth; // 控件寬度  private int mHeight; // 控件高度  private int radius;  // 圓弧所在圓的半徑  private int halfStrokeWidth; // 畫筆寬度的一半  private int rotateDelta = 4;  private int curAngle = 0;  private int minAngle = -90;  private int startAngle = -90; // 上方頂點  private int endAngle = 0;  private RectF rectF;  private StateEnum stateEnum = StateEnum.LOADING;  private Path successPath;  private Path rightFailPath;  private Path leftFailPath;  private ValueAnimator successAnimator;  private ValueAnimator rightFailAnimator;  private ValueAnimator leftFailAnimator;  private PathMeasure pathMeasure;  private float successValue;  private float rightFailValue;  private float leftFailValue;  private Path destPath;  private AnimatorSet animatorSet;  public LoadingView(Context context) {    this(context, null);  }  public LoadingView(Context context, AttributeSet attrs) {    super(context, attrs);    init(context, attrs);  }  private void init(Context context, AttributeSet attrs) {    TypedArray typedArray = null;    try {      typedArray = context.obtainStyledAttributes(attrs, R.styleable.LoadingView);      color = typedArray.getColor(R.styleable.LoadingView_color, DEFAULT_COLOR);      strokeWidth = (int) typedArray.getDimension(R.styleable.LoadingView_storkeWidth, DEFAULT_STROKE_WIDTH);      isShowResult = typedArray.getBoolean(R.styleable.LoadingView_isShowResult, DEFAULT_IS_SHOW_RESULT);    } catch (Exception e) {      e.printStackTrace();    } finally {      if (typedArray != null) {        typedArray.recycle();      }    }    paint = createPaint(color, strokeWidth, Paint.Style.STROKE);  }  @Override  protected void onSizeChanged(int w, int h, int oldw, int oldh) {    super.onSizeChanged(w, h, oldw, oldh);    mWidth = w;    mHeight = h;    Log.i("debug", "getMeasureWidth() = " + getMeasuredWidth());    Log.i("debug", "getMeasureHeight() = " + getMeasuredHeight());    radius = Math.min(mWidth, mHeight) / 2;    halfStrokeWidth = strokeWidth / 2;    rectF = new RectF(halfStrokeWidth - radius, halfStrokeWidth - radius,        radius - halfStrokeWidth, radius - halfStrokeWidth);    // success path    successPath = new Path();    successPath.moveTo(-radius * 2 / 3f, 0f);    successPath.lineTo(-radius / 8f, radius / 2f);    successPath.lineTo(radius / 2, -radius / 3);    // fail path ,right top to left bottom    rightFailPath = new Path();    rightFailPath.moveTo(radius / 3f, -radius / 3f);    rightFailPath.lineTo(-radius / 3f, radius / 3f);    // fail path, left top to right bottom    leftFailPath = new Path();    leftFailPath.moveTo(-radius / 3f, -radius / 3f);    leftFailPath.lineTo(radius / 3f, radius / 3f);    pathMeasure = new PathMeasure();    destPath = new Path();    initSuccessAnimator();    initFailAnimator();  }  private void initSuccessAnimator() {//    pathMeasure.setPath(successPath, false);    successAnimator = ValueAnimator.ofFloat(0, 1f);    successAnimator.setDuration(1000);    successAnimator.setInterpolator(new LinearInterpolator());    successAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        successValue = (float) animation.getAnimatedValue();        invalidate();      }    });  }  private void initFailAnimator() {//    pathMeasure.setPath(rightFailPath, false);    rightFailAnimator = ValueAnimator.ofFloat(0, 1f);    rightFailAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        rightFailValue = (float) animation.getAnimatedValue();        invalidate();      }    });//    pathMeasure.setPath(leftFailPath, false);    leftFailAnimator = ValueAnimator.ofFloat(0, 1f);    leftFailAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {      @Override      public void onAnimationUpdate(ValueAnimator animation) {        leftFailValue = (float) animation.getAnimatedValue();        invalidate();      }    });    animatorSet = new AnimatorSet();    animatorSet.play(leftFailAnimator).after(rightFailAnimator);    animatorSet.setDuration(500);    animatorSet.setInterpolator(new LinearInterpolator());  }  /**   * 測量控件的寬高,當測量模式不是精確模式時,設置默認寬高   *   * @param widthMeasureSpec   * @param heightMeasureSpec   */  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    int widthMode = MeasureSpec.getMode(widthMeasureSpec);    int heightMode = MeasureSpec.getMode(heightMeasureSpec);    if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {      widthMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_VIEW_WIDTH, MeasureSpec.EXACTLY);    }    if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {      heightMeasureSpec = MeasureSpec.makeMeasureSpec(DEFAULT_VIEW_HEIGHT, MeasureSpec.EXACTLY);    }    super.onMeasure(widthMeasureSpec, heightMeasureSpec);  }  @Override  protected void onDraw(Canvas canvas) {    canvas.save();    canvas.translate(mWidth / 2, mHeight / 2);    destPath.reset();    destPath.lineTo(0, 0);  // destPath    if (stateEnum == StateEnum.LOADING) {      if (endAngle >= 300 || startAngle > minAngle) {        startAngle += 6;        if (endAngle > 20) {          endAngle -= 6;        }      }      if (startAngle > minAngle + 300) {        minAngle = startAngle;        endAngle = 20;      }      canvas.rotate(curAngle += rotateDelta, 0, 0);//旋轉rotateDelta=4的弧長      canvas.drawArc(rectF, startAngle, endAngle, false, paint);      // endAngle += 6 放在 drawArc()后面,是防止剛進入時,突兀的顯示了一段圓弧      if (startAngle == minAngle) {        endAngle += 6;      }      invalidate();    }    if (isShowResult) {      if (stateEnum == StateEnum.LOAD_SUCCESS) {        pathMeasure.setPath(successPath, false);        canvas.drawCircle(0, 0, radius - halfStrokeWidth, paint);        pathMeasure.getSegment(0, successValue * pathMeasure.getLength(), destPath, true);        canvas.drawPath(destPath, paint);      } else if (stateEnum == StateEnum.LOAD_FAILED) {        canvas.drawCircle(0, 0, radius - halfStrokeWidth, paint);        pathMeasure.setPath(rightFailPath, false);        pathMeasure.getSegment(0, rightFailValue * pathMeasure.getLength(), destPath, true);        if (rightFailValue == 1) {          pathMeasure.setPath(leftFailPath, false);          pathMeasure.nextContour();          pathMeasure.getSegment(0, leftFailValue * pathMeasure.getLength(), destPath, true);        }        canvas.drawPath(destPath, paint);      }    }    canvas.restore();  }  public void updateState(StateEnum stateEnum) {    this.stateEnum = stateEnum;    if (stateEnum == StateEnum.LOAD_SUCCESS) {      successAnimator.start();    } else if (stateEnum == StateEnum.LOAD_FAILED) {      animatorSet.start();    }  }  public enum StateEnum {    LOADING, // 正在加載    LOAD_SUCCESS,  // 加載成功,顯示對號    LOAD_FAILED   // 加載失敗,顯示叉號  }  /**   * 創建畫筆   *   * @param color    畫筆顏色   * @param strokeWidth 畫筆寬度   * @param style    畫筆樣式   * @return   */  private Paint createPaint(int color, int strokeWidth, Paint.Style style) {    Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);    paint.setStrokeCap(Paint.Cap.ROUND);    paint.setStrokeJoin(Paint.Join.ROUND);    paint.setColor(color);    paint.setStrokeWidth(strokeWidth);    paint.setStyle(style);    return paint;  }  /**   * dp 轉換成 px   *   * @param dpValue   * @return   */  private int dp2Px(int dpValue) {    return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, getResources().getDisplayMetrics());  }}

github : https://github.com/xing16/LoadingView

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持武林網。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 台北县| 莲花县| 吉林省| 溧水县| 咸阳市| 靖江市| 大理市| 东乡县| 蒙阴县| 武功县| 石泉县| 大余县| 溆浦县| 咸宁市| 渑池县| 溆浦县| 奎屯市| 秭归县| 南投市| 奉化市| 榆社县| 安庆市| 浪卡子县| 平远县| 米泉市| 邳州市| 阿坝县| 焉耆| 美姑县| 澄城县| 腾冲县| 通江县| 信阳市| 易门县| 鸡东县| 衡阳县| 个旧市| 瓮安县| 都安| 沅陵县| 浦东新区|