昨天,我們通過Java代碼的形式實作了一些簡單的屬性動畫效果,今天,讓我們通過xml檔案來實作一下同樣的效果,之後,我們再用屬性動畫實作小球的運動。雖然xml寫起來比較麻煩,不像昨天代碼中編寫的隻需要幾行代碼就搞定,但是xml形式好在易于重用,不需要在每個界面都實作一下同樣的效果,下面讓我們看看如何實作吧:
使用XML來編寫動畫,首先要在res目錄下面建立一個animator檔案夾,所有屬性動畫的XML檔案都應該存放在這個檔案夾當中。然後在XML檔案中我們一共可以使用如下三種标簽:
- <animator> 對應代碼中的ValueAnimator
- <objectAnimator> 對應代碼中的ObjectAnimator
- <set> 對應代碼中的AnimatorSet
實作昨天效果代碼如下:
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:ordering="together">
<objectAnimator
android:duration="2000"
android:propertyName="translationX"
android:valueFrom="-600"
android:valueTo="0"
android:valueType="floatType" >
</objectAnimator>
<objectAnimator
android:duration="2000"
android:propertyName="alpha"
android:valueFrom="0"
android:valueTo="1"
android:valueType="floatType" >
</objectAnimator>
</set>
set标簽中的ordering屬性為動畫制定執行順序,然後我們在activity啟動這個動畫:
- Animator animator = AnimatorInflater.loadAnimator(context, R.animator.anim_test);
- animator.setTarget(textview);
- animator.start();
效果就不示範了,同昨天一樣的。
下面,我們來看一下屬性動畫更加進階一點的用法,之是以稱之為進階,主要展現在屬性動畫中TypeEvaluator接口實作中,TypeEvaluator是用來幹什麼的呢?
- 屬性動畫進階- TypeEvaluator的使用
簡單來說,就是告訴動畫系統如何從初始值過度到結束值。因為之前的動畫過渡過程是系統負責的,我們并沒有做出相應的幹涉和處理,如果你想實作更加複雜和炫酷的動畫效果,這個接口相信你會經常用到。
上一篇文章中學到的ValueAnimator.ofFloat()方法就是實作了初始值與結束值之間的平滑過度,那麼這個平滑過度是怎麼做到的呢?其實就是系統内置了一個FloatEvaluator,它通過計算告知動畫系統如何從初始值過度到結束值,我們來看一下FloatEvaluator的代碼實作:
[java] view plain copy
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIn5Gcu82Yp9VRE90Qvw1c0V2czF2LcRXZu5ibkN3YuUGZvN2Lc9CX6MHc0RHaiojIsJye.png)
- public class FloatEvaluator implements TypeEvaluator {
- public Object evaluate(float fraction, Object startValue, Object endValue) {
- float startFloat = ((Number) startValue).floatValue();
- return startFloat + fraction * (((Number) endValue).floatValue() - startFloat);
- }
- }
可以看到,FloatEvaluator實作了TypeEvaluator接口,然後重寫evaluate()方法。evaluate()方法當中傳入了三個參數,第一個參數fraction非常重要,這個參數用于表示動畫的完成度的,我們應該根據它來計算目前動畫的值應該是多少,第二第三個參數分别表示動畫的初始值和結束值。那麼上述代碼的邏輯就比較清晰了,用結束值減去初始值,算出它們之間的內插補點,然後乘以fraction這個系數,再加上初始值,那麼就得到目前動畫的值了。好的,那FloatEvaluator是系統内置好的功能,并不需要我們自己去編寫,但介紹它的實作方法是要為我們後面的功能鋪路的。
上面的方法是對浮點值進行操作的,如果我們想要實作複雜效果,肯定不僅僅局限于這幾種常用類型,對象是肯定需要用到的,那麼到底該如何使用呢?我們通過一個小例子來看一下實作思路:
- 如何實作自定義view小球的運動?
在自定義view中,我們經常用到小球這個簡單的view實作一些效果,大家都知道,我們一般使用坐标來實作它位置的不斷重繪,進而實作動畫效果,是以,讓它運動的方法就是隻要不斷改變它的中心點坐标就可以了。那麼,我們要通過屬性動畫實作小球運動,改變的不是所謂int或者float型的初始值和結束值,而是點坐标即Point對象。既然抓住了突破點,我們就可以下手去做了。
1.首先,我們定義一個Point類,表示小球中心點的坐标:
/**
* Created by MuFeng on 2017/4/17.
* fc:坐标點
*/
public class Point {
private float x;
private float y;
public Point(float x, float y) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
}
代碼很簡單,讓我們直接思考下一步該如何入手呢?剛剛上面說過了,TypeEvaluator這個接口用來處理起始值與結束值之間過渡過程的處理,因為我們這裡重新定義一個自己的類型作為初始值和結束值,是以,理所當然地要實作它的過渡過程的邏輯,那麼下一步當然是寫一個實作類了。
2.PointEvaluator類實作TypeEvaluator接口:
/**
* Created by MuFeng on 2017/4/17.
* fc:制定自己的動畫過渡過程,這裡實作的是初始坐标到終點坐标的過渡
*/
public class PointEvaluator implements TypeEvaluator {
@Override
public Object evaluate(float fraction, Object startValue, Object endValue) {
Point startPoint= (Point) startValue;
Point endPoint = (Point) endValue;
float x=startPoint.getX()+fraction*(endPoint.getX()-startPoint.getX());
float y=startPoint.getY()+fraction*(endPoint.getY()-startPoint.getY());
Point point=new Point(x,y);
return point;
}
}
實作TypeEvaluator首先要重寫它的方法:evaluate()。點的變化取決于橫坐标和縱坐标,是以我們隻需要将x與y過渡的過程實作一下就好了,這裡我們先要定義兩個點用于方法傳入的起始值與結束值,然後對他們的橫縱坐标進行處理,依舊采用的是起始值+fraction*(終點x/y-起點x/y)來得到的,最後建立一個Point傳入計算得到的x與y即可。這樣,我們的起點到終點的過渡實作就完成了,那下面我們該畫出小球并實作其動畫效果了,然而并不是!在此之前,我們需要先看一下這個Point以及過渡邏輯能否有效,我們先對一個點進行動畫操作看看;
3.對Point進行動畫操作,檢視過渡過程的資料:
根據前面的學習,我們知道ValueAnimator是對值的操作,是以,這裡我們就用它看一下點運動的軌迹:
/**
* point 動畫
*/
private void pointAnimation() {
Point start=new Point(0,0);
Point end=new Point(200,200);
ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointEvaluator(),start,end);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//擷取目前的point
Point currentPoint= (Point) animation.getAnimatedValue();
float x=currentPoint.getX();//擷取目前點的x
float y=currentPoint.getY();//擷取目前點的y
Log.d("目前點坐标為:","("+x+","+y+")");
}
});
valueAnimator.start();
}
這裡我們調用的是ofObject()方法來對對象進行動畫操作,第一參數是我們自定義的過度規則,第二第三個則是起點與終點了,我們通過監聽器實時監聽過渡過程point的變化,讓我們檢視一下LOG資訊:
發現point的确是在不斷的變化,最終到達終點的,說明我們前面的工作有效的,下面讓我開始實作小球的動畫吧。
3.小球運動動畫實作:
由上面的分析可知,想要實作小球的運動,隻要實作它中心點的運動即可,上面我們已經完成了該項工作,說明我們已經離成功還差一小步了,我們先來自定義一個BallView繼承自view類,實作裡面的構造方法、ondraw()方法以及小球運動的方法即可:
package test.myanimatortest;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.View;
/**
* Created by MuFeng on 2017/4/17.
* fc:自定義view,實作小球的運動
*/
public class BallView extends View {
Point currentPoint;//目前point
Paint paint;//畫筆
public static final float R=50f;
public BallView(Context context, AttributeSet attrs) {
super(context, attrs);
paint=new Paint(Paint.ANTI_ALIAS_FLAG);//抗鋸齒
paint.setColor(Color.RED);
}
/**
* 在畫布上畫一個球
*/
private void drawBall(Canvas canvas){
float x=currentPoint.getX();
float y=currentPoint.getY();
canvas.drawCircle(x,y,R,paint);
}
/**
* ondraw()重寫
*/
@Override
protected void onDraw(Canvas canvas) {
if (currentPoint==null){
currentPoint=new Point(100,100);
drawBall(canvas);//畫球
//startSport();//開始運動
}else {
drawBall(canvas);
}
}
/**
* 小球的運動動畫
*/
public void startSport() {
//設定起點終點
Point startPoint=new Point(100,100);
Point endPoint=new Point(getWidth()- R,getHeight()-R);
ValueAnimator ballAnim=ValueAnimator.ofObject(new PointEvaluator(),startPoint,endPoint);
ballAnim.setDuration(3000);
//監聽動畫過程
ballAnim.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
currentPoint= (Point) animation.getAnimatedValue();//每次變化,都會重新設定currentPoint的位置
invalidate();//每次位置改變都重繪一下小球位置
}
});
ballAnim.start();
}
}
我們設定好畫筆以及目前點位置資訊後,在ondraw()方法中畫出一個小球,判斷一下,如果目前的point是空的,則重新建立一個,再調用drawBall()方法畫出半徑為50.0的小球,小球動畫是通過startSport()這個方法實作的,我們指定好起點終點後,通過ValueAnimator調用ofObject()方法傳入PointEvaluator對象和起始點、結束點,接着通過監聽器監聽它的動畫過程,方法中,每當point位置發生改變,都會重新給小球位置指派,再調用invalidate()方法不斷重繪,進而實作小球的運動效果。
4.最後,在布局檔案中加入自定義view,activity中按鈕點選實作小球運動:
布局檔案:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_sport_animation"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="test.myanimatortest.SportAnimationActivity">
<Button
android:id="@+id/startSport"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="開始"
android:layout_margin="16dp"/>
<test.myanimatortest.BallView
android:id="@+id/ball"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
然後在activity中擷取view并且實作點選按鈕,小球運動的功能:
public class SportAnimationActivity extends AppCompatActivity implements View.OnClickListener {
private Button start;
private BallView ballView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sport_animation);
start= (Button) findViewById(R.id.startSport);
ballView= (BallView) findViewById(R.id.ball);
start.setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.startSport:
//pointAnimation();
ballView.startSport();//通過點選“開始”按鈕,開始小球的運動
break;
}
}
/**
* point 動畫
*/
private void pointAnimation() {
Point start=new Point(0,0);
Point end=new Point(200,200);
ValueAnimator valueAnimator=ValueAnimator.ofObject(new PointEvaluator(),start,end);
valueAnimator.setDuration(3000);
valueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
//擷取目前的point
Point currentPoint= (Point) animation.getAnimatedValue();
float x=currentPoint.getX();//擷取目前點的x
float y=currentPoint.getY();//擷取目前點的y
Log.d("目前點坐标為:","("+x+","+y+")");
}
});
valueAnimator.start();
}
}
代碼有注釋,了解很簡單,這裡就不多說了,看下運作後的效果吧:
有點卡,總之效果是實作了,今天内容就到這吧,後面會找個屬性動畫的實戰訓練一下自己,持續更新中。
補充:本文僅作學習記錄和供大家參考,如果有不足和改進之處歡迎大家指出,謝謝。
參考:
http://blog.csdn.net/guolin_blog/article/details/43816093,特别緻謝。
http://blog.csdn.net/chenzheng8975/article/details/53710492