前言
動(dòng)態(tài)更換App圖標(biāo),網(wǎng)上可以收搜到很多,這里也是參考前人經(jīng)驗(yàn),讀完本文可以得到,如何動(dòng)態(tài)更換桌標(biāo)(非網(wǎng)絡(luò)獲取桌標(biāo)圖片),標(biāo)志位的闡述,更加透徹的理解.
用到的知識(shí)
activity-alias并不是代表一個(gè)Activity过吻,而是代表一個(gè)已經(jīng)存在的Activity的別名虏肾。
它使用在清單文件中,類似Activity標(biāo)簽赂弓。它可用來設(shè)置某個(gè)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>
屬性解釋:
屬性 | 含義 |
---|---|
enabled | 是否生效。配置多個(gè)activity-alias時(shí)撼短,如果只想一個(gè)生效踩寇,就設(shè)置一個(gè)為true |
exported | 是否可以被其他應(yīng)用調(diào)起际起,配置intent-filter時(shí)默認(rèn)為true滓鸠,未配置intent-filter時(shí)默認(rèn)為false雁乡,只能被應(yīng)用自身調(diào)起 |
icon | 自定義生效時(shí)的icon |
label | 作用同Activity標(biāo)簽中的label屬性第喳,主要表現(xiàn)為桌面上的app名稱和activity的title的名稱 |
name | 該activity-alias的名字 |
permission | 指明通過別名聲明調(diào)起目標(biāo)Activity所必需的權(quán)限 |
targetActivity | 指明目標(biāo)Activity糜俗,類似于Activity標(biāo)簽中的name屬性,需寫明包類路徑曲饱。表明通過activity-alias調(diào)起的是哪個(gè)Activity |
使用
- 首先配置AndroidManifest.xml,設(shè)置別名
<activity
android:name=".activitys.WelcomeActivity"
android:configChanges="keyboard|keyboardHidden|orientation"
android:hardwareAccelerated="true"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/Acrivity_Fullscreen">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<!-- 動(dòng)態(tài)更換app圖片的方案-->
<activity-alias
android:name=".changeLauncherIconActivity"
android:configChanges="keyboard|keyboardHidden|orientation"
android:enabled="false"
android:icon="@drawable/yishijie_logo"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:targetActivity=".activitys.WelcomeActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
這里要注意的是
android:name 標(biāo)識(shí):主要用于在代碼中獲取此組件enable的狀態(tài);
android:targetActivity標(biāo)識(shí),targetActivity標(biāo)識(shí)就是點(diǎn)擊后跳轉(zhuǎn)的Activity;
icon和lable分別是啟動(dòng)圖標(biāo)和桌面名稱
- 代碼配置
思路是首先獲取服務(wù)端下發(fā)接口,緩存到本地,等用戶退出主頁的時(shí)候執(zhí)行更換圖標(biāo)的邏輯
- 獲取服務(wù)端接口,接口提示更換節(jié)日?qǐng)D標(biāo)
- 判斷要顯示組件的狀態(tài)是否為顯示狀態(tài)COMPONENT_ENABLED_STATE_ENABLED
private boolean isComponentState(ComponentName componentName) {
return mPackageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
}
注意:這里的標(biāo)示位
屬性 | 含義 |
---|---|
COMPONENT_ENABLED_STATE_DEFAULT | 默認(rèn)狀態(tài),xml預(yù)設(shè)的狀態(tài) |
COMPONENT_ENABLED_STATE_ENABLED | 此組件或應(yīng)用程序已明確啟用悠抹,無論其清單中指定了什么。 |
COMPONENT_ENABLED_STATE_DISABLED | 此組件或應(yīng)用程序已明確禁用扩淀,無論其清單中指定了什么楔敌。 |
COMPONENT_ENABLED_STATE_DISABLED_USER | 用戶已明確禁用該應(yīng)用程序,無論其在清單中指定了什么驻谆。 |
COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED | his application should be considered, until the point where the user actually wants to use it. (這個(gè)不清楚怎么翻才好,沒有使用過) |
- 如果不是則設(shè)置其可見,否則不變
注意這里設(shè)置標(biāo)志位是永久性的,即使App升級(jí)獲取此組件狀態(tài)時(shí),也是之前的值
private void enableComponent(ComponentName componentName) {
//此方法用以啟用和禁用組件卵凑,會(huì)覆蓋Androidmanifest文件下定義的屬性
mPackageManager.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
完整代碼
public class ChangeAppIconUtils {
private PackageManager mPackageManager;
//默認(rèn)桌標(biāo)
private static final String DEFAULT_ICON = "com.x.x.activitys.WelcomeActivity";
//活動(dòng)桌標(biāo)
private static final String ANTHER_ICON = "com.x.x.changeLauncherIconActivity";
//緩存文件鍵值
public static final String KEY_LAUNCHER_ICON = "key_launcher_icon";
public ChangeAppIconUtils(PackageManager mPackageManager) {
this.mPackageManager = mPackageManager;
}
/**
* 啟動(dòng)組件
*
* @param componentName 組件名
*/
private void enableComponent(ComponentName componentName) {
//此方法用以啟用和禁用組件,會(huì)覆蓋Androidmanifest文件下定義的屬性
mPackageManager.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
}
/**
* 禁用組件
*
* @param componentName 組件名
*/
private void disableComponent(ComponentName componentName) {
mPackageManager.setComponentEnabledSetting(componentName,
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
}
/**
* 當(dāng)前組件的狀態(tài),判斷當(dāng)前enable狀態(tài)
* 即使xml里面設(shè)置enable=false 標(biāo)志位第一次獲取時(shí) 還是COMPONENT_ENABLED_STATE_DEFAULT
* 所以這里判斷是否為enable
*
* @param componentName return true 未被應(yīng)用為可顯示
*/
private boolean isComponentState(ComponentName componentName) {
//默認(rèn)圖標(biāo)且為默認(rèn)狀態(tài)則返回false
return !(DEFAULT_ICON.equals(componentName.getClassName()) && mPackageManager.getComponentEnabledSetting(componentName) == PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)
&& mPackageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED;
}
/**
* 更換app 圖標(biāo)
*
* @param context context
* @param changeIcon changeIcon
*/
private void changeIconState(Context context, String changeIcon) {
ComponentName defaultIcon = new ComponentName(context, DEFAULT_ICON);
ComponentName otherIcon = new ComponentName(context, ANTHER_ICON);
//判斷狀態(tài)
if (DEFAULT_ICON.equals(changeIcon)) {//設(shè)置默認(rèn)icon
boolean componentState = isComponentState(defaultIcon);
if (componentState) {//如果不一樣則設(shè)置
enableComponent(defaultIcon);
disableComponent(otherIcon);
// restartSystemLauncher(context, mPackageManager);
}
} else {//其它icon
boolean componentState = isComponentState(otherIcon);
if (componentState) {
enableComponent(otherIcon);
disableComponent(defaultIcon);
// restartSystemLauncher(context, mPackageManager);
}
}
}
/**
* 沒啥用,有的rom不會(huì)讓你殺掉Launcher進(jìn)程胜臊,例如華為勺卢,VIVO
* @param context
* @param pm
*/
private void restartSystemLauncher(Context context, PackageManager pm) {
ActivityManager am = (ActivityManager) context.getSystemService(Activity.ACTIVITY_SERVICE);
Intent i = new Intent(Intent.ACTION_MAIN);
i.addCategory(Intent.CATEGORY_HOME);
i.addCategory(Intent.CATEGORY_DEFAULT);
List<ResolveInfo> resolves = pm.queryIntentActivities(i, 0);
for (ResolveInfo res : resolves) {
if (res.activityInfo != null && am != null) {
am.killBackgroundProcesses(res.activityInfo.packageName);
}
}
}
public void setAppLauncherIcon(Context context, String tagName) {
if (!TextUtils.isEmpty(tagName)) {
if ("icon2".equals(tagName)) {
changeIconState(context, ANTHER_ICON);
} else {
changeIconState(context, DEFAULT_ICON);
}
}
}
}
使用的時(shí)候只需要
new ChangeAppIconUtils(getPackageManager()).setAppLauncherIcon(getApplicationContext(), sharePreUtils.getStringValue(ChangeAppIconUtils.KEY_LAUNCHER_ICON, ""));
這里的sharepreUtils是工具類獲取接口中下發(fā)的狀態(tài)值,icon1默認(rèn)圖標(biāo),icon2為節(jié)日?qǐng)D標(biāo),因?yàn)橹坝腥苏f會(huì)導(dǎo)致app的重啟,所以這里的操作時(shí)放在主Activity onDestory里面執(zhí)行的.
問題
- 目前已知的問題,當(dāng)改完圖標(biāo)之后,使用AS再次啟動(dòng)會(huì)無法啟動(dòng),把快速啟動(dòng)關(guān)掉就可以了
Error while executing: am start -n "in.myinnos.changeappiconandname/in.myinnos.changeappiconandname.MainActivity-settings" -a android.intent.action.MAIN -c android.intent.category.LAUNCHER
Starting: Intent { act=android.intent.action.MAIN cat=[android.intent.category.LAUNCHER] cmp=in.myinnos.changeappiconandname/.MainActivity-settings }
Error type 3
Error: Activity class {in.myinnos.changeappiconandname/in.myinnos.changeappiconandname.MainActivity-settings} does not exist.
Error while Launching activity
這里我使用打包的方式覆蓋安裝沒有出現(xiàn)這個(gè)問題
- 改過圖標(biāo)后,會(huì)過一會(huì)兒圖標(biāo)才會(huì)改變象对,有的(華為)改變之前點(diǎn)擊會(huì)提示<該應(yīng)用未安裝>黑忱,但是桌面更新后就可以點(diǎn)進(jìn)去了
- 使用重啟桌面的方法,加快圖標(biāo)的切換,1.會(huì)被系統(tǒng)禁用例如華為 Vivo Oppo則無法重啟桌面2.小米可以,但是還是會(huì)在1~3秒的時(shí)候關(guān)閉應(yīng)用一次
- 僅僅修改的是啟動(dòng)圖標(biāo),如果有快捷方式那么快捷方式不會(huì)發(fā)生改變,當(dāng)然可以使用代碼動(dòng)態(tài)更新快捷方式
- 無法動(dòng)態(tài)加載網(wǎng)絡(luò)圖片,還是僅僅是本地資源修改的桌標(biāo)