Androidは、Pathで検索ボタンとクロックの複雑な効果を実現します。


Androidでの複雑な図形の描画は、ほとんどがパスによって実現されます。例えば、曲線を描いて、物体をこの曲線に沿って移動させます。例えば、検索ボタン、例えば簡単な時計の実現です。
何がパステルですか? 
定義:パス  パスです。図形のパスの集合です。パスの中の座標点などの属性が含まれています。任意の点の座標,正接値を得ることができます。 
Path上のすべての点の座標を取得するには、クラス、PathMeasreを使用する必要があります。 
PathMesure:
PathMeasreはPathを測定するためのクラスであり、主に以下の方法がある。 
作成方法
 
共通の方法
 
これはPathのツールと同じです。方法は簡単です。じゃ、私達が作るボタンと時計の開発を始めましょう。 
(1)検索ボタンは、まず上の図を示します。
 
この機能を実現するには、まず彼を分解して作らなければならない。
検索ボタンのパスを作成し、外周回転のパスを作成します。 

 public void initPath(){
    mPath_search = new Path();
    mPath_circle = new Path();

    mMeasure = new PathMeasure();

    //   ,   360 ,         ,           
    RectF oval1 = new RectF(-50, -50, 50, 50);     //      
    mPath_search.addArc(oval1, 45, 359.9f);

    RectF oval2 = new RectF(-100, -100, 100, 100);   //     
    mPath_circle.addArc(oval2, 45, -359.9f);

    float[] pos = new float[2];

    mMeasure.setPath(mPath_circle, false);        //         
    mMeasure.getPosTan(0, pos, null);

    mPath_search.lineTo(pos[0], pos[1]);         //      

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

  }

私たちが求めている効果は検索ボタンをクリックした時からボタンが回転して検索が終わったら検索ボタンになります。 
ですから、4つの状態があることを確認できます。 

  public  enum Seach_State{
    START,END,NONE,SEARCHING
  }

  その後、状態に応じて動的にパスを描画し、現在のパスの座標をPathMeasreに使用して測定し、描画します。 

  private void drawPath(Canvas c) {
    c.translate(mWidth / 2, mHeight / 2);
    switch (mState){

      case NONE:
        c.drawPath(mPath_search,mPaint);
        break;

      case START:
        mMeasure.setPath(mPath_search,true);
        Path path = new Path();
        mMeasure.getSegment(mMeasure.getLength() * curretnAnimationValue,mMeasure.getLength(),path, true);
        c.drawPath(path,mPaint);
        break;

      case SEARCHING:
        mMeasure.setPath(mPath_circle,true);
        Path path_search = new Path();
        mMeasure.getSegment(mMeasure.getLength()*curretnAnimationValue -30,mMeasure.getLength()*curretnAnimationValue,path_search,true);
        c.drawPath(path_search,mPaint);
        break;

      case END:
        mMeasure.setPath(mPath_search,true);
        Path path_view = new Path();

        mMeasure.getSegment(0,mMeasure.getLength()*curretnAnimationValue,path_view,true);
        c.drawPath(path_view,mPaint);
        break;
    }

  }

次に、プロパティアニメーションを使用して、現在の描画のパーセントを返します。この値を使って、描画するパスを計算します。
以下はコード全体です。

package com.duoku.platform.demo.canvaslibrary.attract.view;

import android.animation.Animator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

/**
 * Created by chenpengfei_d on 2016/9/7.
 */
public class SearchView extends View {
  private Paint mPaint;
  private Context mContext;
  private Path mPath_circle;
  private Path mPath_search;
  private PathMeasure mMeasure;
  private ValueAnimator mValueAnimator_search;
  private long defaultduration=3000;
  private float curretnAnimationValue;
  private Seach_State mState = Seach_State.SEARCHING;
  public SearchView(Context context) {
    super(context);
    init(context);
  }

  public SearchView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init(context);
  }

  public SearchView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context);
  }

  public void init(Context context){
    this.mContext = context;
    initPaint();
    initPath();
    initAnimation();

  }
  public void initPaint(){
    mPaint = new Paint();
    mPaint.setDither(true);
    mPaint.setStrokeCap(Paint.Cap.ROUND);//      
    mPaint.setAntiAlias(true);
    mPaint.setColor(Color.RED);
    mPaint.setStrokeWidth(3);
    mPaint.setStyle(Paint.Style.STROKE);
  }

  public void initPath(){
    mPath_search = new Path();
    mPath_circle = new Path();

    mMeasure = new PathMeasure();

    //   ,   360 ,         ,           
    RectF oval1 = new RectF(-50, -50, 50, 50);     //      
    mPath_search.addArc(oval1, 45, 359.9f);

    RectF oval2 = new RectF(-100, -100, 100, 100);   //     
    mPath_circle.addArc(oval2, 45, -359.9f);

    float[] pos = new float[2];

    mMeasure.setPath(mPath_circle, false);        //         
    mMeasure.getPosTan(0, pos, null);

    mPath_search.lineTo(pos[0], pos[1]);         //      

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

  }

  public void initAnimation(){
    mValueAnimator_search = ValueAnimator.ofFloat(0f,1.0f).setDuration(defaultduration);

    mValueAnimator_search.addUpdateListener(updateListener);

    mValueAnimator_search.addListener(animationListener);
  }
  private ValueAnimator.AnimatorUpdateListener updateListener = new ValueAnimator.AnimatorUpdateListener() {
    @Override
    public void onAnimationUpdate(ValueAnimator animation) {
      curretnAnimationValue = (float) animation.getAnimatedValue();
      invalidate();
    }
  };

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

    }

    @Override
    public void onAnimationEnd(Animator animation) {
        if(mState ==Seach_State.START){
          setState(Seach_State.SEARCHING);
        }
    }

    @Override
    public void onAnimationCancel(Animator animation) {

    }

    @Override
    public void onAnimationRepeat(Animator animation) {

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

    drawPath(canvas);
  }
  private int mWidth,mHeight;
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    mHeight = h;

  }

  private void drawPath(Canvas c) {
    c.translate(mWidth / 2, mHeight / 2);
    switch (mState){

      case NONE:
        c.drawPath(mPath_search,mPaint);
        break;

      case START:
        mMeasure.setPath(mPath_search,true);
        Path path = new Path();
        mMeasure.getSegment(mMeasure.getLength() * curretnAnimationValue,mMeasure.getLength(),path, true);
        c.drawPath(path,mPaint);
        break;

      case SEARCHING:
        mMeasure.setPath(mPath_circle,true);
        Path path_search = new Path();
        mMeasure.getSegment(mMeasure.getLength()*curretnAnimationValue -30,mMeasure.getLength()*curretnAnimationValue,path_search,true);
        c.drawPath(path_search,mPaint);
        break;

      case END:
        mMeasure.setPath(mPath_search,true);
        Path path_view = new Path();

        mMeasure.getSegment(0,mMeasure.getLength()*curretnAnimationValue,path_view,true);
        c.drawPath(path_view,mPaint);
        break;
    }

  }


  public void setState(Seach_State state){
    this.mState = state;
    startSearch();
  }

  public void startSearch(){
    switch (mState){
      case START:
        mValueAnimator_search.setRepeatCount(0);
        break;

      case SEARCHING:
        mValueAnimator_search.setRepeatCount(ValueAnimator.INFINITE);
        mValueAnimator_search.setRepeatMode(ValueAnimator.REVERSE);
        break;

      case END:
        mValueAnimator_search.setRepeatCount(0);
        break;
    }
    mValueAnimator_search.start();
  }
  public  enum Seach_State{
    START,END,NONE,SEARCHING
  }
}

 (学習のポイント:パスは組み合わせられます。異なるパスを一つのパスの中に置いて、統一的に描きます。 
(2)クロック効果:
 
時計の考え方としては、ネット上の時計の多くは、Canvasを通じて基本図形を描いています。pathを通じて実現されていません。pathを使って実現するのは、これからもっと柔軟に時計の効果をコントロールするためです。 
時計の実装部分: 
1、外輪パスの作成 
2、目盛パスを作成し、整数点を区分し、時間点を描きます。 
3、ポインタを描きます。(これはcanvasで描かれた線分を使っています。Pathを使ってもいいです。自分でテストできます。) 
現在の時計回り、分針、秒針の角度を計算してから描画します。 
全体コード: 

package com.duoku.platform.demo.canvaslibrary.attract.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PathMeasure;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;

import java.util.Calendar;

/**
 * Created by chenpengfei_d on 2016/9/8.
 */
public class TimeView extends View {
  private Paint mPaint,mPaint_time;
  private Paint mPaint_h,mPaint_m,mPaint_s;
  private Path mPath_Circle;
  private Path mPath_Circle_h;
  private Path mPath_Circle_m;
  private Path mPath_h,mPath_m,mPath_s;
  private Path mPath_duration;

  private PathMeasure mMeasure;
  private PathMeasure mMeasure_h;
  private PathMeasure mMeasure_m;
  private Handler mHandler = new Handler();
  private Runnable clockRunnable;
  private boolean isRunning;
  public TimeView(Context context) {
    super(context);
    init();
  }

  public TimeView(Context context, AttributeSet attrs) {
    super(context, attrs);
    init();
  }

  public TimeView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init();
  }
  int t = 3;
  public void init(){
    //     
    mPaint = new Paint();
    mPaint.setDither(true);
    mPaint.setAntiAlias(true);
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(2);
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    mPaint.setStrokeJoin(Paint.Join.ROUND);
    mPaint.setColor(Color.RED);
    mPaint_time = new Paint();
    mPaint_time.setDither(true);
    mPaint_time.setAntiAlias(true);
    mPaint_time.setStyle(Paint.Style.STROKE);
    mPaint_time.setStrokeWidth(2);
    mPaint_time.setTextSize(15);
    mPaint_time.setStrokeCap(Paint.Cap.ROUND);
    mPaint_time.setStrokeJoin(Paint.Join.ROUND);
    mPaint_time.setColor(Color.RED);

    mPaint_h = new Paint();
    mPaint_h.setDither(true);
    mPaint_h.setAntiAlias(true);
    mPaint_h.setStyle(Paint.Style.STROKE);
    mPaint_h.setStrokeWidth(6);
    mPaint_h.setTextSize(15);
    mPaint_h.setStrokeCap(Paint.Cap.ROUND);
    mPaint_h.setStrokeJoin(Paint.Join.ROUND);
    mPaint_h.setColor(Color.RED);

    mPaint_m = new Paint();
    mPaint_m.setDither(true);
    mPaint_m.setAntiAlias(true);
    mPaint_m.setStyle(Paint.Style.STROKE);
    mPaint_m.setStrokeWidth(4);
    mPaint_m.setTextSize(15);
    mPaint_m.setStrokeCap(Paint.Cap.ROUND);
    mPaint_m.setStrokeJoin(Paint.Join.ROUND);
    mPaint_m.setColor(Color.RED);

    mPaint_s = new Paint();
    mPaint_s.setDither(true);
    mPaint_s.setAntiAlias(true);
    mPaint_s.setStyle(Paint.Style.STROKE);
    mPaint_s.setStrokeWidth(2);
    mPaint_s.setTextSize(15);
    mPaint_s.setStrokeCap(Paint.Cap.ROUND);
    mPaint_s.setStrokeJoin(Paint.Join.ROUND);
    mPaint_s.setColor(Color.RED);
    //     
    mPath_Circle = new Path();
    mPath_Circle.addCircle(0,0,250, Path.Direction.CCW);
    mPath_Circle_h = new Path();
    mPath_Circle_h.addCircle(0,0,220, Path.Direction.CCW);
    mPath_Circle_m = new Path();
    mPath_Circle_m.addCircle(0,0,235, Path.Direction.CCW);
    //   PathMeasure  path  ,
    mMeasure = new PathMeasure();
    mMeasure.setPath(mPath_Circle,true);
    mMeasure_h = new PathMeasure();
    mMeasure_h.setPath(mPath_Circle_h,true);
    mMeasure_m = new PathMeasure();
    mMeasure_m.setPath(mPath_Circle_m,true);
    //    path
    mPath_duration = new Path();
    for (int i = 60; i>0 ;i --){
      Path path = new Path();
      float pos [] = new float[2];
      float tan [] = new float[2];
      float pos2 [] = new float[2];
      float tan2 [] = new float[2];
      float pos3 [] = new float[2];
      float tan3 [] = new float[2];
      mMeasure.getPosTan(mMeasure.getLength()*i/60,pos,tan);
      mMeasure_h.getPosTan(mMeasure_h.getLength()*i/60,pos2,tan2);
      mMeasure_m.getPosTan(mMeasure_m.getLength()*i/60,pos3,tan3);

      float x = pos[0];
      float y = pos[1];
      float x2 = pos2[0];
      float y2 = pos2[1];
      float x3 = pos3[0];
      float y3 = pos3[1];
      path.moveTo(x , y);

      if(i% 5 ==0){
        path.lineTo(x2,y2);
        if(t>12){
          t = t-12;
        }
        String time = t++ +"";
        Path path_time = new Path();
        mMeasure_h.getPosTan(mMeasure_h.getLength()*(i-1)/60,pos2,tan2);
        mPaint.getTextPath(time,0,time.length(),(x2- (x2/15)),y2-(y2/15),path_time);
        path.close();
        path.addPath(path_time);
      }else{
        path.lineTo(x3,y3);
      }


      mPath_duration.addPath(path);
      clockRunnable = new Runnable() {//            ,      
        @Override
        public void run() {
          //       
          postInvalidate();
          mHandler.postDelayed(this, 1000);
        }
      };
    }

    mPath_h = new Path();
    mPath_h.rLineTo(50,30);

    mPath_m = new Path();
    mPath_m.rLineTo(80,80);

    mPath_s = new Path();
    mPath_s.rLineTo(130,50);
  }
  private int mWidth,mHeight;
  @Override
  protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = w;
    mHeight = h;
  }

  @Override
  protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    if(!isRunning){
      isRunning = true;
      mHandler.postDelayed(clockRunnable,1000);
    }else{
      canvas.translate(mWidth/2,mHeight/2);

      canvas.drawPath(mPath_Circle,mPaint);
      canvas.save();
      canvas.drawPath(mPath_duration,mPaint_time);

      canvas.drawPoint(0,0,mPaint_time);

      drawClockPoint(canvas);
    }



  }
  private Calendar cal;
  private int hour;
  private int min;
  private int second;
  private float hourAngle,minAngle,secAngle;
  /**
   *       
   * @param canvas
   */
  private void drawClockPoint(Canvas canvas) {
    cal = Calendar.getInstance();
    hour = cal.get(Calendar.HOUR);//Calendar.HOUR    12   ,Calendar.HOUR_OF_DAY    24   
    min = cal.get(Calendar.MINUTE);
    second = cal.get(Calendar.SECOND);
    //                
    hourAngle = (float)hour / 12 * 360 + (float)min / 60 * (360 / 12);//360/12           
    minAngle = (float)min / 60 * 360;
    secAngle = (float)second / 60 * 360;
    //    、 、                ,         canvas     
    canvas.save();
    canvas.rotate(hourAngle,0, 0);
    canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 65, mPaint_h);//       65

    canvas.restore();
    canvas.save();
    canvas.rotate(minAngle,0, 0);
    canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 90 , mPaint_m);//       90

    canvas.restore();
    canvas.save();
    canvas.rotate(secAngle,0, 0);
    canvas.drawLine(0, 0, mWidth/6, getHeight() / 6 - 110 , mPaint_s);//       110

    canvas.restore();
  }
}

これは複雑なアニメーションではないです。何かいいアイデアがあるかもしれません。自分でPath+属性アニメーションを通してもっと美しい効果を実現できます。 
例えば星空の効果、例えば動的に文字を描く+パスは、電子書籍の自動ページをめくるなど、pptで再生されるような効果を実現します。 
(3)もう一つの知識を紹介します。svgです。 
svgは何ですか? 
彼の学名はスケーリングベクトルグラフィックスといい、二次元ベクトルパターンを記述するための拡張可能なマーク言語(標準的な共通マーク言語のサブセット)に基づいたグラフィックスフォーマットである。 
このようなフォーマットの図形式は、AndroidのPathにロードすることができる。 
Pathにロードできるなら、もっと複雑な効果が得られますか?
以上が本文の全部です。皆さんの勉強に役に立つように、私たちを応援してください。