Android自定義view --Path 的高階用法之-搜尋按鈕動畫

WangRain1發表於2017-06-07

關於Path之前寫的也很多了,例如path繪製線,path繪製一階,二階和三階貝塞爾路徑,這些都是path的基本用法。今天我要帶大家看的是Path 的高階用法,先上圖,再吹。


效果大致是這樣的。看著是不是挺好。話不多說,切入正題:

既然今天要談Path的高階用法,那就先來講一講(Path -- 中文 )就是“路徑”既然是路徑,從我們物件導向的想法的話,我們就容易想到 路徑 的長度,路徑的某一點等。想到這裡我們就引出今天 的主要 類--------PathMeasure字面意思很容易理解--翻譯成 路徑測量對這就是我們用來測量路徑的類。既然是一個類,我們就要看他集體有哪些方法和屬性

1.構造方法:

方法名 釋義
PathMeasure() 建立一個空的PathMeasure
PathMeasure(Path path, boolean forceClosed) 建立 PathMeasure 並關聯一個指定的Path(Path需要已經建立完成)。
一個是有引數,一個無引數。

無引數就很容易理解就是直接建立物件,那有引數呢?

PathMeasure(Path path, boolean forceClosed) ;第一個引數 Path  咿 !這不就是我們的Path 了嗎。對既然是測量類,那就是要確定需要測量的那個路徑,那第二個引數呢?

第二個引數是用來確保 Path 閉合,如果設定為 true, 則不論之前Path是否閉合,都會自動閉合該 Path(如果Path可以閉合的話)。

2.方法:

返回值 方法名 釋義
void setPath(Path path, boolean forceClosed) 關聯一個Path
boolean isClosed() 是否閉合
float getLength() 獲取Path的長度
boolean nextContour() 跳轉到下一個輪廓
boolean getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo) 擷取片段
boolean getPosTan(float distance, float[] pos, float[] tan) 獲取指定長度的位置座標及該點切線值
boolean getMatrix(float distance, Matrix matrix, int flags) 獲取指定長度的位置座標及該點Matrix
這方法還挺多,都是幹啥的呢?就找幾個重要的,常用的講講:

第一個那就是 setPath(Path path, boolean forceClosed)

這方法一看就知道設定path 路徑的,就是如果我們建立 PathMeasure,時用的是無參的構造方法時 ,這時候就要用此方法,告訴PathMeasure 當前需要測量的是哪一個path路徑。(個人喜歡用無參,比較靈活)。

第二個那就是getLength(),很容易理解就是 獲得 path 的總長度。

第三個:getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo),這個方法非常重要,擷取片段,擷取的結果就是,我們的path路徑。引數也很好理解。第一個是開始的距離,第二個是結束的距離(相對於起點==既然是起點,我們怎麼找他們的起點呢,其實就是我們繪製圖畫筆開始的哪一點)。第三個引數就是返回的路徑。第四個引數起始點是否使用 moveTo 用於保證擷取的 Path 第一個點位置不變。

第四個:getPosTan(float distance, float[] pos, float[] tan),這個也是非常重要的,為什麼呢?聽我解釋一下他的引數就知道了。第一個引數是距離(相對於繪製起點的路徑距離),第二個引數是距離起點的座標點,第三個引數返回的是正玄函式tan@角度。

對這些引數瞭解之後下面就來講解我們今天的例子:

首先我們先來個簡單的:例如


如圖這樣的分析過程:


1.首先繪製一個圓(藍色的),從45度開始繪製,注意圓的起點。

2.同圓心繪製大圓(輔助圓)用來輔助繪製藍色的線。

3.通過getPosTan(float distance, float[] pos, float[] tan)方法得到兩個圓的終點的座標。

4.利用lineTo(x,y)繪製線。就得到我們的搜尋圖形了。

5.接下來就是實時 擷取片段。getSegment(float startD, float stopD, Path dst, boolean startWithMoveTo),擷取從距離0到getlength();這樣就產生了一個動態的效果了。

看程式碼:

關於畫筆,canvas畫布,valueanimator動畫,這些請看之前的部落格,都有詳細講解。

public class MySearch extends View {

    Paint paint;
    Path searchPath; //搜尋的圓
    Path ciclePath; //外圓
    //獲得寬高
    int w;
    int h;
    //這是儲存擷取時,返回的座標點
    float serpos[];
    float cicpos[];
    //測量 path 的類
    PathMeasure measure;
    //動畫產生的實時資料
    float value;

    public MySearch(Context context) {
        this(context,null);
    }

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

    public MySearch(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        //獲得螢幕的寬高
        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
        w = wm.getDefaultDisplay().getWidth();
        h = wm.getDefaultDisplay().getHeight();
        //初始化資料
        serpos = new float[2];
        cicpos = new float[2];
        //畫筆
        paint = new Paint();
        paint.setStyle(Paint.Style.STROKE);
        paint.setDither(true);
        paint.setStrokeWidth(6);
        paint.setAntiAlias(true);
        //初始化路徑
        initPath();
        //初始化動畫
        initAnimator();
    }




    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //移動畫布的遠點到螢幕中心
        canvas.translate(w/2,h/5);
        paint.setColor(Color.BLUE);
        //這是擷取方法返回的路徑
        Path dra = new Path();
        //解除硬體加速,不然沒效果
        dra.reset();
        dra.lineTo(0,0);
        //設定當前需要測量的path
        measure.setPath(searchPath,false);
        //開始擷取
        boolean s = measure.getSegment(measure.getLength()*value,measure.getLength(),dra,true);
        //繪製路徑
        canvas.drawPath(dra,paint);
    }

    /**
     * 初始化路徑
     */
    public void initPath(){

        paint.setColor(Color.BLUE);
        //搜尋的圓
        searchPath = new Path();
        RectF rectF = new RectF(-50,-50,50,50);
        searchPath.addArc(rectF,45,359.9f);
        //輔助圓
        ciclePath = new Path();
        RectF rectF1 = new RectF(-100,-100,100,100);
        ciclePath.addArc(rectF1,45,359.9f);
        //測量類
        measure = new PathMeasure();
        measure.setPath(searchPath,false);
        //獲取座標
        measure.getPosTan(0,serpos,null);
    
        measure.setPath(ciclePath,false);
        //獲取座標
        measure.getPosTan(0,cicpos,null);
        //根據兩點座標繪製線
        searchPath.lineTo(cicpos[0],cicpos[1]);
    }

    /**
     * 初始化動畫
     */
    public void initAnimator(){

        ValueAnimator animator = ValueAnimator.ofFloat(0,1);
        animator.setDuration(2600);
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                value = (float) animation.getAnimatedValue();
                postInvalidate();
            }
        });
        animator.start();
    }
}

第一個圖大家學會了,去練習一下吧!










相關文章