Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> android自定義view 模仿win10進度條

android自定義view 模仿win10進度條

編輯:關於Android編程

先上預覽圖:

預覽圖

流程

1.一個勻速圓周運動的點 2.多個勻速圓周運動的點 3.多個圓周運動的點,速度由快到慢 4.點與點之間的間距線性減少,動畫的最後合為一個點 5.為了讓動畫看起來更加流暢,需要在動畫即將結束的時候手動繪制點

核心控件

PathMeasure:截取Path中的一部分並顯示 ValueAnimator:完成動畫從初始值平滑的過度到結束值的效果,同時還負責管理動畫的播放次數、播放模式、以及對動畫設置監聽器等

流程一

思路

先用path畫一個圓 ValueAnimator設置為0f-1f的平滑 用PathMeasure根據ValueAnimator返回的值截取path上的一個點
    private Paint mPaint;
    private Path mPath;
    private PathMeasure mPathMeasure;
    private int mWidth,mHeight;
    private ValueAnimator valueAnimator;
    //用這個來接受ValueAnimator的返回值,代表整個動畫的進度
    private float t;
初始化畫筆
    mPaint = new Paint();
    mPaint.setStyle(Paint.Style.STROKE);
    mPaint.setStrokeWidth(15);
    mPaint.setColor(Color.WHITE);
    //設置畫筆為園筆
    mPaint.setStrokeCap(Paint.Cap.ROUND);
    //抗鋸齒
    mPaint.setAntiAlias(true);
初始化Path和mPathMeasure
這裡角度不能選360,否則會測量失誤,具體原因和android的內部優化有關
    mPath = new Path();
    RectF rect = new RectF(-150,-150,150,150);
    mPath.addArc(rect,-90,359.9f);
    mPathMeasure = new PathMeasure(mPath,false);
初始化ValueAnimator
        valueAnimator = ValueAnimator.ofFloat(0f,1f).setDuration(3000);
        valueAnimator.setRepeatCount(-1);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                t = (float) animation.getAnimatedValue();
                invalidate();
            }
        });

這裡的ValueAnimator設置的是一個時長3秒的動畫,再這3秒中,ValueAnimator會返回一個由0f-1f平滑的數字
ValueAnimator.ofFloat(0f,1f).setDuration(3000)
在這裡我們用t來接受返回值,同時刷新視圖

t = (float) animation.getAnimatedValue();
invalidate();

這裡寫圖片描述
這裡可以看出t的值,有0到1,這裡就可以把t理解為我們這個動畫的進度

開始繪制
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(mWidth/2,mHeight/2);
        Path dst = new Path();
        mPathMeasure.getSegment(mPathMeasure.getLength()*t,mPathMeasure.getLength()*t+1,dst,true);
        canvas.drawPath(dst,mPaint);

    }
結果圖:
這裡寫圖片描述

流程二

思路

我們設置讓t每間隔0.05就畫一個點,總共畫4個點,注意這裡getSegment()的最後一個要設置為true來保證畫出來的是多個點而不是一條線
    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(mWidth/2,mHeight/2);
        Path dst = new Path();
        int num = (int) (t/0.05);
        float s,y,x;
        switch(num){
            default:
            case 3:
                x = t-0.15f;
                s = mPathMeasure.getLength();
                y = s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 2:
                x = t-0.10f;
                s = mPathMeasure.getLength();
                y = s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 1:
                x = t-0.05f;
                s = mPathMeasure.getLength();
                y = s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 0:
                x = t;
                s = mPathMeasure.getLength();
                y = s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
                break;
        }
        canvas.drawPath(dst,mPaint);
    }
結果圖
這裡寫圖片描述

流程三

思路

我們先繪制出路程-時間的函數圖像
這裡寫圖片描述
函數為y = -x*x + 2*x,當x=1時,y=mPathMeasure.getLength();
設s = mPathMeasure.getLength();
最終我們套用函數:y = -s*x*x+2*s*x;
這裡的Y軸代表的是path的長度,X軸對應時間
所以把流程二中的y = s*x改成y = -s*x*x+2*s*x即可
<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;"> switch(num){ default: case 3: x = t-0.15f; s = mPathMeasure.getLength(); y = -s*x*x+2*s*x; mPathMeasure.getSegment(y,y+1,dst,true); case 2: x = t-0.10f; s = mPathMeasure.getLength(); y = -s*x*x+2*s*x; mPathMeasure.getSegment(y,y+1,dst,true); case 1: x = t-0.05f; s = mPathMeasure.getLength(); y = -s*x*x+2*s*x; mPathMeasure.getSegment(y,y+1,dst,true); case 0: x = t; s = mPathMeasure.getLength(); y = -s*x*x+2*s*x; mPathMeasure.getSegment(y,y+1,dst,true); break; } canvas.drawPath(dst,mPaint); 結果圖
這裡寫圖片描述

流程四

思路

雖然流程3中點與點的間距已經開始減少,不過這只是因為速度不同間距才改變的,我們的目的是讓這些點到最後合並為1個點,也就是說開始的時候每個點的X間距0.05,結束的時候要讓他們的X相同
目前點之間X的間距函數如下:
這裡寫圖片描述
我們最後要讓當X=1時,他們的Y值相等,而且他們X的間距由0.05線性平滑到0
這裡寫圖片描述
看函數圖像已經很清楚了,修改後代碼如下:

        switch(num){
            default:
            case 3:
                x = t-0.15f*(1-t);
                s = mPathMeasure.getLength();
                y = -s*x*x+2*s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 2:
                x = t-0.10f*(1-t);
                s = mPathMeasure.getLength();
                y = -s*x*x+2*s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 1:
                x = t-0.05f*(1-t);
                s = mPathMeasure.getLength();
                y = -s*x*x+2*s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 0:
                x = t;
                s = mPathMeasure.getLength();
                y = -s*x*x+2*s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
                break;
        }
結果圖
這裡寫圖片描述

流程五

思路

這裡已經完成了99.9%了,但細心的同學會發現,進度條每次轉動一圈聚成一個點後都會閃一下,這是因為重新開始動畫刷新視圖的原因,這裡的補救方法就是我們在動畫快結束的時候手動畫一個點

        if(t>=0.95){
            canvas.drawPoint(0,-150,mPaint);
        }

這樣我們就完成了這個進度條
這裡寫圖片描述
最後這裡附上源碼:

package com.example.zhangml.view;

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.view.View;

/**
 * Created by zhangml on 2016/9/12 0012.
 */
public class Win8Search extends View{

    private Paint mPaint;
    private Path mPath;
    private PathMeasure mPathMeasure;
    private int mWidth,mHeight;
    private ValueAnimator valueAnimator;
    //用這個來接受ValueAnimator的返回值,代表整個動畫的進度
    private float t;

    public Win8Search(Context context) {
        super(context);
    }

    public Win8Search(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
        valueAnimator.start();
    }

    private void init() {

        mPaint = new Paint();
        mPaint.setStyle(Paint.Style.STROKE);
        mPaint.setStrokeWidth(15);
        mPaint.setColor(Color.WHITE);
        //設置畫筆為園筆
        mPaint.setStrokeCap(Paint.Cap.ROUND);
        //抗鋸齒
        mPaint.setAntiAlias(true);

        mPath = new Path();
        RectF rect = new RectF(-150,-150,150,150);
        mPath.addArc(rect,-90,359.9f);

        mPathMeasure = new PathMeasure(mPath,false);

        valueAnimator = ValueAnimator.ofFloat(0f,1f).setDuration(3000);
        valueAnimator.setRepeatCount(-1);
        valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                t = (float) animation.getAnimatedValue();
                invalidate();
            }
        });
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        canvas.translate(mWidth/2,mHeight/2);
        Path dst = new Path();
        if(t>=0.95){
            canvas.drawPoint(0,-150,mPaint);
        }
        int num = (int) (t/0.05);
        float s,y,x;
        switch(num){
            default:
            case 3:
                x = t-0.15f*(1-t);
                s = mPathMeasure.getLength();
                y = -s*x*x+2*s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 2:
                x = t-0.10f*(1-t);
                s = mPathMeasure.getLength();
                y = -s*x*x+2*s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 1:
                x = t-0.05f*(1-t);
                s = mPathMeasure.getLength();
                y = -s*x*x+2*s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
            case 0:
                x = t;
                s = mPathMeasure.getLength();
                y = -s*x*x+2*s*x;
                mPathMeasure.getSegment(y,y+1,dst,true);
                break;
        }
        canvas.drawPath(dst,mPaint);
    }

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


}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved