通過上兩篇的講解,我們對(duì) ValueAnimator 的動(dòng)畫的整個(gè)過程應(yīng)該都已經(jīng)有較深入的理解,不過還有兩個(gè)概念我們還沒有講解關(guān)鍵幀和 ofObject(),關(guān)鍵幀的部分涉及問題比較多,我們將其放在系列的末尾再講,這篇著重講一下 ofObject 函數(shù)的使用
前面我們講了 ofInt()和 ofFloat()來定義動(dòng)畫,但 ofInt()只能傳入 Integer 類型的值,而 ofFloat()則只能傳入 Float 類型的值。那如果我們需要操作其它類型的變量要怎么辦呢?其實(shí) ValueAnimator 還有一個(gè)函數(shù) ofObject(),可以傳進(jìn)去任何類型的變量,定義如下:
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);
它有兩個(gè)參數(shù),第一個(gè)是自定義的 Evaluator,第二個(gè)是可變長(zhǎng)參數(shù),Object 類型的; 大家可能會(huì)疑問,為什么要強(qiáng)制傳進(jìn)去自定義的 Evaluator?首先,大家知道 Evaluator 的作用是根據(jù)當(dāng)前動(dòng)畫的顯示進(jìn)度,計(jì)算出當(dāng)前進(jìn)度下把對(duì)應(yīng)的值。那既然 Object 對(duì)象是我們自定的,那必然從進(jìn)度到值的轉(zhuǎn)換過程也必須由我們來做,不然系統(tǒng)哪知道你要轉(zhuǎn)成個(gè)什么鬼。 好了,現(xiàn)在我們先簡(jiǎn)單看一下 ofObject 這個(gè)怎么用。 我們先來看看我們要實(shí)現(xiàn)的效果:
http://wiki.jikexueyuan.com/project/android-animation/images/66.gif" alt="" />
從效果圖中可以看到,按鈕上的字母從 A 變化到 Z,剛開始變的慢,后來逐漸加速;
ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('A'),new Character('Z'));
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
char text = (char)animation.getAnimatedValue();
tv.setText(String.valueOf(text));
}
});
animator.setDuration(10000);
animator.setInterpolator(new AccelerateInterpolator());
animator.start();
這里注意三點(diǎn): 第一,構(gòu)造時(shí):
ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('A'),new Character('Z'));
我們自定義的一個(gè) CharEvaluator,這個(gè)類實(shí)現(xiàn),后面會(huì)講;在初始化動(dòng)畫時(shí),傳進(jìn)去的是 Character 對(duì)象,一個(gè)是字母 A,一個(gè)是字母 Z; 我們這里要實(shí)現(xiàn)的效果是,對(duì) Character 對(duì)象來做動(dòng)畫,利用動(dòng)畫自動(dòng)從字母 A 變到字母 Z,具體怎么實(shí)現(xiàn)就是 CharEvaluator 的事了,這里我們只需要知道,在構(gòu)造時(shí)傳進(jìn)去的是兩個(gè) Character 對(duì)象 第二:看監(jiān)聽:
char text = (char)animation.getAnimatedValue();
tv.setText(String.valueOf(text));
通過 animation.getAnimatedValue()得到當(dāng)前動(dòng)畫的字符,然后把字符設(shè)置給 textview;大家知道我們構(gòu)造時(shí)傳進(jìn)去的值類型是 Character 對(duì)象,所以在動(dòng)畫過程中通過 Evaluator 返回的值類型必然跟構(gòu)造時(shí)的類型是一致的,也是 Character 第三:插值器
animator.setInterpolator(new AccelerateInterpolator());
我們使用的是加速插值器,加速插值器的特點(diǎn)就是隨著動(dòng)畫的進(jìn)行,速度會(huì)越來越快,這點(diǎn)跟我們上面的效果圖是一致的。 下面最關(guān)鍵的就是看 CharEvaluator 是怎么實(shí)現(xiàn)的了,先拋開的代碼,我們先講一個(gè)點(diǎn),ASCII 碼中數(shù)值與字符的轉(zhuǎn)換方法。 我們知道在 ASCII 碼表中,每個(gè)字符都是有數(shù)字跟他一一對(duì)應(yīng)的,字母 A 到字母 Z 之間的所有字母對(duì)應(yīng)的數(shù)字區(qū)間為 65 到 90; 而且在程序中,我們能通過數(shù)字強(qiáng)轉(zhuǎn)成對(duì)應(yīng)的字符。 比如: 數(shù)字轉(zhuǎn)字符:
char temp = (char)65;//得到的 temp 的值就是大寫字母 A
字符轉(zhuǎn)數(shù)字:
char temp = 'A';
int num = (int)temp;
在這里得到的 num 值就是對(duì)應(yīng)的 ASCII 碼值 65; 好了,在我們理解了 ASCII 碼數(shù)值與對(duì)應(yīng)字符的轉(zhuǎn)換原理之后,再來看看 CharEvaluator 的實(shí)現(xiàn):
public class CharEvaluator implements TypeEvaluator<Character> {
@Override
public Character evaluate(float fraction, Character startValue, Character endValue) {
int startInt = (int)startValue;
int endInt = (int)endValue;
int curInt = (int)(startInt + fraction *(endInt - startInt));
char result = (char)curInt;
return result;
}
}
在這里,我們就利用 A-Z 字符在 ASCII 碼表中對(duì)應(yīng)數(shù)字是連續(xù)且遞增的原理,先求出來對(duì)應(yīng)字符的數(shù)字值,然后再轉(zhuǎn)換成對(duì)應(yīng)的字符。代碼難度不大,就不再細(xì)講了。 源碼在文章底部給出 好了,到這里,有關(guān) ofObject()的使用大家應(yīng)該就會(huì)了,上面我們說過,ofObject()能夠初始化任何對(duì)象,下面我們就稍微加深些難度, 我們自定義一個(gè)類對(duì)象,然后利用 ofObject()來構(gòu)造這個(gè)對(duì)象的動(dòng)畫。
我們先看看這部分,我們將實(shí)現(xiàn)的效果:
http://wiki.jikexueyuan.com/project/android-animation/images/67.gif" alt="" />
在這里,我們自定義了一個(gè) View,在這個(gè) view 上畫一個(gè)圓,但這個(gè)圓是有動(dòng)畫效果的。從效果中可以看出使用的插值器應(yīng)該是回彈插值器(BounceInterpolator) 下面就來看看這個(gè)動(dòng)畫是怎么做出來的
public class Point {
private int radius;
public Point(int radius){
this.radius = radius;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
}
point 類內(nèi)容很簡(jiǎn)單,只有一個(gè)成員變量:radius 表示當(dāng)前 point 的半徑。
public class MyPointView extends View {
private Point mCurPoint;
public MyPointView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mCurPoint != null){
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300,300,mCurPoint.getRadius(),paint);
}
}
public void doPointAnim(){
ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurPoint = (Point)animation.getAnimatedValue();
invalidate();
}
});
animator.setDuration(1000);
animator.setInterpolator(new BounceInterpolator());
animator.start();
}
}
(1)、doPointAnim()函數(shù) 在這段代碼中,首先來看看供外部調(diào)用開始動(dòng)畫的 doPointAnim()函數(shù):
public void doPointAnim(){
ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurPoint = (Point)animation.getAnimatedValue();
invalidate();
}
});
animator.setDuration(1000);
animator.setInterpolator(new BounceInterpolator());
animator.start();
}
同樣,先來看 ofObject 的構(gòu)造動(dòng)畫的方法:
ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));
在構(gòu)造動(dòng)畫時(shí),動(dòng)畫所對(duì)應(yīng)的值的類型是 Point 對(duì)象,那說明我們自定義的 PointEvaluator 中的返回值也必然是 Point 了。有關(guān) PointEvaluator 的實(shí)現(xiàn)后面再講 然后再來看看動(dòng)畫過程監(jiān)聽:
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
mCurPoint = (Point)animation.getAnimatedValue();
invalidate();
}
});
在監(jiān)聽過程中,先根據(jù) animation.getAnimatedValue()得到當(dāng)前動(dòng)畫進(jìn)度所對(duì)應(yīng)的 Point 實(shí)例,保存在 mCurPoint 中,然后強(qiáng)制刷新 (2)、OnDraw()函數(shù) 在強(qiáng)制刷新之后,就會(huì)走到 OnDraw()函數(shù)下面:
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mCurPoint != null){
Paint paint = new Paint();
paint.setAntiAlias(true);
paint.setColor(Color.RED);
paint.setStyle(Paint.Style.FILL);
canvas.drawCircle(300,300,mCurPoint.getRadius(),paint);
}
}
onDraw 函數(shù)沒什么難度,就是根據(jù) mCurPoint 的半徑在(300,300)的位置畫出來圓形,有關(guān)繪圖的知識(shí)大家可以參考另一個(gè)系列《android Graphics(一):概述及基本幾何圖形繪制》 (3)、PointEvaluator 在構(gòu)造 ofObject 中,我們也可以知道,初始值和動(dòng)畫中間值的類型都是 Point 類型,所以 PointEvaluator 輸入的返回類型都應(yīng)該是 Point 類型的,先看看 PointEvaluator 的完整代碼:
public class PointEvaluator implements TypeEvaluator<Point> {
@Override
public Point evaluate(float fraction, Point startValue, Point endValue) {
int start = startValue.getRadius();
int end = endValue.getRadius();
int curValue = (int)(start + fraction * (end - start));
return new Point(curValue);
}
}
這段代碼其實(shí)比較容易理解,就是根據(jù)初始半徑和最終半徑求出當(dāng)前動(dòng)畫進(jìn)程所對(duì)應(yīng)的半徑值,然后新建一個(gè) Point 對(duì)象返回。
首先在 main.xml 中添加對(duì)應(yīng)的控件布局:從效果圖中也可以看到,我們將 MyPointView 是布局在最下方的,布局代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<Button
android:id="@+id/btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:padding="10dp"
android:text="start anim"
/>
<Button
android:id="@+id/btn_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:padding="10dp"
android:text="cancel anim"
/>
<TextView
android:id="@+id/tv"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:gravity="center"
android:padding="10dp"
android:background="#ffff00"
android:text="Hello qijian"/>
<com.harvic.BlogValueAnimator4.MyPointView
android:id="@+id/pointview"
android:layout_below="@id/tv"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
其實(shí)也沒什么難度,就是在原來的布局代碼下面加一個(gè) MyPointView 控件,難度不大,不再細(xì)講了 然后我們來看看在 MyActivity.java 中是怎么來用的吧
public class MyActivity extends Activity {
private Button btnStart;
private MyPointView mPointView;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
btnStart = (Button) findViewById(R.id.btn);
mPointView = (MyPointView)findViewById(R.id.pointview);
btnStart.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPointView.doPointAnim();
}
});
}
}
這段代碼沒什么難度,就是在點(diǎn)擊 start anim 按鈕的時(shí)候,調(diào)用 mPointView.doPointAnim()方法開始動(dòng)畫。
源碼在文章底部給出
好了,這篇到這里就結(jié)束了,其實(shí)沒想再開這一篇單獨(dú)來講 ofObject 的,但上篇實(shí)在是太長(zhǎng)了,所以只能再開一篇,這篇講了兩個(gè)實(shí)例,確實(shí)有些嘮叨,畢竟開了一篇還是要多寫點(diǎn),不然也太辜負(fù)這一篇文章的地了。就此,有關(guān) VauleAnimator 的所有問題我們都講完了,下一篇講會(huì)講述有關(guān) ObjectAnimator 系列的知識(shí)。
如果本文有幫到你,記得加關(guān)注哦
源碼下載地址:
csdn:http://download.csdn.net/detail/u013210620/9420359
github:https://github.com/harvic/BlogResForGitHub
請(qǐng)大家尊重原創(chuàng)者版權(quán),轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/harvic880925/article/details/50549385 謝謝