Android 圖片彈跳動畫

來源:互聯網
上載者:User

標籤:

這幾天看到一個小動畫,覺得有點意思,就自己實現來看看,先看


OK,這個效果準系統就是,一個圖片,從頂部掉下來,完後彈幾下,再停止,實現起來還是比較簡單的,不過也走了點小彎路,這裡記錄下。


有段時間做自訂控制項比較多,有點中毒了,看到任何效果第一個先想到自訂控制項,所以一開始我是用自訂控制項嵌套自己用動畫計算距離來實現,後來發現沒必要,但基本思路是一致的,這裡先看看自訂控制項嵌套動畫如何?。


首先自訂一個ViewGroup, 看一下裡面用到的幾個變數

private int mWidth; // 螢幕寬度    private int mHeight; // 螢幕高度    private boolean mFirst = true; // 是否第一次執行    private int mTotalRound; // 總次數    private int mCurRound; // 當前次數    private long mTime; // 動畫時間
都有注釋,主要注意下次數,其中圖片落下或彈起,都算一次動畫

完後實現一個加入圖片的方法

private void init() {        ImageView view = new ImageView(mContext);        view.setBackgroundResource(R.mipmap.jump);        view.setScaleType(ImageView.ScaleType.FIT_XY);        addView(view);    }

這沒什麼好說,就是定義一個ImageView,設定圖片,全屏,完後將ImageView加入ViewGroup。

接下來,實現onMeasure方法,擷取螢幕寬高,這個在後面動畫中有用

@Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mWidth = MeasureSpec.getSize(widthMeasureSpec);   //擷取ViewGroup寬度        mHeight = MeasureSpec.getSize(heightMeasureSpec);  //擷取ViewGroup高度        setMeasuredDimension(mWidth, mHeight);    //設定ViewGroup的寬高        if (mFirst) {            mFirst = false;            beginAnimation();        }    }

這裡mFirst是一個布爾變數,初始為true,這裡就是動畫只執行一次,不管進入onMeasure方法多少次,beginAnimation就是我們要進行的動畫。

實現動畫前,我們先來仔細觀察一下這個動畫,這個動畫看著挺簡單,但是有幾個細節還是要注意:

1. 動畫彈起的高度越來越小,我這裡是第一次彈起螢幕的高度的1/2,第二次彈起1/4,第三次彈起1/8,以此類推

2. 我們將圖片的一次落下或彈起看成一次動畫,動畫的時間越來越短,假設第一次落下動畫需要1秒,那第一次彈起就需要1/2秒,第二次落下也是1/2秒,第二次彈起則需要1/4秒,以此類推

3. 下落的時候,速度越來越快,彈起的時候,速度越來越慢

瞭解了這些細節後,我就可以用layout方法來動態改變ImageView的位置,從而實現動畫的效果,layout方法很簡單

 public void layout(int l, int t, int r, int b)
傳入left, top, right, bottom的值來絕對位置,left和right我們不用關注,因為圖片是上下移動,所以left恒為0,而right恒為螢幕的寬度,而top和bottom是即時變化的,按順序看下top和bottom的值變化,假設螢幕高度為mHeight

第一次動畫,圖片從螢幕的頂部下落到螢幕的底部,top的變化為從-mHeight到0,bottom的變化從0到mHeight
第二次動畫,圖片從螢幕的底部彈起到圖片的底部正好在螢幕高度的1/2處,top的變化從0到-mHeight/2,bottom的變化從mHeight到mHeight/2

第三次動畫,圖片底部從螢幕高度的1/2處下落到螢幕的底部,top的變化從-mHeight/2到0,bottom的變化從mHeight/2到mHeight

以此類推,後面的動畫都是類似的,這些都清楚後,直接來看動畫如何?

private void beginAnimation() {        final int height;        if (mCurRound % 2 == 0) { // 向下            height = (int) (-mHeight * Math.pow(0.5, mCurRound / 2));        } else { // 向上            height = (int) (-mHeight * Math.pow(0.5, (mCurRound + 1) / 2));        }        Animation translateAnimation = new Animation() {            @Override            protected void applyTransformation(float interpolatedTime, Transformation t) {                if (mCurRound % 2 == 0) {                    getChildAt(0).layout(0, (int) (height * (1 - interpolatedTime)), mWidth, mHeight + height - (int) (interpolatedTime * height));                } else {                    getChildAt(0).layout(0, (int) (height * interpolatedTime), mWidth, mHeight + (int) (height * interpolatedTime));                }            }        };        if (mCurRound % 2 == 0) {            translateAnimation.setInterpolator(new AccelerateInterpolator());            translateAnimation.setDuration((long)(mTime * Math.pow(0.5, mCurRound / 2)));        } else {            translateAnimation.setInterpolator(new DecelerateInterpolator());            translateAnimation.setDuration((long)(mTime * Math.pow(0.5, (mCurRound + 1) / 2)));        }        startAnimation(translateAnimation);        translateAnimation.setAnimationListener(new Animation.AnimationListener() {            @Override            public void onAnimationStart(Animation animation) {            }            @Override            public void onAnimationEnd(Animation animation) {                mCurRound++;                if (mCurRound < mTotalRound) {                    beginAnimation();                }            }            @Override            public void onAnimationRepeat(Animation animation) {            }        });    }
mCurRound就是當前動畫的次數,從0開始計數,模數2為0,也就是偶數,也就是下落,模數2為1,也就是奇數,也就是彈起,後面的applyTransformation方法,有一個參數interpolatedTime,在動畫執行的過程中,會不斷調用applyTransformation這個方法,而interpolatedTime的值從0變化1,我們可以根據這個值,來動態計算當前的高度,而計算方法參考上面的每次動畫的top和bottom的值變化範圍,自己在紙上畫畫就知道了。後面的
if (mCurRound % 2 == 0) {            translateAnimation.setInterpolator(new AccelerateInterpolator());            translateAnimation.setDuration((long)(mTime * Math.pow(0.5, mCurRound / 2)));        } else {            translateAnimation.setInterpolator(new DecelerateInterpolator());            translateAnimation.setDuration((long)(mTime * Math.pow(0.5, (mCurRound + 1) / 2)));        }
就是之前分析的,動畫下落和彈起的速率變化不一樣,動畫時間也越來越短。

最後就是在動畫執行結束的時候,判斷下當前動畫執行了多少個了,如果沒執行完就繼續執行下一個動畫,這裡是一個遞迴的調用。代碼實現起來還是蠻簡單的,關鍵是前面的分析過程,和細節的注意,下面貼出完整代碼

public class JumpActivity extends Activity {    private int totalRound = 10;    private int time = 2000;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(new BounceView(this, totalRound, time));    }}
public class BounceView extends ViewGroup {    private Context mContext;    private int mWidth; // 螢幕寬度    private int mHeight; // 螢幕高度    private boolean mFirst = true; // 是否第一次執行    private int mTotalRound; // 總次數    private int mCurRound; // 當前次數    private long mTime; // 動畫時間    public BounceView(Context context) {        super(context);        init();    }    public BounceView(Context context, AttributeSet attrs) {        super(context, attrs);        init();    }    public BounceView(Context context, int roundNum, long time) {        super(context);        mContext = context;        mTime = time;        mTotalRound = roundNum;        init();    }    private void init() {        ImageView view = new ImageView(mContext);        view.setBackgroundResource(R.mipmap.jump);        view.setScaleType(ImageView.ScaleType.FIT_XY);        addView(view);    }    @Override    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {        mWidth = MeasureSpec.getSize(widthMeasureSpec);   //擷取ViewGroup寬度        mHeight = MeasureSpec.getSize(heightMeasureSpec);  //擷取ViewGroup高度        setMeasuredDimension(mWidth, mHeight);    //設定ViewGroup的寬高        if (mFirst) {            mFirst = false;            beginAnimation();        }    }    @Override    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {    }    private void beginAnimation() {        final int height;        if (mCurRound % 2 == 0) { // 向下            height = (int) (-mHeight * Math.pow(0.5, mCurRound / 2));        } else { // 向上            height = (int) (-mHeight * Math.pow(0.5, (mCurRound + 1) / 2));        }        Animation translateAnimation = new Animation() {            @Override            protected void applyTransformation(float interpolatedTime, Transformation t) {                if (mCurRound % 2 == 0) {                    getChildAt(0).layout(0, (int) (height * (1 - interpolatedTime)), mWidth, mHeight + height - (int) (interpolatedTime * height));                } else {                    getChildAt(0).layout(0, (int) (height * interpolatedTime), mWidth, mHeight + (int) (height * interpolatedTime));                }            }        };        if (mCurRound % 2 == 0) {            translateAnimation.setInterpolator(new AccelerateInterpolator());            translateAnimation.setDuration((long)(mTime * Math.pow(0.5, mCurRound / 2)));        } else {            translateAnimation.setInterpolator(new DecelerateInterpolator());            translateAnimation.setDuration((long)(mTime * Math.pow(0.5, (mCurRound + 1) / 2)));        }        startAnimation(translateAnimation);        translateAnimation.setAnimationListener(new Animation.AnimationListener() {            @Override            public void onAnimationStart(Animation animation) {            }            @Override            public void onAnimationEnd(Animation animation) {                mCurRound++;                if (mCurRound < mTotalRound) {                    beginAnimation();                }            }            @Override            public void onAnimationRepeat(Animation animation) {            }        });    }}
這是第一種方法,我們自訂了控制項,而且還在applyTransformation中做了計算,後來想了下,其實沒必要,不使用自訂控制項,不自己去計算,也可以實現,思路其實差不多,唯一的區別就是我們可以使用屬性動畫,直接用ObjectAnimator的ofFloat方法來移動圖片就行了,這裡因為思路都是一致的,我就直接貼代碼了:

先聲明一個布局檔案jump_layout.xml:

<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"    android:orientation="vertical" android:layout_width="match_parent"    android:layout_height="match_parent">    <ImageView        android:id="@+id/move"        android:layout_width="match_parent"        android:layout_height="match_parent"        android:src="@mipmap/jump"        android:scaleType="fitXY"        /></LinearLayout>
完後聲明Java類JumpActivity.java:
public class JumpActivity extends Activity {    private ImageView view;    private int mHeight;    private int totalRound = 10;    private int curRound = 0;    private int time = 2000;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        //setContentView(new BounceView(this, totalRound, time));        setContentView(R.layout.jump_layout);        view = (ImageView) findViewById(R.id.move);    }    @Override    public void onWindowFocusChanged(boolean hasFocus) {        super.onWindowFocusChanged(hasFocus);        if (hasFocus) {            Rect outRect = new Rect();            getWindow().findViewById(Window.ID_ANDROID_CONTENT).getDrawingRect(outRect);            mHeight = outRect.height();            beginTransAnimation();        }    }    private void beginTransAnimation() {        final int height;        if (curRound % 2 == 0) { // 向下            height = (int) (-mHeight * Math.pow(0.5, curRound / 2));        } else { // 向上            height = (int) (-mHeight * Math.pow(0.5, (curRound + 1) / 2));        }        float from = 0;        float to = 0;        if (curRound % 2 == 0) {            from = height;            to = 0;        } else {            from = 0;            to = height;        }        ObjectAnimator animator = ObjectAnimator.ofFloat(view, "translationY", from, to);        animator.setInterpolator(new AccelerateInterpolator());        animator.addListener(new Animator.AnimatorListener() {            @Override            public void onAnimationStart(Animator animation) {            }            @Override            public void onAnimationEnd(Animator animation) {                curRound++;                if (curRound <= totalRound) {                    beginTransAnimation();                }            }            @Override            public void onAnimationCancel(Animator animation) {            }            @Override            public void onAnimationRepeat(Animator animation) {            }        });        if (curRound % 2 == 0) {            animator.setInterpolator(new AccelerateInterpolator());            animator.setDuration((long) (time * Math.pow(0.5, curRound / 2))).start();        } else {            animator.setInterpolator(new DecelerateInterpolator());            animator.setDuration((long) (time * Math.pow(0.5, (curRound + 1) / 2))).start();        }    }}


源碼下載





Android 圖片彈跳動畫

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.