1. 程式人生 > >用activity-alias實現節日時替換icon

用activity-alias實現節日時替換icon

activity-alias標籤元素
AndroidManifest是一個xml檔案,它包含很多標籤元素,如application、activity、receiver等,其中有一個叫做activity-alias,該標籤平時很少用到,可是這個功能卻很常見。

activity-alias,顧名思義,即activity的別名。看到這裡,大家會想那它究竟是哪個Activity的別名呢?我們在建立一個Activity時,必須在AndroidManifest中靜態宣告該Activity,同時配置android:name、android:label、android:icon等屬性,還可配置intent-filter。對於activity-alias標籤,它有一個屬性叫android:targetActivity,這個屬性就是用來為該標籤設定目標Activity的,或者說它就是這個目標Activity的別名。至此我們已經明白activity-alias並非代表一個獨立的Activity,而是為一個已經存在的Activity建立的別名。

activity-alias功能
知道了activity-alias的概念,那麼它的功能是什麼呢?activity-alias作為一個已存在Activity的別名,則應該可以通過該別名標籤宣告快速開啟目標Activity。因此activity-alias可用來設定某個Activity的快捷入口,可以放在桌面上或者通過該別名被其他元件快速調起。該標籤元素支援一些屬性及intent-filter、meta-data等配置,因此可以觸發一些跟目標Activity不同的功能邏輯,雖然開啟的是同一個Activity。舉個簡單的例子,如之前需要先開啟主介面,然後才能點選進入某個Activity,如果使用activity-alias為該Activity配置一個快捷入口,甚至可以為其在桌面生成一個圖示,然後點選桌面圖示可直接進入該Activity,該功能可滿足某些需要快速到達功能介面的需求。

activity-alias語法及宣告
其基本語法如下

<activity-alias android:enabled=["true" | "false"]
                android:exported=["true" | "false"]
                android:icon="drawable resource"
                android:label="string resource"
                android:name="string"
                android:permission="string"
                android:targetActivity="string" >
    . . .
</activity-alias>
部分屬性說明如下

android:enable 該屬性用來決定目標Activity可否通過別名被系統例項化,預設為true。需要注意的是application也有enable屬性,只用當它們同時為true時,activity-alias的enable才生效。
android:exported 該屬性為true的話,則目標Activity可被其他應用調起,如為false則只能被應用自身調起。其預設值根據activity-alias是否包含intent-filter元素決定,如果有的話,則預設為true;沒有的話則為false。其實也很好理解,如果有intent-filter,則目標Activity可以匹配隱式Intent,因此可被外部應用喚起;如果沒有intent-filter,則目標Activity要被調起的話必須知道其精確類名,因為只有應用本身才知道精確類名,所以此時預設為false。
android:icon 該屬性就比較好玩了,允許自定義icon,可以不同於應用本身在桌面的icon。如果需要在桌面上建立快捷入口,也許產品會要求換個不同的icon。
android:label 該屬性類似於android:icon,圖示都換了,換個名稱也合情合理吧,此屬性就是為此而生的。
android:name 該屬性可以為任意字串,但最好符合類名命名規範。activity元素的name屬性實質上都會指向一個具體的Activity類,而activity-alias的name屬性僅作為一個唯一標識而已。
android:permission 該屬性指明瞭通過別名宣告調起目標Activity所必需的許可權。
android:targetActivity 該屬性指定了目標Activity,即通過activity-alias調起的Activity是哪個,此屬性其實類似於activity標籤中的name屬性,需要規範的Activity包名類名。
看了以上幾個主要屬性,大家應該意識到activity-alias的屬性是activity屬性的子集,如果是activity-alias和activity共有的屬性,則以activity-alias為準,目標Activity中的設定並不會在activity-alias中生效;如果是僅activity才有的屬性,則為目標Activity配置的屬性會在activity-alias中生效。

activity-alias可以設定自己的intent-filter或meta-date,最常用的就是設定如下intent-filter從而在桌面Launcher上建立一個快捷入口:

<intent-filter>
    <action android:name="android.intent.action.MAIN" />
    <category android:name="android.intent.category.LAUNCHER" />
</intent-filter>

另外需要注意的一點是,在AndroidManifest配置檔案中,activity-alias標籤元素必須宣告在目標Acitvity對應的activity標籤元素之後,否則會編譯錯誤。

下面我們看個例子,到節日的時候自動替換成節日的icon:

<activity
    android:name="com.iscs.mobilewcs.activity.LauncherActivity"
    android:screenOrientation="portrait"
    android:theme="@style/reload_bg_theme"
    >
</activity>

<activity-alias
    android:name=".icon_tag_1212"
    android:enabled="false"
    android:icon="@drawable/img_default"
    android:label="網倉3號-節日版"
    android:targetActivity="com.iscs.mobilewcs.activity.LauncherActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />

        <action android:name="android.intent.action.VIEW" /> <!-- 顯示資料 -->
        <category android:name="android.intent.category.BROWSABLE" /> <!-- 定義成瀏覽器型別,有URL需要處理時會過濾 -->
        <data android:scheme="${APP_SCHEME}" /> <!-- 開啟以iscs協議的URL,這個自己隨便定義。 -->
    </intent-filter>
</activity-alias>

<activity-alias
    android:name=".icon_tag"
    android:enabled="true"
    android:icon="@drawable/ic_launcher"
    android:label="@string/app_mine_name"
    android:targetActivity="com.iscs.mobilewcs.activity.LauncherActivity">
    <intent-filter>
        <action android:name="android.intent.action.MAIN" />
        <category android:name="android.intent.category.LAUNCHER" />
    </intent-filter>
    <intent-filter>
        <category android:name="android.intent.category.DEFAULT" />

        <action android:name="android.intent.action.VIEW" /> <!-- 顯示資料 -->
        <category android:name="android.intent.category.BROWSABLE" /> <!-- 定義成瀏覽器型別,有URL需要處理時會過濾 -->
        <data android:scheme="${APP_SCHEME}" /> <!-- 開啟以iscs協議的URL,這個自己隨便定義。 -->
    </intent-filter>
</activity-alias>

配置好以後就可以呼叫如下程式碼來切換icon:

/**
    1為節日圖示, 2為普通圖示
 */
protected void switchIcon(int useCode) {

    try {
        //要跟manifest的activity-alias 的name保持一致,否則切換沒反應
        String icon_tag = "com.iscs.mobilewcs.icon_tag";
        String icon_tag_1212 = "com.iscs.mobilewcs.icon_tag_1212";

        if (useCode != 3) {

            PackageManager pm = getPackageManager();

            ComponentName normalComponentName = new ComponentName(
                    getBaseContext(),
                    icon_tag);

            //正常圖示新狀態,此處使用用來修改清單檔案中activity-alias下的android:enable的值
            int normalNewState = useCode == 2 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                    : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
            //新狀態跟當前狀態不一樣才執行
            if (pm.getComponentEnabledSetting(normalComponentName) != normalNewState) {
                //PackageManager.DONT_KILL_APP表示執行此方法時不殺死當前的APP程序
                pm.setComponentEnabledSetting(
                        normalComponentName,
                        normalNewState,
                        PackageManager.DONT_KILL_APP);
            }

            ComponentName actComponentName = new ComponentName(
                    getBaseContext(),
                    icon_tag_1212);
            //活動圖示新狀態
            int actNewState = useCode == 1 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
                    : PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
            //新狀態跟當前狀態不一樣才執行
            if (pm.getComponentEnabledSetting(actComponentName) != actNewState) {
                pm.setComponentEnabledSetting(
                        actComponentName,
                        actNewState,
                        PackageManager.DONT_KILL_APP);
            }

        }
    } catch (Exception e) {
        e.printStackTrace();
    }
}