前面咱們講了ofInt()和ofFloat()來定義動畫,但ofInt()只能傳入Integer類型的值,而ofFloat()則只能傳入Float類型的值。那若是咱們須要操做其它類型的變量要怎麼辦呢?其實ValueAnimator還有一個函數ofObject(),能夠傳進去任何類型的變量,定義以下:java
public static ValueAnimator ofObject(TypeEvaluator evaluator, Object... values);
它有兩個參數,第一個是自定義的Evaluator,第二個是可變長參數,Object類型的;
你們可能會疑問,爲何要強制傳進去自定義的Evaluator?首先,你們知道Evaluator的做用是根據當前動畫的顯示進度,計算出當前進度下把對應的值。那既然Object對象是咱們自定的,那必然從進度到值的轉換過程也必須由咱們來作,否則系統哪知道你要轉成個什麼鬼。
好了,如今咱們先簡單看一下ofObject這個怎麼用。
咱們先來看看咱們要實現的效果: android
從效果圖中能夠看到,按鈕上的字母從A變化到Z,剛開始變的慢,後來逐漸加速;canvas
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();
這裏注意三點:
第一,構造時:ide
ValueAnimator animator = ValueAnimator.ofObject(new CharEvaluator(),new Character('A'),new Character('Z'));
咱們自定義的一個CharEvaluator,這個類實現,後面會講;在初始化動畫時,傳進去的是Character對象,一個是字母A,一個是字母Z;
咱們這裏要實現的效果是,對Character對象來作動畫,利用動畫自動從字母A變到字母Z,具體怎麼實現就是CharEvaluator的事了,這裏咱們只須要知道,在構造時傳進去的是兩個Character對象 函數
第二:看監聽:佈局
char text = (char)animation.getAnimatedValue(); tv.setText(String.valueOf(text));
經過animation.getAnimatedValue()獲得當前動畫的字符,而後把字符設置給textview;你們知道咱們構造時傳進去的值類型是Character對象,因此在動畫過程當中經過Evaluator返回的值類型必然跟構造時的類型是一致的,也是Character 動畫
第三:插值器this
animator.setInterpolator(new AccelerateInterpolator());
咱們使用的是加速插值器,加速插值器的特色就是隨着動畫的進行,速度會愈來愈快,這點跟咱們上面的效果圖是一致的。
下面最關鍵的就是看CharEvaluator是怎麼實現的了,先拋開的代碼,咱們先講一個點,ASCII碼中數值與字符的轉換方法。
咱們知道在ASCII碼錶中,每一個字符都是有數字跟他一一對應的,字母A到字母Z之間的全部字母對應的數字區間爲65到90;
並且在程序中,咱們能經過數字強轉成對應的字符。
好比: lua
數字轉字符:spa
char temp = (char)65;//獲得的temp的值就是大寫字母A
字符轉數字:
char temp = 'A'; int num = (int)temp;
在這裏獲得的num值就是對應的ASCII碼值65;
好了,在咱們理解了ASCII碼數值與對應字符的轉換原理以後,再來看看CharEvaluator的實現:
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碼錶中對應數字是連續且遞增的原理,先求出來對應字符的數字值,而後再轉換成對應的字符。代碼難度不大,就再也不細講了。
好了,到這裏,有關ofObject()的使用你們應該就會了,上面咱們說過,ofObject()可以初始化任何對象,下面咱們就稍微加深些難度, 咱們自定義一個類對象,而後利用ofObject()來構造這個對象的動畫。
咱們先看看這部分,咱們將實現的效果:
在這裏,咱們自定義了一個View,在這個view上畫一個圓,但這個圓是有動畫效果的。從效果中能夠看出使用的插值器應該是回彈插值器(BounceInterpolator)
下面就來看看這個動畫是怎麼作出來的
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類內容很簡單,只有一個成員變量:radius表示當前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(); } }
在這段代碼中,首先來看看供外部調用開始動畫的doPointAnim()函數:
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的構造動畫的方法:
ValueAnimator animator = ValueAnimator.ofObject(new PointEvaluator(),new Point(20),new Point(200));
在構造動畫時,動畫所對應的值的類型是Point對象,那說明咱們自定義的PointEvaluator中的返回值也必然是Point了。有關PointEvaluator的實現後面再講
而後再來看看動畫過程監聽:
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator animation) { mCurPoint = (Point)animation.getAnimatedValue(); invalidate(); } });
在監聽過程當中,先根據animation.getAnimatedValue()獲得當前動畫進度所對應的Point實例,保存在mCurPoint中,而後強制刷新
在強制刷新以後,就會走到OnDraw()函數下面:
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函數沒什麼難度,就是根據mCurPoint的半徑在(300,300)的位置畫出來圓形,有關繪圖的知識你們能夠參考另外一個系列《android Graphics(一):概述及基本幾何圖形繪製》
在構造ofObject中,咱們也能夠知道,初始值和動畫中間值的類型都是Point類型,因此PointEvaluator輸入的返回類型都應該是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); } }
這段代碼其實比較容易理解,就是根據初始半徑和最終半徑求出當前動畫進程所對應的半徑值,而後新建一個Point對象返回。
首先在main.xml中添加對應的控件佈局:從效果圖中也能夠看到,咱們將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>
其實也沒什麼難度,就是在原來的佈局代碼下面加一個MyPointView控件,難度不大,再也不細講了
而後咱們來看看在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(); } }); } }
這段代碼沒什麼難度,就是在點擊start anim按鈕的時候,調用mPointView.doPointAnim()方法開始動畫。