屬性動畫之紛繁的星星
效果就是你手點哪,就會生成一個星星然後彈射出去
這個效果很酷炫吧,這個是我看了郭神的屬性動畫後寫的,裏面將大部分的屬性動畫都用上了包括Evaluator和Interpolator
不過Interpolator是用的系統的,希望各位大神能自己寫,然後還用到個數學公式貝塞爾曲線
整體就是一個RelativeLayout,我爲大家剖析下這個動畫的實現
如果大家不清楚屬性動畫的基礎請轉看郭神文章http://blog.csdn.net/guolin_blog/article/details/43536355
首先請大家看下佈局文件
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<cn.edu.nuc.animator.StarView
android:id="@+id/star"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="0dp"
android:layout_centerInParent="true">
</cn.edu.nuc.animator.StarView>
</RelativeLayout>
什麼都沒,只有一個StarView,這個就是今天咱們自定義的佈局嘍
public class StarView extends RelativeLayout {
private Drawable star1; //第一張圖片
private Drawable star2; //第二張圖片
private Drawable star3; //第三張圖片
private Drawable[] drawables;//一個圖片數組用了存儲圖片
private int dHeight,dWight; //記錄圖片的寬和高
private int mHeight,mWight; //記錄佈局的寬和高
private Random random=new Random();//先做個隨機數一會用到的地方很多
private PointF curPointF; //點擊屏幕的點
public StarView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
}
這就是咱們今天的主角,可以看到它繼承自一個RelativeLayout,成員變量已經加上註釋
然後就是構造方法,其中有個init()方法進行初始化,看看這個init()方法
private void init() {
drawables=new Drawable[3];
star1=getResources().getDrawable(R.drawable.star1);
star2=getResources().getDrawable(R.drawable.star2);
star3=getResources().getDrawable(R.drawable.star3);
drawables[0]=star1;
drawables[1]=star2;
drawables[2]=star3;
//得到圖片的實際寬高
dHeight=star1.getIntrinsicHeight();
dWight=star1.getIntrinsicWidth();
}
這個init()方法是中將各個圖片從資源文件獲得到,並加入到圖片數組中,然後獲得到了每張圖片的寬和高,因爲我這裏選的圖片寬高一樣,所以按第 一張圖片獲取即可
然後我要獲得佈局的寬高,因爲OnCreat()方法佈局還沒顯示所以無法測量,
所以調用onMeasure(int widthMeasureSpec, int heightMeasureSpec)方法
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//得到佈局的寬高
mHeight=getMeasuredHeight();
mWight=getMeasuredWidth();
}
初始化完成,然後我要進行點擊肯定要重寫onTouchEvent(MotionEvent event)方法
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getAction()==0){//判斷是否是按下屏幕
PointF pointF=new PointF();
pointF.x=event.getX();
pointF.y=event.getY();
if(pointF.y<dHeight){//做個判斷,如果屏幕上端邊緣就不出現動畫,因爲後面有一些計算,避免crash
return true;
}
addStar(pointF);
}
return true;
}
這裏獲得我點擊屏幕的位置然後作爲參數傳給addStar(pointF)這個方法,好的,這個動畫的核心代碼開始
public void addStar(PointF clickPointF){
curPointF=clickPointF;
final ImageView iv=new ImageView(getContext());//定義並初始化一個ImageView控件
iv.setX(clickPointF.x-dWight/2); //設置ImageView的初始x座標
iv.setY(clickPointF.y-dHeight/2); //設置ImageView的初始y座標
iv.setImageDrawable(drawables[random.nextInt(3)]); //隨機選取圖像數組中的三張圖片中的一種作爲這個ImageView的顯示圖片
addView(iv); //將這個ImageView動態添加到這個佈局中
Animator set=getAnimator(iv); //獲得這個動畫
set.setInterpolator(new OvershootInterpolator());//爲這個動畫設置Interpolator,這個方法主要是更改動畫對象的速度變動
//爲動畫設置一個監聽,當一個動畫執行完畢後,馬上將這個控件回收掉,不然你點多了,其實這個佈局就會有
//上千個ImageView
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
removeView(iv);
}
});
set.start();//開始執行動畫
}
這個方法首先是給這個佈局進行動態添加了ImageView,然後獲得一個動畫對象,設置一個Interpolator,設置了一個動畫的監聽
最後開始執行動畫,其關鍵還是如何獲得那個動畫對象,接着看下getAnimator(ImageView iv)方法
private Animator getAnimator(ImageView iv) {
//1.alpha動畫
ObjectAnimator alpha=ObjectAnimator.ofFloat(iv,"alpha",0.3f,1f);
//2.縮放動畫
ObjectAnimator scaleX=ObjectAnimator.ofFloat(iv,"scaleX",0.2f,1f);
ObjectAnimator scaleY=ObjectAnimator.ofFloat(iv,"scaleY",0.2f,1f);
AnimatorSet enter=new AnimatorSet();
enter.setDuration(500);//設置動畫時間爲0.5秒
//3個動畫同時執行
enter.playTogether(alpha, scaleX, scaleY);
enter.setTarget(iv);//設置動畫目標
//貝賽爾曲線動畫(核心,不斷修改ImageView的座標,PointF(X,Y))
ValueAnimator bezierAnimator=getbezierAnimator(iv);
AnimatorSet bezierSet = new AnimatorSet();
bezierSet.playSequentially(enter, bezierAnimator);
bezierSet.setTarget(iv);
return bezierSet;
}
這個方法用ObjectAnimator.ofFloat先對這個ImageView做了一些出現動畫的處理,並且將這些動畫添加到一個動畫集合AnimatorSet裏,
並且讓他們同時執行,出現動畫完成,然後開始一個運動的動畫,獲得一個運動的貝賽爾曲線動畫如何將這個和剛剛的那個動畫集合
再放到一個大的集合裏面,然後先讓出現動畫執行,再讓運動動畫執行,然後將這個動畫返回,接下來看下
getbezierAnimator(final ImageView iv)這個運動動畫是如何實現的getbezierAnimator(final ImageView iv)這個運動動畫是如何實現的
getbezierAnimator(final ImageView iv)這個運動動畫是如何實現的
private ValueAnimator getbezierAnimator(final ImageView iv) {
//貝賽爾曲線動畫(核心,不斷修改ImageView的座標,PointF(X,Y))
PointF pointF2=getPointF(2);
PointF pointF1=getPointF(1);
PointF pointF0=new PointF(curPointF.x-dWight/2,curPointF.y-dHeight/2);
PointF pointF3=new PointF(random.nextInt(mWight),0);
BezierEvaluator evaluator=new BezierEvaluator(pointF1,pointF2);
ValueAnimator animator=ValueAnimator.ofObject(evaluator,pointF0,pointF3);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
@Override
public void onAnimationUpdate(ValueAnimator animation) {
PointF pointF= (PointF) animation.getAnimatedValue();
iv.setX(pointF.x);
iv.setY(pointF.y);
iv.setAlpha(1-animation.getAnimatedFraction());
}
});
animator.setTarget(iv);
animator.setDuration(3000);
return animator;
}
先給大家發下貝賽爾曲線動畫的公式
再給大家一張原理圖
原理圖中可以看到貝賽爾曲線的p0是起點,p3是終點,p1和p2是中間2個點,t是一個[0,1]之間的參數
原理明白了開始寫嘍,p0當然就是我點擊點,不過得做下處理因爲圖片的點是按左上角算得,所以要讓我們點擊位置成爲中心那就
都減去圖片寬高的一半,P3的話飛到上邊就行,y設爲0,x隨機,只要屏幕內就行
p1和p2隨機獲得,但也不能太隨機,p1要在p2的下邊,2者y值要在P0和p3的y值之間,這樣纔好看一些,p1和p2的方法最後貼出
然後爲了讓動畫按這個貝賽爾曲線必須得構造一個BezierEvaluator,然後給這個動畫設置一個更新監聽,運動過程中透明度也要變化
public class BezierEvaluator implements TypeEvaluator<PointF> {
private PointF pointF1;
private PointF pointF2;
public BezierEvaluator(PointF pointF1, PointF pointF2) {
this.pointF1 = pointF1;
this.pointF2 = pointF2;
}
@Override
public PointF evaluate(float fraction, PointF startValue, PointF endValue) {
PointF pointF=new PointF();
pointF.x=startValue.x*(1-fraction)*(1-fraction)*(1-fraction)+
3*pointF1.x*fraction*(1-fraction)*(1-fraction)+
3*pointF2.x*fraction*fraction*(1-fraction)+
endValue.x*fraction*fraction*fraction;
pointF.y=startValue.y*(1-fraction)*(1-fraction)*(1-fraction)+
3*pointF1.y*fraction*(1-fraction)*(1-fraction)+
3*pointF2.y*fraction*fraction*(1-fraction)+
endValue.y*fraction*fraction*fraction;
return pointF;
}
}
這個是我寫的一個BezierEvaluator,其實就是抄寫下公式
最後貼下獲取P1和P2點的方法
private PointF getPointF(int i) {
PointF pointF=new PointF();
pointF.x=random.nextInt(mWight);
if(i==2){
pointF.y=random.nextInt((int) ((curPointF.y)/2));
}else {
pointF.y=random.nextInt((int) (curPointF.y/2))+curPointF.y/2;
}
return pointF;
}
搞定!