Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Activity切換動畫多種實現方式與封裝

Android Activity切換動畫多種實現方式與封裝

編輯:關於Android編程

關於activity的動畫,相信大家再熟悉不過了,我們開發中經常用到,網上資料也很多,但是也有一些小細節需要我們注意,今天這篇文章我總結了幾種常用的動畫實現方式,通過這篇文章,你可以了解到:

幾種常見的activity動畫實現方式 activity動畫中需要注意的細節 這幾種方式的優缺點比較,我們如何取捨 對這幾種方式進行簡易封裝,提高我們的開發效率

幾種常見的實現方式:

1. activity.overridePendingTransition()

這種方式相信大家一定非常熟悉了,簡單回憶一下,我們只需要在start另一個activity啟動的時候調用就行,這個方法接收兩個參數,一個是我們新啟動的activity進入時的動畫,另一個是當前activity退出時的動畫(很多時候我們是不需要這個動畫的,只需要另一個activity進入的效果),首先我們在我們res文件目錄下新建一個anim文件夾,隨便建一個動畫效果,我這裡以從右邊劃入為例,首先我建一個slide_in_right.xml的文件:


    

我們最熟悉的平移動畫,用法:

startActivity(new Intent(MainActivity.this, TestActivity.class));               overridePendingTransition(R.anim.slide_in_right, 0);

效果如圖:
這裡寫圖片描述
這樣寫似乎是我們要的效果,然而,在有些手機上卻出現了很奇怪的黑色平移塊,此時我們需要保持前一個頁面不同,所以我們假寫一個動畫 animo_no:<喎?/kf/ware/vc/" target="_blank" class="keylink">vcD4NCjxwcmUgY2xhc3M9"brush:java;">

這個動畫是沒有任何效果的,我們拿來抵消動畫效果,現在跑起來就像是另一個頁面執行動畫,前面一個頁面不動的效果了:

startActivity(new Intent(MainActivity.this, TestActivityWithTheme.class));            overridePendingTransition(R.anim.slide_in_right, R.anim.animo_no);

這裡寫圖片描述

結束動畫:
進入之後,為了保證動畫的一致連貫性,所以我們需要在已經啟動的activity中也覆蓋同樣的方法來執行結束動畫,我們再建一個slide_out_right.xml:


    

使用:
我們重寫finish方法,在finish的時候加上我們的動畫效果:

 @Override
    public void finish() {
        super.finish();
        overridePendingTransition(0, R.anim.slide_out_right);
    }

這裡有個細節需要注意一下,這個方法只不能在onBackPressed裡面重寫,重寫的話只有在點回退鍵的時候才有動畫效果,而在代碼中調用finish的時候,是沒有效果的,所以一定要重寫在finish方法裡面,如圖,我們看看完整的效果,包括回退鍵和代碼內部調用finish方法。
這裡寫圖片描述


2. ActivityOptionsCompat

這個類是supportv4中新加的一個類,可以為activity添加各種動畫效果,這裡面的api至少要求4.0以上的系統,部分要求5.0以上,不過目前4.0一下的手機幾乎沒有了,這裡權當對新方法的介紹了,為了實現我們剛剛才的效果,現在我們可以這麼寫:

ActivityOptionsCompat compat = ActivityOptionsCompat.makeCustomAnimation(MainActivity.this, R.anim.slide_in_right, R.anim.animo_no);           ActivityCompat.startActivity(MainActivity.this,Intent, compat.toBundle());

這樣就可以達到跟我們之前一樣的效果了,當然,結束的時候還是要調用overridePendingTransition,不過這樣寫起來好像還是之前那個overridePendingTransition更好用,當然他的功能遠不止這些,包括谷歌我們封裝好了的一些拉伸擴散動畫,共享元素動畫等等,其實這個方法也就是新的api對老的方法的兼容而已,今天這裡只討論同一種動畫效果的多種方式,算總結了。


3.AppTheme: 直接在主題中修改activity動畫樣式:

首先我們在res的style.xml文件中寫一個ActivityAnimation,繼承自@android:style/Animation.Activity:分別把我們之前用的三個動畫樣式指定進去,代碼如下

在我們activity 用到的theme中加入android:windowAnimationStyle 屬性:

然後我們在我們需要用到的activity中指定我們剛剛定義的theme——
AppThemeWithWindowAnimation

如果我們需要所有頁面都使用我們定制的樣式,只需要在在整個application 的theme標簽下指定即可:


so,大功告成,這樣一來,我們不需要額外代碼,自動替換了原有的app默認跳轉樣式,很方便有木有~!

比較與總結:

首先,這三種方式都能實現同一種效果,但是相比前兩種方式,第三種更為方便的為所有頁面指定動畫樣式,不需要額外的代碼,但是不夠靈活,不能代碼控制,如果我們需要第一個頁面左右劃入,第二個頁面上下劃入,第三種方式就做不到了,所以往往實際開發者,往往結合起來使用會比較方便,針對於前兩種方式,我們就可以靈活的在代碼中更換動畫樣式,但是這兩個方法的實現都是基於activity的,只有在activity中調用才能有效果,但有些時候,我們啟動另一個activity的實例本身可能不是activity,也許是Service 也許是applicationContext(比如有時候我們需要從推送服務來啟動某一個activity,但是推送回調提供的context就不是activity實例),所以在這種情況下,我們的第一二中方法是沒有效果的,此時只能依賴第三種方法,開發中,我們需要根據自己需要的情況而具體變動。

簡單封裝:

當然,為了提高我們的開發效率,我們可以對前兩種方式進行簡單的封裝,讓我們在使用的時候,不需要寫額外的代碼,所以思路就是我為activity設計一個啟動方法,將動畫代碼邏輯寫在其中即可,我們自己新建activity的時候繼承自這個activity即可:

public abstract class AnimationBaseActivity extends Activity {
    /**
     * 此變量用於啟動activity時的標識,用於判斷activity結束時是否覆蓋原有動畫,以保持啟動退出動畫一致性
     */
    private static boolean isFromActivityStart = false;
    /**
     * 結束動畫
     */
    private static int exitAnimation = 0;

    /**
     * 獲取啟動intent
     *
     * @param context
     * @param tClass
     * @return
     */
    public static Intent getLaunchIntent(Context context, Class tClass) {
        return new Intent(context, tClass);
    }

    /**
     * 帶動畫啟動頁面,覆蓋系統原有動畫樣式原理是startactivity 前overridePendingTransition()
     *
     * @param context 這裡需要context是activity實例才能覆蓋動畫效果,否則仍舊是系統原有動畫樣式
     * @param intent  調用getLaunchIntent方法獲取
     */
    public static void startWithOldAnimation(Context context, Intent intent) {
        if (context instanceof Activity) {
            isFromActivityStart = true;
            exitAnimation = 0;
            context.startActivity(intent);//須在activity啟動前調用
//            ((Activity) context).overridePendingTransition(R.anim.slide_in_right, 0);本應該是這個參數,但是有的機型會有前一個activity結束動畫,所以需要虛擬一個動畫代替,做到保持原有頁面不動的效果
            ((Activity) context).overridePendingTransition(R.anim.slide_in_right, R.anim.animo_no);
        } else {//context 非activity的情形 ,無法調用覆蓋方法,因此還是系統默認的跳轉動畫
            isFromActivityStart = false;
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }
    }

    /**
     * @param context        這裡需要context是activity實例才能覆蓋動畫效果,否則仍舊是系統原有動畫樣式
     * @param intent         調用getLaunchIntent方法獲取
     * @param enterAnimation 進入動畫
     * @param exiteAnimation 結束動畫
     */
    public static void startWithOldAnimation(Context context, Intent intent, int enterAnimation, int exiteAnimation) {
        if (context instanceof Activity) {
            isFromActivityStart = true;
            exitAnimation = exiteAnimation;
            context.startActivity(intent);
            ((Activity) context).overridePendingTransition(enterAnimation, R.anim.animo_no);
        } else {
            isFromActivityStart = false;
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }
    }

    /**
     * 帶動畫啟動頁面,使用ActivityOptionsCompat實現 4.1以上有效
     *
     * @param context 這裡需要context是activity實例才能覆蓋動畫效果,否則仍舊是系統原有動畫樣式
     * @param intent  調用getLaunchIntent方法獲取
     */
    public static void startWithNewAnimation(Context context, Intent intent) {
        if (context instanceof Activity) {
            isFromActivityStart = true;
            exitAnimation = 0;
            ActivityOptionsCompat compat = ActivityOptionsCompat.makeCustomAnimation(context, R.anim.slide_in_right, R.anim.animo_no);
            ActivityCompat.startActivity((Activity) context, intent, compat.toBundle());
        } else {//context 非activity的情形 ,無法調用覆蓋方法,因此還是系統默認的跳轉動畫
            isFromActivityStart = false;
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }
    }

    /**
     * @param context        這裡需要context是activity實例才能覆蓋動畫效果,否則仍舊是系統原有動畫樣式
     * @param intent         調用getLaunchIntent方法獲取
     * @param enterAnimation 進入動畫
     * @param exiteAnimation 結束動畫
     */
    public static void startWithNewAnimation(Context context, Intent intent, int enterAnimation, int exiteAnimation) {
        if (context instanceof Activity) {
            isFromActivityStart = true;
            exitAnimation = exiteAnimation;
            ActivityOptionsCompat compat = ActivityOptionsCompat.makeCustomAnimation(context, enterAnimation, R.anim.animo_no);
            ActivityCompat.startActivity((Activity) context, intent, compat.toBundle());
        } else {
            isFromActivityStart = false;
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent);
        }
    }

    /**
     * 在finish方法中覆蓋原有動畫樣式,若之前不是通過activity啟動的 則不覆蓋,使動畫樣式一致
     */
    @Override
    public void finish() {
        super.finish();
        if (isFromActivityStart)
            overridePendingTransition(0, exitAnimation == 0 ? R.anim.slide_out_right : exitAnimation);
    }
}

然後我們在我們真正開發的頁面裡面這麼寫:

public class TestActivity extends AnimationBaseActivity {
    private static String TEST_KEY = "test_key";

    /**
     * 隨便寫的啟動方法用於測試參數傳遞這裡以String類型為例,如果有別的需求直接自己隨意加即可
     *
     * @param context
     * @param testValue
     */
    public static void startWithNew(Context context, String testValue) {
        Intent intent = getLaunchIntent(context, TestActivity.class).putExtra(TEST_KEY, testValue);
        startWithNewAnimation(context, intent);//帶動畫效果啟動
    }

    public static void startWithOld(Context context, String testValue) {
        Intent intent = getLaunchIntent(context, TestActivity.class).putExtra(TEST_KEY, testValue);
        startWithOldAnimation(context, intent);
    }

    public static void startWithOld(Context context, int enterAnimation, int exiteAnimation, String testValue) {
        Intent intent = getLaunchIntent(context, TestActivity.class).putExtra(TEST_KEY, testValue);
        startWithOldAnimation(context, intent, enterAnimation, exiteAnimation);
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.test_activity);
        findViewById(R.id.finish).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                finish();
            }
        });
        initData();
    }

    /**
     * 處理數據傳遞
     */
    private void initData() {
        Toast.makeText(this, getIntent().getExtras().getString(TEST_KEY, ""), Toast.LENGTH_SHORT).show();
    }
}

分析:
首先我們看到第10行,我們自己寫來用於真正調用的啟動方法,11行我們調用了基類的獲取啟動Intent的方法,拿到這個對象,我們可以處理我們自己所需要傳遞的參數什麼的,我這裡只是隨便寫的一個String類型作為測試參數傳遞,然後我們才調用基類中我們封裝的方法執行動畫。然後我們再來看看基類中的封裝方法:首先看第29行,這裡首先會判斷當前context是否為activity的實例,是則isFromActivityStart 會被賦值為ture ,在啟動的時候我們就可以調用overridePendingTransition()方法覆蓋動畫樣式,這裡我們分別指定了默認進入樣式,然後啟動activity 執行動畫,如果不是activity的實例對象,這裡需要對intent加入setFlags(Intent.FLAG_ACTIVITY_NEW_TASK),這樣才能保證由applicationcontext 啟動activity不會報錯,此時我們無法調用overridePendingTransition方法,isFromActivityStart=false,此時activity啟動即為系統默認動畫。
再來看看第105行,根據isFromActivityStart的值來確定是否重寫我們的系統動畫方法,來保證我們自己定義的動畫啟動和系統默認啟動的動畫開始和結束都保持一致。
同樣的,在48行,我們重載了startWithOldAnimation方法,此時多了進入動畫和退出動畫,我們可以通過此方法靈活改變樣式,exitAnimation 即我們指定的自定義退出動畫樣式,在退出時使用。

使用測試:

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        findViewById(R.id.btn_old).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
//                TestActivity.startWithOld(getApplicationContext(), "這是老的方法動畫實現"); //applicationcontext 或者service 等其他context實例 啟動activity 調用系統默認動畫
//                TestActivity.startWithOld(MainActivity.this, "這是老的方法動畫實現");//用基類中默認的動畫替換樣式
                TestActivity.startWithOld(MainActivity.this, R.anim.slide_in_bottom, R.anim.slide_out_bottom, "這是老的方法動畫實現");//用自己指定的動畫樣式
            }
        });
        findViewById(R.id.btn_new).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                TestActivity.startWithNew(MainActivity.this, "這是新的方法動畫實現");

            }
        });
        findViewById(R.id.btn_theme).setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                startActivity(new Intent(MainActivity.this, TestActivityWithTheme.class));
            }
        });
    }
}

下面來看看效果

這裡寫圖片描述

大功告成,這樣一來將啟動方法封裝之後,我們就不用額外寫動畫代碼了,偷懶什麼的最開心了~

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved