屬性動畫之紛繁的星星

屬性動畫之紛繁的星星


效果就是你手點哪,就會生成一個星星然後彈射出去
這個效果很酷炫吧,這個是我看了郭神的屬性動畫後寫的,裏面將大部分的屬性動畫都用上了包括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;
    }

搞定!
發佈了38 篇原創文章 · 獲贊 14 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章