Android偽動態修改Icon(ic_launcher),activity-alias
轉載請註明原創出處,謝謝!
ps:很早以前,在寫日曆需求的時候,特意留意了一下小米日曆app的ui,發現了一個有意思的互動(月和周之間的切換),隨著小米日曆app的更新,發現了一個更有意思的地方,app的icon是隨著日期,天氣,溫度改變的。聯想到馬上要到雙11了,手機裡的很多app的icon改成了雙11版。所以簡單的瞭解了一下
本文實現的效果絕對不是小米日曆的效果,差別還是很大的。
所以本文章為偽動態修改Icon。
實現還是很簡單的,主要用到的是activity-alias這個便籤,也就是activity的一個別名,android允許一個activity有多個別名。
在寫程式碼前,先準備幾個icon,我裝備了2個,加上原生的,一共3個icon。

ic_launcher.png

ic_launcher1.png

ic_launcher2.png
先看AndroidManifest.xml程式碼
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.demo.icon"> <uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:supportsRtl="true" 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=".MainActivity_Test1" android:enabled="false" android:icon="@mipmap/ic_launcher1" android:label="Test1" android:targetActivity=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity-alias> <activity-alias android:name=".MainActivity_Test2" android:enabled="false" android:icon="@mipmap/ic_launcher2" android:label="Test2" android:targetActivity=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity-alias> </application> </manifest>
在AndroidManifest.xml中設定了2個activity-alias,其中MainActivity_Test1的icon是ic_launcher1,MainActivity_Test2的icon是ic_launcher2。
android:enabled="false"程式碼決定了是否起作用,如果想要有一個有效,就配置成 android:enabled="true"。
因為我的程式碼是預設使用MainActivity的icon。所以2個activity-alias設定為fale。如果你有興趣,將他們都設定成true,執行成功後,你會神奇的發現桌面上除了MainActivity的icon,還多了2個activity-alias的icon。
xml寫好後,開始寫MainActivity程式碼。
public class MainActivity extends AppCompatActivity { private static final String TAG = "MainActivity"; private PackageManager packageManager; private ActivityManager activityManager; private ComponentName componentNameDefault; private ComponentName componentName1; private ComponentName componentName2; private static final String DEFAULT = "com.demo.icon.MainActivity"; private static final String ACTIVITY_ALIAS_1 = "com.demo.icon.MainActivity_Test1"; private static final String ACTIVITY_ALIAS_2 = "com.demo.icon.MainActivity_Test2"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); // componentNameDefault = getComponentName(); // getComponentName得到的是當前的,不是MainActivity Log.i(TAG, "onCreate:------> " + getComponentName()); packageManager = getPackageManager(); activityManager = (ActivityManager) getSystemService(Activity.ACTIVITY_SERVICE); componentNameDefault = new ComponentName(this, DEFAULT); componentName1 = new ComponentName(this, ACTIVITY_ALIAS_1); componentName2 = new ComponentName(this, ACTIVITY_ALIAS_2); // 顯示預設 findViewById(R.id.Default).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { disableComponent(componentName1); disableComponent(componentName2); enableComponent(componentNameDefault); start(); } }); // 顯示1 findViewById(R.id.Test1).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { disableComponent(componentNameDefault); disableComponent(componentName2); enableComponent(componentName1); start(); } }); // 顯示2 findViewById(R.id.Test2).setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { disableComponent(componentNameDefault); disableComponent(componentName1); enableComponent(componentName2); start(); } }); } /** * 立即開始執行,如果不執行start方法,根據ROM的不同,在禁用了元件之後,會等一會,Launcher也會自動重新整理圖示。 */ private void start() { Intent intent = new Intent(Intent.ACTION_MAIN); intent.addCategory(Intent.CATEGORY_HOME); intent.addCategory(Intent.CATEGORY_DEFAULT); List<ResolveInfo> resolves = packageManager.queryIntentActivities(intent, PackageManager.COMPONENT_ENABLED_STATE_DEFAULT); // 預設啟用狀態 for (ResolveInfo res : resolves) { if (res.activityInfo != null) { activityManager.killBackgroundProcesses(res.activityInfo.packageName); // 殺死後臺程序 } } } private void enableComponent(ComponentName componentName) { // 顯示 packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP); } private void disableComponent(ComponentName componentName) { // 隱藏 packageManager.setComponentEnabledSetting(componentName, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP); } }
MainActivity有3個按鈕,點選不同按鈕,就可以在家嗎中看到切換成功的icon。
注意
-
如果你在顯示和隱藏後,不執行start()方法,icon的圖片也可以被換掉,只是根據ROM的不同,會等一會自動重新整理圖示。
-
不要使用Activity的getComponentName()方法,得到預設的ComponentName,getComponentName得到的是當前的ComponentName,不是“com.demo.icon.MainActivity”。

getComponentName輸出日誌.png
- activity-alias最好不要刪除。
如果我們在應用v1.0的版本在某些特定的情況下使用的activity-alias1,而在應用v2.0的版本中刪除了activity-alias1的程式碼,那麼你的應用,將在桌面上找不到,也打不開。這種魔鬼操作對於公司是絕對不可以的。
-
執行start方法,當年退出app的時候,桌面會進入一個空白期(我不知道該桌面描述,就是類似桌面重新載入一樣),這就是我為什麼所這是偽動態修改Icon。
-
icon檔案是寫死的,如果你想從網路上獲取,很抱歉,我還不知道怎麼實現。