Android 利用 <activity-alias> 動態改變 App 桌面圖標

LilaCuller 7年前發布 | 14K 次閱讀 Activity Android開發 移動開發

最近看到「醫生」寫的一篇文章: 上次發版我就改了一行代碼! ,真是腦洞大開,沒想到安卓應用的桌面圖標還能動態改變,漲知識了,接觸安卓開發以來還是第一次知道這玩意兒,頓感無地自容。于是細細讀來,著手實踐,對其中涉及到的知識點對著官方文檔了解一番,總結記錄于此。

案例分析 & 需求來源

每逢雙十一購物節,你會發現手淘 App 的桌面圖標會發生改變,當然應用里面的主題色調也會改變,變得非常喜慶,營造出一種節日的氛圍,用戶體驗極好。既然存在這樣的產品運營方式,那么如何從技術上實現呢?修改應用主題色調在這里就不談了,常見的效果有黑白主題切換、主題包下載等,關于對應開發實現方式的講解,網上相關資料很多。這里聊聊如何修改桌面圖標,畢竟這個點涉及到的知識還是很少見的。

其實很簡單,利用 AndroidManifest.xml 文件中的 <activity-alias> 標簽即可實現。

<activity-alias> 介紹

大家知道,對于 Activity 組件,使用時需要在 Manifest 文件中通過 標簽注冊 name、theme、intent-filter 等相關屬性信息,然后通過 Intent 操作便可以啟動對應 Activity。殊不知,我們還能通過 <activity-alias> 標簽為每個 Activity 注冊一個“別名”,通過這個別名也能啟動對應的目標 Activity。我們來看一下這個“別名”能夠設置哪些屬性:

<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>

可以看出,大部分屬性與 <Activity> 標簽的屬性一致,簡單分析一下:

  • android:enabled屬性,布爾類型,是否開啟別名設置,默認值為 true;

  • android:exported屬性,布爾類型,是否支持其他應用通過這個別名訪問目標 Activity,默認值為 true;

  • android:icon和 label 屬性:類似 <activity> 標簽,表示目標 Activity 的顯示圖標和標簽;

  • android:name屬性:Activity 別名,在 <activity> 標簽中, name 屬性必須與對應 Activity 文件的名字保持一致,而這里的別名可任意設置,保證唯一性即可;

  • android:permission屬性:權限設置,對別名的使用加以限制,詳細屬性值參考開發者官網對 權限部分 的說明;

  • android:targetActivity屬性:指定別名能夠啟動的目標 Activity,注意,屬性值一定要對應到 <activity> 標簽中的 name 屬性,并且該 <activity> 標簽一定要位于 <activity-alias> 標簽前面;

實戰演練

了解完 <activity-alias> 的基本知識之后,就知道動態修改桌面圖標和應用名稱是怎么做到的了。其實就是給整個應用的入口 Activity 添加一個 <activity-alias> 標簽,并設置預先設計好的替代桌面圖標和應用名稱,并配置相同的 <intent-filter> 屬性,動態啟動即可。

舉例說明一下,Manafest 文件關鍵代碼如下:

<application
    android:allowBackup="true"
    android:icon="@mipmap/ic_launcher"
    android:label="Samples"
    android:supportsRtl="true"
    android:name=".MyApplication"
    android:theme="@style/AppTheme">
    <activity android:name=".MainActivity">
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

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

    <activity-alias
        android:name=".MainAliasActivity"
        android:targetActivity=".MainActivity"
        android:label="Samples Alias"
        android:icon="@mipmap/ic_launcher_alias"
        android:enabled="false">

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

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

    </activity-alias>

</application>

注意看,在別名設置中添加了 <intent-filter> 標簽,與 targetActivity 的設置一致:

  • android.intent.action.MAIN 表示這個別名設置是整個應用的入口,應用啟動時第一個創建的就是這個 Activity;
  • android.intent.category.LAUNCHER 表示這個別名設置將出現在桌面 Launcher 應用上;

至于其他屬性, <activity> 標簽中也有相應設置,只是通常我們在 <application> 標簽中統一設置而已,然后 <activity> 標簽默認繼承 <application> 標簽中的設置。上述代碼還有一點需要注意的是, android:enabled 屬性設為 false,否則運行時將會在桌面上出現兩個相同功能但不同顯示的應用圖標和名稱。

然后在 Activity 中動態切換,通過 PackageManager 對象提供的 setComponentEnabledSetting() 方法關閉當前 Component 組件,并啟動別名對應的 Component 組件即可,參考代碼如下:

public void onClickOne(View v){
    PackageManager pm = getPackageManager();
    pm.setComponentEnabledSetting(getComponentName(),
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
    pm.setComponentEnabledSetting(new ComponentName(this, "com.yifeng.samples.AliasName"),
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
}

效果如圖:

注意,修改之后,需要稍等片刻才能看到變化。如果想在修改完成之后立即看到變化,只能通過 Intent 重啟 Launcher 應用。代碼如下:

Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.addCategory(Intent.CATEGORY_DEFAULT);
List<ResolveInfo> resolves = pm.queryIntentActivities(intent, 0);
for (ResolveInfo res : resolves) {
    if (res.activityInfo != null) {
        ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
        am.killBackgroundProcesses(res.activityInfo.packageName);
    }
}

然后不要忘了在 Manifest 文件中添加權限:

<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>

通過這些設置,基本上就能實現動態修改應用的桌面圖標和名稱。通常,可以提前將新的圖標放置在應用中,待到特定時間段通過服務器端的消息推送等行為靈活觸發 App 修改 Launcher 上顯示的圖標和名稱,這樣就不必為了改個圖標單獨迭代一個新版本。值得注意的是,從產品角度上講,一般不會為了短期的一個活動修改應用名稱,而是只修改桌面圖標,并且新的桌面圖標也只是在原有的基礎樣式上動動手腳,起到錦上添花的作用,萬不可改得面貌全非,否則會讓用戶產生誤解,那就得不償失了。

遺留問題

第一個,以上這種設置只能修改 Launcher 上的應用圖標和名稱,屬于應用級別(application level)的,無法達到系統級別(OS level)的修改,比如改完之后,使用 menu 物理鍵打開 multi-task 窗口,或者打開設置查看應用列表,你會發現,對應應用的圖標和名稱還是顯示之前默認的那些。不過,對于普通用戶來說,主要還是在于桌面 Launcher 上的顯示,畢竟這里才是最直觀也是最常用到的地方。

第二個,這里我們將新的桌面圖標提前放置在應用資源文件中,然后通過在 <activity-alias> 標簽中指定對應引用即可,有沒有一種方式能夠在 Java 代碼中設置 Icon 屬性呢?如果可以的話,那就更加完美了,將圖標文件放置在服務器,使用起來豈不是更加靈活?一番努力之后,還是沒能找到對應解決方案,如果你們有知道的話,請留言告知,或者關注我的微信公眾號(搜索:NiaoTech),與我交流,謝謝。

 

來自:http://yifeng.studio/2016/12/30/android-change-app-launcher-icon-dynamically/

 

 本文由用戶 LilaCuller 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!