上一篇文章中講解了如何自定義一個帶有清除按鈕的Edittext,這次講解如何實現一個帶有動畫效果的圓弧及扇形圖。先簡單看一下效果:
簡單看一下這兩個圖形:
- 弧形是根據輸入的一個範圍在0-360範圍內的值,增加時會顯示一個逐漸增加的動畫,減少時也會有一個逐漸減少的動畫,這個動畫的插值器我設定的是先增速後減速。
- 扇形百分比動畫是一個每次都會從開始的位置從新生成的動畫,也可以做成類似於圓弧動畫的效果。
自定義圓弧類
這個圓弧類我們直接繼承自View,然後必然實現構造方法。
private float value;//使用者設定的值
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();
}複製程式碼
在構造器中我們定義了一個init方法,這個方法主要是用來初始化一些東西,我只是初始化了畫筆,注意一點,不能將rectF在這時候設定範圍,因為我們是要根據使用者設定的大小來填充rectF。
private void init() {
arcPaint = new Paint();
arcPaint.setAntiAlias(true);//抗鋸齒
arcPaint.setStyle(Paint.Style.STROKE);//只繪製圓弧邊界
arcPaint.setColor(Color.parseColor("#2c2c2c"));
arcPaint.setStrokeWidth(50);//50px的圓弧寬度
}複製程式碼
畫筆大家應該用的都很熟練了,此處只說一點,如果不設定setStyle,預設是FILL,是填充效果。這個畫筆是在ondraw方法中用來繪製圓弧的。
onsizechange方法
還記得上節內容中將的view的初始化順序,
constructor->onmeasure->onDraw複製程式碼
現在引入一個新的方法,onSizeChanged(int w, int h, int oldw, int oldh)
這個方法是在控制元件的佈局引數發生變化時呼叫的,oldvalue在第一次載入時是0。
這個方法是在onmeasure之後ondraw之前呼叫。呼叫順序為:
constructor->onmeasure->onSizeChanged->onDraw複製程式碼
在這個方法中我們定義了rectF的範圍
rectF = new RectF(50, 50, getMeasuredHeight() - 50, getMeasuredHeight() - 50);複製程式碼
我們設定了一個邊界範圍是50px,目的是看的更清楚,關於stroke的繪製是在寬度外還是內的問題此處不做詳解。
ondraw方法
canvas.drawArc(rectF, 270, value, false, arcPaint);複製程式碼
方法只有一個最簡單的繪製圓弧,注意第二個引數是繪製的起始角度,第三個引數是繪製的角度,為不是結束角度,結束角度是起始角度+繪製角度。第三個引數設定為false,這時候繪製的圓弧而不是扇形。
設定值的介面
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();
}複製程式碼
這個介面是要暴漏出來的,設定為public。
此處我們定義了一個ValueAnimator,用oldv接收設定的v,此處為了使用者體驗,當變化角度小於180時設定持續時間為500ms,當變化角度大於180度時設定持續時間為500ms。
定義的插值器是一個先加速後減速的插值器。
重要的是為animator中新增UpdateListener,這個函式會持續返回給我們一個ValueAnimator物件,這個物件包含了我們在插值器中設定的不同的時間段對應的值,相當於一個時間函式。回撥函式一直持續到動畫結束。
此處我們通過ValueAnimator取出對應的數值,然後通過呼叫invalidate方法來重新整理當前view,產生一個不斷在動的效果。
這個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));
}
});複製程式碼
這是呼叫程式碼,點選按鈕生成隨機的一個值。
同樣的方式我們可以用來設定扇形。基本原理相似。
在ondraw方法中要設定為如下
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的第三個引數要用true,才能繪製扇形。
設定值的方法如下:
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來包裹著三個動畫同時發生,當然也可以按順序發生,效果如下:
最後,關於動畫的使用