自定義view之可伸縮的圓弧與扇形

原文地址: 自定義view之可伸縮的圓弧與扇形

上一篇文章中講解了如何自定義一個帶有清除按鈕的Edittext,這次講解如何實(shí)現(xiàn)一個帶有動畫效果的圓弧及扇形圖。先簡單看一下效果:

簡單看一下這兩個圖形:

  1. 弧形是根據(jù)輸入的一個范圍在0-360范圍內(nèi)的值,增加時會顯示一個逐漸增加的動畫,減少時也會有一個逐漸減少的動畫,這個動畫的插值器我設(shè)置的是先增速后減速。
  2. 扇形百分比動畫是一個每次都會從開始的位置從新生成的動畫,也可以做成類似于圓弧動畫的效果。

自定義圓弧類

這個圓弧類我們直接繼承自View,然后必然實(shí)現(xiàn)構(gòu)造方法。

 private float value;//用戶設(shè)置的值
    private Paint arcPaint;//要用到的畫筆
    private RectF rectF;//繪制的范圍
    private float oldValue;//過時的值

public ArcProgress(Context context) {
        super(context);
        init();
    }

    public ArcProgress(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public ArcProgress(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

在構(gòu)造器中我們定義了一個init方法,這個方法主要是用來初始化一些東西,我只是初始化了畫筆,注意一點(diǎn),不能將rectF在這時候設(shè)置范圍,因?yàn)槲覀兪且鶕?jù)用戶設(shè)置的大小來填充rectF。

private void init() {
        arcPaint = new Paint();
        arcPaint.setAntiAlias(true);//抗鋸齒
        arcPaint.setStyle(Paint.Style.STROKE);//只繪制圓弧邊界
        arcPaint.setColor(Color.parseColor("#2c2c2c"));
        arcPaint.setStrokeWidth(50);//50px的圓弧寬度
    }

畫筆大家應(yīng)該用的都很熟練了,此處只說一點(diǎn),如果不設(shè)置setStyle,默認(rèn)是FILL,是填充效果。這個畫筆是在ondraw方法中用來繪制圓弧的。

onsizechange方法

還記得上節(jié)內(nèi)容中將的view的初始化順序,

constructor->onmeasure->onDraw

現(xiàn)在引入一個新的方法,onSizeChanged(int w, int h, int oldw, int oldh)

這個方法是在控件的布局參數(shù)發(fā)生變化時調(diào)用的,oldvalue在第一次加載時是0。

這個方法是在onmeasure之后ondraw之前調(diào)用。調(diào)用順序?yàn)椋?/p>

constructor->onmeasure->onSizeChanged->onDraw

在這個方法中我們定義了rectF的范圍

rectF = new RectF(50, 50, getMeasuredHeight() - 50, getMeasuredHeight() - 50);

我們設(shè)置了一個邊界范圍是50px,目的是看的更清楚,關(guān)于stroke的繪制是在寬度外還是內(nèi)的問題此處不做詳解。

ondraw方法

canvas.drawArc(rectF, 270, value, false, arcPaint);

方法只有一個最簡單的繪制圓弧,注意第二個參數(shù)是繪制的起始角度,第三個參數(shù)是繪制的角度,為不是結(jié)束角度,結(jié)束角度是起始角度+繪制角度。第三個參數(shù)設(shè)置為false,這時候繪制的圓弧而不是扇形。

設(shè)置值的接口

public void setValue(final float v) {
        ValueAnimator animator = ValueAnimator.ofFloat(oldValue, v);
        oldValue = v;
        if (Math.abs(v - oldValue) > 180) {
            animator.setDuration(1000);
        } else {
            animator.setDuration(500);
        }
        animator.setInterpolator(new AccelerateDecelerateInterpolator());
        animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                value = (float) animation.getAnimatedValue();
                Log.d("change", "=" + animation.getAnimatedValue());
                invalidate();
            }
        });
        animator.start();
    }

這個接口是要暴漏出來的,設(shè)置為public。

此處我們定義了一個ValueAnimator,用oldv接收設(shè)置的v,此處為了用戶體驗(yàn),當(dāng)變化角度小于180時設(shè)置持續(xù)時間為500ms,當(dāng)變化角度大于180度時設(shè)置持續(xù)時間為500ms。

定義的插值器是一個先加速后減速的插值器。

重要的是為animator中添加UpdateListener,這個函數(shù)會持續(xù)返回給我們一個ValueAnimator對象,這個對象包含了我們在插值器中設(shè)定的不同的時間段對應(yīng)的值,相當(dāng)于一個時間函數(shù)。回調(diào)函數(shù)一直持續(xù)到動畫結(jié)束。

此處我們通過ValueAnimator取出對應(yīng)的數(shù)值,然后通過調(diào)用invalidate方法來刷新當(dāng)前view,產(chǎn)生一個不斷在動的效果。

這個invalid會刷新所有的可見view,但是必須工作在ui線程。

這樣就完成了一個簡單的view

button = (Button) view.findViewById(R.id.btn_change);
        circleProgress.setValue(300);
        progress.setValue(10, 10, 10);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                circleProgress.setValue(new Random().nextInt(360));
                progress.setValue(new Random().nextInt(20), new Random().nextInt(20),
                        new Random().nextInt(20));
            }
        });

這是調(diào)用代碼,點(diǎn)擊按鈕生成隨機(jī)的一個值。

同樣的方式我們可以用來設(shè)置扇形。基本原理相似。
在ondraw方法中要設(shè)置為如下

canvas.drawArc(rectF, 0, 360 * percentOne, true, circlePaint1);
        canvas.drawArc(rectF, 360 * percentOne, 360 * percentTwo, true, circlePaint2);
        canvas.drawArc(rectF, 360 * (percentOne + percentTwo), 360 * percentThree, true, circlePaint3);

drawArc的第三個參數(shù)要用true,才能繪制扇形。

設(shè)置值的方法如下:

public void setValue(int value1, int value2, int value3) {
        int sum = value1 + value2 + value3;
        percentOne = (float) value1 / sum;
        newPercentOne = percentOne;
        percentTwo = (float) value2 / sum;
        newPercentTwo = percentTwo;
        percentThree = (float) value3 / sum;
        newPercentThree = percentThree;
        ValueAnimator animator1 = ValueAnimator.ofFloat(oldPercentOne, newPercentOne);
        ValueAnimator animator2 = ValueAnimator.ofFloat(oldPercentTwo, newPercentTwo);
        ValueAnimator animator3 = ValueAnimator.ofFloat(oldPercentThree, newPercentThree);
        animator1.setDuration(1000);
        animator2.setDuration(1000);
        animator3.setDuration(1000);
        animator1.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                percentOne = (float) animation.getAnimatedValue();

            }
        });
        animator2.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                percentTwo = (float) animation.getAnimatedValue();
            }
        });
        animator3.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                percentThree= (float) animation.getAnimatedValue();
                invalidate();
            }
        });
        AnimatorSet animatorSet=new AnimatorSet();
        animatorSet.playTogether(animator1,animator2,animator3);
        animatorSet.start();
        Log.d("rect :", "percentone=" + percentOne + ",percenttwo=" + percentTwo + ",percentthree=" + percentThree);

    }

我們用一個animatorset來包裹著三個動畫同時發(fā)生,當(dāng)然也可以按順序發(fā)生,效果如下:


最后,關(guān)于動畫的使用

安卓Property Animator動畫詳解(一)-官方文檔

安卓Property Animator動畫詳解(二)-自定義屬性

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容