安卓開發之使用PathMeasure自定義加載動畫控件


先上效果圖:

這裏寫圖片描述

一、PathMeasure

在繪製畫布時,我們通常會使用Path對象來勾勒出繪製的路徑,而PathMeasure可以讓我們得到路徑上的一些信息,比如獲取路徑上點的座標,截取路徑上的某一段小路徑等。。

它主要有既個方法:

  • setPath(Path path, boolean forceClosed);

    與一個Path對象綁定,forceClose爲true時,不管關聯的Path是否是閉合的,都會被閉合。

  • getLength()

    獲取路徑的長度

  • getPosTan(float distance, float pos[],float tan[])

    distance 爲一個 0 - getLength() 之間的值,根據這個距離值計算出當前點的xy座標和正切值封裝到 pos和tan當中;

  • boolean getSegment (float startD, float stopD, Path dst, boolean startWithMoveTo)

    判斷截取路徑成功時返回true;

    startD爲開始截取位置距離 Path 起點的長度
    stopD 結束截取位置距離 Path 起點的長度
    取值範圍: 0 <= startD < stopD <= Path總長度getLength(),超過範圍截取不成功

    截取的 Path 將會添加到 dst 中

    startWithMoveTo 起始點是否使用 moveTo,用於保證截取的 Path 第一個點位置不變(一般爲true)

    在默認開啓硬件加速的情況下,更改 dst 的內容後可能繪製會出現問題,可以給 dst 添加一個操作:dst.lineTo(0, 0)

二、自定義動畫控件

大概思路就是:

創建一個勾勒空心圓的Path對象
再根據上面的Path對象創建PathMeasure對象
最後再通過屬性動畫不斷動態獲取startD和stopD的值,並更新View

public class LoadingCircleView extends View {

    private Path path;
    private PathMeasure pathMeasure;

    private Path dst; // 被截取的路徑

    private Paint paint; // 畫筆

    // View的寬高
    private int height;
    private int width;

    private float radius; // 空心圓的半徑
    private float mLength; // path路徑的長度

    ValueAnimator valueAnimator; // 屬性動畫
    private float mAnimatorValue; // 屬性動畫返回的百分比

    private float stop; // 截取路徑時的stopD值
    private float start; // 截取路徑時的startD值

    public LoadingCircleView(Context context) {
        super(context);
        init(); // 初始化
    }

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

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

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public LoadingCircleView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init();
    }

    private void init() {
        radius = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,21,getContext().getResources().getDisplayMetrics()); // 初始化半徑
        path = new Path();
        dst = new Path();
        paint = new Paint();
        pathMeasure = new PathMeasure();

        // 設置畫筆屬性
        paint.setAntiAlias(true);
        paint.setColor(0xbfe46d32);
        paint.setStyle(Paint.Style.STROKE);
        paint.setStrokeCap(Paint.Cap.ROUND);        paint.setStrokeWidth(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,3.5f,getContext().getResources().getDisplayMetrics()));

    }

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

        width = w;
        height = h;

        // 勾勒空心圓
        path.reset();
        path.addCircle(width/2,height/2,radius, Path.Direction.CW);

        // 生成pathMeasure對象
        pathMeasure.setPath(path,true);

        // 獲取path的長度
        mLength = pathMeasure.getLength();

        // 通過屬性動畫取得百分比值,並更新View
        valueAnimator = ValueAnimator.ofFloat(0,1);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                // 獲取動畫進行的百分比
                mAnimatorValue = (float) animation.getAnimatedValue();
                postInvalidate(); // 更新界面
            }
        });
        // 設置動畫的屬性
        valueAnimator.setDuration(2100);
        valueAnimator.setRepeatCount(1000);
        valueAnimator.setRepeatMode(ValueAnimator.RESTART);
        valueAnimator.start();
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        // 只對EXACTLY的條件的寬高處理
        setMeasuredDimension(Math.min(widthSize,heightSize),Math.min(widthSize,heightSize));

    }

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

        // 爲加強動畫效果,每次對畫布旋轉不同角度
        canvas.rotate(360.0f*mAnimatorValue-45.0f,width/2,height/2);
        // 初始化截取的路徑
        dst.reset();
        dst.lineTo(0,0); // 消除硬件加速的影響
        // 更新截取的開始值和結束值:當mAnimatorValue爲0或1時,兩個值相等
        stop = mAnimatorValue*mLength;
        start = (float) (stop - ((0.5 - Math.abs(mAnimatorValue - 0.5)) * mLength));
        // 截取路徑後,並繪製路徑
        pathMeasure.getSegment(start, stop, dst, true);
        canvas.drawPath(dst,paint);

    }
}

參考:

PathMeasure之迷徑追蹤

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章