天天看點

安卓底部導航欄菜單彈出屬性動畫

一向以不重複造輪子為宗旨的小程式員,在找遍全網沒找到稱心的動畫之後,隻好自己寫了個屬性動畫。也就産生了本人第一篇原創部落格。OK,廢話不多說,先上效果圖。有點類似鹹魚的釋出動畫。

安卓底部導航欄菜單彈出屬性動畫
//MainActivity的布局xml代碼:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:background="#fff988"
    >

    <ImageView
        android:id="@+id/boss1"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"
        android:background="@drawable/bg_oval_pink"
        />
</RelativeLayout>

//MainActivity的java代碼
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;

/**
 * Created by staticman on 2016/12/28.
 */

public class MainActivity extends AppCompatActivity {
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.findViewById(R.id.boss1).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent intent = new Intent(getApplicationContext(), ActivityOpenAnim.class);
                startActivity(intent);
            }
        });
    }
}
           
//展開菜單的布局xml代碼,每個ImageView的背景随便設定即可,這裡就不貼上來了
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
    >

    <LinearLayout
        android:id="@+id/clickOut"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        >
    </LinearLayout>

    <ImageView
        android:id="@+id/anim1"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"
        android:background="@drawable/bg_oval_blue"
        />

    <ImageView
        android:id="@+id/anim2"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"
        android:background="@drawable/bg_oval_red"
        />

    <ImageView
        android:id="@+id/anim3"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"
        android:background="@drawable/bg_oval_green"
        />

    <ImageView
        android:id="@+id/boss"
        android:layout_width="48dp"
        android:layout_height="48dp"
        android:layout_alignParentBottom="true"
        android:layout_centerHorizontal="true"
        android:layout_marginBottom="20dp"
        android:background="@drawable/bg_oval_pink"
        />
</RelativeLayout>

//ActivityOpenAnim的java代碼
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.DisplayMetrics;
import android.view.KeyEvent;
import android.view.View;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.OvershootInterpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;

import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import org.greenrobot.eventbus.ThreadMode;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;

public class ActivityOpenAnim extends AppCompatActivity {
    boolean isOpen = false;
    @BindView(R.id.anim1)
    ImageView anim1;
    @BindView(R.id.anim2)
    ImageView anim2;
    @BindView(R.id.anim3)
    ImageView anim3;
    @BindView(R.id.clickOut)
    LinearLayout clickOut;
    @BindView(R.id.boss)
    ImageView boss;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        getSupportActionBar().hide();
        setContentView(R.layout.activity_open_menu);
        EventBus.getDefault().register(this);
        ButterKnife.bind(this);
        //進入頁面等ms之後再進行動畫
        new Thread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep();//子線程中睡300ms是為了Acticity的布局初始化完畢才觸發動畫,否則動畫會出現卡頓
                    boss.setClickable(false);//禁用點選,防止快速點選的時候動畫未完成就再次開始
                    EventBus.getDefault().post(new OpenBean());//使用EventBus發送消息到主線程的訂閱事件開啟動畫,使用handler也可以
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

            }
        }).start();

    }

    //所有的點選事件
    @OnClick({R.id.clickOut, R.id.boss, R.id.anim1, R.id.anim2, R.id.anim3})
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.clickOut://點選任意空白處均可關閉此頁面
            case R.id.boss:
                if (isOpen) {
                    isOpen = !isOpen;
                    animRunBack(anim1, anim2, anim3);//關閉時開啟收回動畫
                }
                break;
            case R.id.anim1:
                finish();
                break;
            case R.id.anim2:
                finish();
                break;
            case R.id.anim3:
                finish();
                break;
        }
    }

    //eventbus的訂閱事件
    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEventMainThread(OpenBean openBean) {
        if (!isOpen) {
            isOpen = !isOpen;//禁止快速點選
            animRun(anim1, anim2, anim3);
        }
    }

    //菜單展開的動畫
    public void animRun(final View view1, final View view2, final View view3) {
        //擷取螢幕寬度
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        final int screenWidth = dm.widthPixels;

        //左邊控件的動畫
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(view1, "translationY",
                F, -screenWidth / f).setDuration();//Y方向移動距離
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(view1, "translationX",
                F, -screenWidth / f).setDuration();//X方向移動距離
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(view1, "scaleX", f, f).setDuration();//X方向放大
        ObjectAnimator animator4 = ObjectAnimator.ofFloat(view1, "scaleY", f, f).setDuration();//Y方向放大
        AnimatorSet animSet1 = new AnimatorSet();
        animSet1.setInterpolator(new OvershootInterpolator());//到達指定位置後繼續向前移動一定的距離然後彈回指定位置,達到顫動的特效
        animSet1.playTogether(animator1, animator2, animator3, animator4);//四個動畫同時執行

        //中間控件的動畫,因需要設定監聽是以與
        final ObjectAnimator animator5 = ObjectAnimator.ofFloat(view2, "translationY",
                F, -screenWidth / f).setDuration();
        ObjectAnimator animator6 = ObjectAnimator.ofFloat(view2, "scaleX", f, f).setDuration();
        ObjectAnimator animator7 = ObjectAnimator.ofFloat(view2, "scaleY", f, f).setDuration();
        final AnimatorSet animatorSet2 = new AnimatorSet();
        animatorSet2.setInterpolator(new OvershootInterpolator());
        animatorSet2.playTogether(animator5, animator6, animator7);
        animatorSet2.setStartDelay();//監聽第一個動畫開始之後50ms開啟第二個動畫,達到相繼彈出的效果

        //右側控件的動畫
        ObjectAnimator animator8 = ObjectAnimator.ofFloat(view3, "translationY",
                F, -screenWidth / ).setDuration();
        ObjectAnimator animator9 = ObjectAnimator.ofFloat(view3, "translationX",
                F, screenWidth / f).setDuration();
        ObjectAnimator animator10 = ObjectAnimator.ofFloat(view3, "scaleX", f, f).setDuration();
        ObjectAnimator animator11 = ObjectAnimator.ofFloat(view3, "scaleY", f, f).setDuration();
        final AnimatorSet animatorSet3 = new AnimatorSet();
        animatorSet3.setInterpolator(new OvershootInterpolator());
        animatorSet3.playTogether(animator8, animator9, animator10, animator11);
        animatorSet3.setStartDelay();


        //三個動畫結束之後設定boss按鍵可點選,點選即收回動畫
        animatorSet3.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                boss.setClickable(true);
            }
        });

        //第二個開始之後再開啟第三個
        animator3.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                animatorSet3.start();
            }

        });

        //第一個動畫開始之後再開啟第二個
        animSet1.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                animatorSet2.start();
            }
        });

        animSet1.start();//放在最後是為了初始化完畢所有的動畫之後才觸發第一個控件的動畫
    }


    //收回動畫,相當于反向執行展開動畫,此處不做更詳細的注釋
    public void animRunBack(final View view1, final View view2, final View view3) {
        //擷取螢幕寬度
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        final int screenWidth = dm.widthPixels;

        //第一個收回動畫
        ObjectAnimator animator1 = ObjectAnimator.ofFloat(view1, "translationY",
                -screenWidth / f, F).setDuration();
        ObjectAnimator animator2 = ObjectAnimator.ofFloat(view1, "translationX",
                -screenWidth / f, F).setDuration();
        ObjectAnimator animator3 = ObjectAnimator.ofFloat(view1, "scaleX", f, f).setDuration();
        ObjectAnimator animator4 = ObjectAnimator.ofFloat(view1, "scaleY", f, f).setDuration();
        AnimatorSet animSet = new AnimatorSet();
        animSet.setInterpolator(new DecelerateInterpolator());
        animSet.playTogether(animator1, animator2, animator3, animator4);

        //第二個收回動畫
        final ObjectAnimator animator5 = ObjectAnimator.ofFloat(view2, "translationY",
                -screenWidth / f, F).setDuration();
        ObjectAnimator animator6 = ObjectAnimator.ofFloat(view2, "scaleX", f, f).setDuration();
        ObjectAnimator animator7 = ObjectAnimator.ofFloat(view2, "scaleY", f, f).setDuration();
        //                animator3.setInterpolator(new OvershootInterpolator());
        final AnimatorSet animatorSet2 = new AnimatorSet();
        animatorSet2.setInterpolator(new DecelerateInterpolator());
        animatorSet2.playTogether(animator5, animator6, animator7);
        animatorSet2.setStartDelay();

        //第三個收回動畫
        ObjectAnimator animator8 = ObjectAnimator.ofFloat(view3, "translationY",
                -screenWidth / , F).setDuration();
        ObjectAnimator animator9 = ObjectAnimator.ofFloat(view3, "translationX",
                screenWidth / f, F).setDuration();
        ObjectAnimator animator10 = ObjectAnimator.ofFloat(view3, "scaleX", f, f).setDuration();
        ObjectAnimator animator11 = ObjectAnimator.ofFloat(view3, "scaleY", f, f).setDuration();
        final AnimatorSet animatorSet3 = new AnimatorSet();
        animatorSet3.setInterpolator(new DecelerateInterpolator());
        //四個動畫同時執行
        animatorSet3.playTogether(animator8, animator9, animator10, animator11);
        animatorSet3.setStartDelay();

        animator3.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {

                animatorSet3.start();
                animatorSet3.addListener(new AnimatorListenerAdapter() {
                    @Override
                    public void onAnimationEnd(Animator animation) {
                        finish();//收回動畫結束後finish此頁面
                    }
                });
            }

        });

        animSet.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                animatorSet2.start();
            }
        });

        animSet.start();
    }

    //優化傳回鍵的點選事件
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK && isOpen) {
            isOpen = !isOpen;
            boss.setClickable(false);
            animRunBack(anim1, anim2, anim3);
        }
        return false;
    }

    //必須在ondestroy中登出eventbus,否則會出事件重複執行的bug.
    @Override
    protected void onDestroy() {
        super.onDestroy();
        EventBus.getDefault().unregister(this);
    }

}

//其中空類OpenBean
package com.it.animdemo;

/**
 * Created by staticman on 2016/12/28.
 */

public class OpenBean {
    public OpenBean() {
    }
}
           
//菜單頁的主題
<resources>

    <!-- Base application theme. -->

    <style name="HalfTrans" parent="AppTheme">
        <item name="android:windowIsTranslucent">true</item>
        <item name="android:windowBackground">@color/halfTrans</item>
    </style>

</resources>

//其中半透明的背景色是
<color name="halfTrans">#80000000</color>

//在清單檔案中配置如下配置
<activity android:name=".ActivityOpenAnim"
            android:theme="@style/HalfTrans"
            />
           

OK,睡覺.