高級UI特效之酷炫搶紅包金幣下落動畫

最近項目需求要求作一個搶紅包UI特效。效果以下:

項目效果
項目效果

從這張效果圖中咱們這可看出要包括功能:

  • 實現是個彈框:
  • 金幣下落功能android

  • 打開金幣按鈕的翻轉效果git

分析

  1. 實現是個彈框:
    能夠用thime爲Dialog的Activity
    或者 之談彈出一個Dialog,或者彈出一個PopupWindowgithub

  2. 金幣下落功能: 可用自定義View+自定義屬性動畫canvas

  3. 金幣的翻轉效果:可硬用幀動畫或者自定義View+ScheduledExecutorService發送runnablebash

Markdown
Markdown

利用PopupWindow彈出一些紅包界面:微信

private PopupWindow showPopWindows(View v, String moneyStr, boolean show) {
            View view = this.getLayoutInflater().inflate(R.layout.view_login_reward, null);
            TextView tvTips = (TextView) view.findViewById(R.id.tv_tip);
            TextView money = (TextView) view.findViewById(R.id.tv_money);
            tvTips.setText("連續登錄3天,送您" + moneyStr + "個愛心幣");
            money.setText(moneyStr);
            final LinearLayout container = (LinearLayout) view.findViewById(R.id.container);
            container.removeAllViews();
            //將flakeView 添加到佈局中
            container.addView(flakeView);
            //設置背景
            this.getWindow().setBackgroundDrawable(new ColorDrawable(Color.BLACK));
            //設置同時出如今屏幕上的金幣數量  建議64之內 過多會引發卡頓
            flakeView.addFlakes(8);
            /**
             * 繪製的類型
             * @see View.LAYER_TYPE_HARDWARE
             * @see View.LAYER_TYPE_SOFTWARE
             * @see View.LAYER_TYPE_NONE
             */
            flakeView.setLayerType(View.LAYER_TYPE_NONE, null);
            iv_onclick = (BofangView) view.findViewById(R.id.iv_onclick);
           // iv_onclick.setBackgroundResource(R.drawable.open_red_animation_drawable);
            iv_onclick.startAnation();
            iv_onclick.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View v) {
                    if (container!=null){
                        container.removeAllViews();
                    }
                    pop.dismiss();
                    GetToast.useString(getBaseContext(),"恭喜您,搶到紅包");
                }
            });
            pop = new PopupWindow(view, FrameLayout.LayoutParams.MATCH_PARENT, FrameLayout.LayoutParams.MATCH_PARENT);
            ColorDrawable dw = new ColorDrawable(getResources().getColor(R.color.half_color));
            pop.setBackgroundDrawable(dw);
            pop.setOutsideTouchable(true);
            pop.setFocusable(true);
            pop.showAtLocation(v, Gravity.CENTER, 0, 0);

            /**
             * 移除動畫
             */
            final Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try {
                        //設置2秒後
                        Thread.sleep(2000);
                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                container.removeAllViews();
                            }
                        });
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                }
            });
            if (!show)
                thread.start();
            //ivOpen指的是須要播放動畫的ImageView控件
    //        AnimationDrawable animationDrawable = (AnimationDrawable)iv_onclick.getBackground();
    //        animationDrawable.start();//啓動動畫
            MediaPlayer player = MediaPlayer.create(this, R.raw.shake);
            player.start();
            return pop;
        }複製代碼

在Java中萬物皆對象,一個金幣就是一個對象,
擁有本身bitmap,寬高,大小,還有本身的座標 利用屬性動畫進行改變每個小金幣的屬性值
這裏將每個金幣看做爲一個類ide

/**
     * 類功能描述:</br>
     *紅包金幣仿雨滴下落效果
     * @author yuyahao
     * @version 1.0 </p> 修改時間:</br> 修改備註:</br>
     */
    public class FlakeView extends View {

        Bitmap droid;
        int numFlakes = 0;
        ArrayList<Flake> flakes = new ArrayList<Flake>(); // List of current flakes
        public ValueAnimator animator = ValueAnimator.ofFloat(0, 1);
        long startTime, prevTime; // Used to track elapsed time for animations and fps
        int frames = 0;     // Used to track frames per second
        Paint textPaint;    // Used for rendering fps text
        float fps = 0;      // frames per second
        Matrix m = new Matrix(); // Matrix used to translate/rotate each flake during rendering
        String fpsString = "";
        String numFlakesString = "";
        /**
         * 利用屬性動畫進行改變每個小金幣的屬性值
         * 這裏是將每個金幣看做爲一個類
         * 在Java中萬物皆對象,一個金幣就是一個對象,
         * 擁有本身bitmap,寬高,大小,還有本身的座標
         * the animator
         */
        public FlakeView(Context context) {
            super(context);
            droid = BitmapFactory.decodeResource(getResources(), R.drawable.b);
            textPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
            textPaint.setColor(Color.WHITE);
            textPaint.setTextSize(24);
            animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator arg0) {
                    long nowTime = System.currentTimeMillis();
                    float secs = (float) (nowTime - prevTime) / 100f;
                    prevTime = nowTime;
                    for (int i = 0; i < numFlakes; ++i) {
                        Flake flake = flakes.get(i);
                        flake.y += (flake.speed * secs);
                        if (flake.y > getHeight()) {
                            // If a flake falls off the bottom, send it back to the top
                            flake.y = 0 - flake.height;
                        }
                        flake.rotation = flake.rotation + (flake.rotationSpeed * secs);
                    }
                    // Force a redraw to see the flakes in their new positions and orientations
                    invalidate();
                }
            });
            animator.setRepeatCount(ValueAnimator.INFINITE);
            animator.setDuration(3000);
        }



        private void setNumFlakes(int quantity) {
            numFlakes = quantity;
            numFlakesString = "numFlakes: " + numFlakes;
        }

        /**
         *增長每個小金幣屬性
         */
        public void addFlakes(int quantity) {
            for (int i = 0; i < quantity; ++i) {
                flakes.add(Flake.createFlake(getWidth(), droid,getContext()));
            }
            setNumFlakes(numFlakes + quantity);
        }

        /**
         * 減去指定數量的金幣,其餘的金幣屬性保持不變
         */
        void subtractFlakes(int quantity) {
            for (int i = 0; i < quantity; ++i) {
                int index = numFlakes - i - 1;
                flakes.remove(index);
            }
            setNumFlakes(numFlakes - quantity);
        }

        /**
         * nSizeChanged()實在佈局發生變化時的回調函數,間接回去調用onMeasure, onLayout函數從新佈局
         * @param w
         * @param h
         * @param oldw
         * @param oldh
         */
        @Override
        protected void onSizeChanged(int w, int h, int oldw, int oldh) {
            super.onSizeChanged(w, h, oldw, oldh);
            // Reset list of droidflakes, then restart it with 8 flakes
            flakes.clear();
            numFlakes = 0;
            addFlakes(16);
            // Cancel animator in case it was already running
            animator.cancel();
            // Set up fps tracking and start the animation
            startTime = System.currentTimeMillis();
            prevTime = startTime;
            frames = 0;
            animator.start();
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            for (int i = 0; i < numFlakes; ++i) {
                Flake flake = flakes.get(i);
                m.setTranslate(-flake.width / 2, -flake.height / 2);
                m.postRotate(flake.rotation);
                m.postTranslate(flake.width / 2 + flake.x, flake.height / 2 + flake.y);
                canvas.drawBitmap(flake.bitmap, m, null);
            }
            ++frames;
            long nowTime = System.currentTimeMillis();
            long deltaTime = nowTime - startTime;
            if (deltaTime > 1000) {
                float secs = (float) deltaTime / 1000f;
                fps = (float) frames / secs;
                startTime = nowTime;
                frames = 0;
            }
        }
        /**
         * 生命週期 pause
         */
        public void pause() {
            animator.cancel();
        }
        /**
         * 生命週期 resume
         */
        public void resume() {
            animator.start();
        }
    }複製代碼

利用PropertyValuesHolder實現圖片的左右晃動效果函數

PropertyValuesHolder的做用:佈局

PropertyValuesHolder這個類能夠先將動畫屬性和值暫時的存儲起來,後一塊兒執行,在有些時候可使用替換掉AnimatorSet,減小代碼量post

public static void doWaggleAnimation(View view) {
        PropertyValuesHolder rotation = PropertyValuesHolder.ofFloat("Rotation", 0f, -20f, 20f, -20f, 20f, -20f, 20f, -20f, 0f);
        PropertyValuesHolder scaleX = PropertyValuesHolder.ofFloat("ScaleX", 0.8f, 0.85f, 0.9f, 1f);
        PropertyValuesHolder scaleY = PropertyValuesHolder.ofFloat("ScaleY", 0.8f, 0.85f, 0.9f, 1f);
        ObjectAnimator animator = ObjectAnimator.ofPropertyValuesHolder(view, rotation, scaleX, scaleY);
        animator.setDuration(1000);
        animator.setRepeatMode(ValueAnimator.REVERSE);
        animator.setRepeatCount(ValueAnimator.INFINITE);
        animator.start();
    }複製代碼

本文用於自定義動畫比較多一些。

自定義動畫總結

Android提供了幾種動畫類型:

  • View Animation
  • Drawable Animation
  • Property Animation

View Animation至關簡單,不過只能支持簡單的縮放、平移、旋轉、透明度基本的動畫,且有必定的侷限性。好比:你但願View有一個顏色的切換動畫;你但願可使用3D旋轉動畫;你但願當動畫中止時,View的位置就是當前的位置;這些View Animation都沒法作到

Property Animation故名思議就是經過動畫的方式改變對象的屬性了,咱們首先須要瞭解幾個屬性:

  • Duration動畫的持續時間,默認300ms。

  • TimeInterpolation:定義動畫變化速率的接口,全部插值器都必須實現此接口,如線性、非線性插值器;

  • TypeEvaluator:用於定義屬性值計算方式的接口,有int、float、color類型,根據屬性的起始、結束值和插值一塊兒計算出當前時間的屬性值;

  • Animation sets:動畫集合,便可以同時對一個對象應用多個動畫,這些動畫能夠同時播放也能夠對不一樣動畫設置不一樣的延遲;

  • Frame refreash delay:多少時間刷新一次,即每隔多少時間計算一次屬性值,默認爲10ms,最終刷新時間還受系統進程調度與硬件的影響;

  • Repeat Country and behavoir:重複次數與方式,如播放3次、5次、無限循環,可讓此動畫一直重複,或播放完時向反向播放;

  • Frame refresh delay:幀刷新延遲,對於你的動畫,多久刷新一次幀;默認爲10ms,但最終依賴系統的當前狀態;基本不用管。

    相關的類

  • ObjectAnimator 動畫的執行類

    ObjectAnimator是其中比較容易使用的一個動畫類,它繼承自ValueAnimator,

    說比較容易使用是由於它在動畫啓動後自動監視屬性值的變化並把值賦給對象屬性,

    而ValueAnimator則只監視屬性值的變化,但不會自動在屬性中應用該值,所以咱們須要手動應用這些值

  • ValueAnimator 動畫的執行類

    ValueAnimtor動畫的建立基本上和ObjectAnimator同樣,只是咱們須要手動應用屬性值

  • AnimatorSet 用於控制一組動畫的執行:線性,一塊兒,每一個動畫的前後執行等。

  • AnimatorInflater 用戶加載屬性動畫的xml文件

  • TypeEvaluator 類型估值,主要用於設置動畫操做屬性的值。

  • TimeInterpolator 時間插值

ValueAnimator和ObjectAnimator之間的關係:

自定義屬性詳解
自定義屬性詳解

掌握了這邊能夠輕鬆的寫一個自定義屬性動畫了

PropertyValuesHolder的做用:

PropertyValuesHolder這個類能夠先將動畫屬性和值暫時的存儲起來,後一塊兒執行,在有些時候可使用替換掉AnimatorSet,減小代碼量

項目中效果圖:

抖動特效
抖動特效

最後獻上整個項目的源碼:

項目GitHub連接地址:

github.com/androidstar…

項目csdn連接地址:

download.csdn.net/detail/andr…

博客地址:

blog.csdn.net/androidstar…

相信本身,沒有作不到的,只有想不到的

若是你以爲此文對您有所幫助,歡迎入羣 QQ交流羣 :232203809
微信公衆號:終端研發部

技術+職場
技術+職場

(歡迎關注學習和交流)複製代碼
相關文章
相關標籤/搜索