前言

动态更换App图标的方法有很多,本文参考了前人的经验,通过阅读本文,您将了解到如何动态更换桌标(非网络获取桌标图片),以及对标志位的详细阐述,从而更加透彻地理解这一过程。

用到的知识

activity-alias并不是代表一个Activity,而是代表一个已经存在的Activity的别名。它使用在清单文件中,类似Activity标签。它可用来设置某个Activity的快捷入口。

activity-alias基本用法

```xml

...

```

属性解释:

属性 | 含义

---|---

enabled | 是否生效。配置多个activity-alias时,如果只想一个生效,就设置一个为true

exported | 是否可以被其他应用调起。配置intent-filter时默认为true,未配置intent-filter时默认为false,只能被应用自身调起

icon | 自定义生效时的icon

label | 作用同Activity标签中的label属性,主要表现为桌面上的app名称和activity的title的名称

name | 该activity-alias的名字

permission | 指明通过别名声明调起目标Activity所必需的权限

targetActivity | 目标Activity的完整类名

```

要指明目标Activity,可以在AndroidManifest.xml文件中配置activity-alias标签。首先,需要设置包类路径,然后在name属性中指定要调用的Activity名称。这样,就可以通过activity-alias来调起指定的Activity了。

以下是一个示例:

1. 在AndroidManifest.xml中配置别名:

```xml

package="com.example.myapplication">

...>

android:targetActivity="com.example.anotherpackage.AnotherActivity">

```

2. 在代码中调用目标Activity:

```java

Intent intent = new Intent(this, TargetActivity.class);

startActivity(intent);

```

```xml

android:configChanges="keyboard|keyboardHidden|orientation"

android:hardwareAccelerated="true"

android:label="@string/app_name"

android:screenOrientation="portrait"

android:theme="@style/Acrivity_Fullscreen">

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

```

在Android应用中,我们可以通过设置不同的属性来控制组件的显示状态、目标Activity、启动图标等。以下是一些常用属性的解释:

1. android:name:主要用于在代码中获取此组件的启用状态。

2. android:targetActivity:表示点击后跳转的Activity。

3. icon和lable分别表示启动图标和桌面名称。

要在用户退出主页时执行更换图标的逻辑,首先需要获取服务端下发接口并缓存到本地。当获取到服务端接口提示更换节日图标时,判断要显示组件的状态是否为显示状态(COMPONENT_ENABLED_STATE_ENABLED)。

以下是检查组件状态的方法:

```java

private boolean isComponentState(ComponentName componentName) {

return mPackageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED;

}

```

需要注意的是,这里的标识位有以下几种状态:

- COMPONENT_ENABLED_STATE_DEFAULT:默认状态,XML预设的状态。

- COMPONENT_ENABLED_STATE_ENABLED:此组件或应用程序已明确启用,无论其清单中指定了什么。

- COMPONENT_ENABLED_STATE_DISABLED:此组件或应用程序已明确禁用,无论其清单中指定了什么。

- COMPONENT_ENABLED_STATE_DISABLED_USER:用户已明确禁用该应用程序,无论其在清单中指定了什么。

- COMPONENT_ENABLED_STATE_DISABLED_UNTIL_USED:此应用程序应在用户实际使用之前被视为禁用 (这个不清楚怎么翻才好,没有使用过)。

如果组件不处于显示状态,则设置其可见,否则不变。需要注意的是,设置标志位是永久性的,即使应用升级获取此组件状态时,也是之前的值。

以下是完整的代码:

```java

import android.content.ComponentName;

import android.content.pm.PackageManager;

public class ComponentEnabler {

private PackageManager mPackageManager;

public ComponentEnabler(PackageManager packageManager) {

mPackageManager = packageManager;

}

private void enableComponent(ComponentName componentName) {

// 此方法用以启用和禁用组件,会覆盖Androidmanifest文件下定义的属性

mPackageManager.setComponentEnabledSetting(componentName,

PackageManager.COMPONENT_ENABLED_STATE_ENABLED,

PackageManager.DONT_KILL_APP);

}

}

```

```java

public class ChangeAppIconUtils {

private PackageManager mPackageManager;

//默认桌标

private static final String DEFAULT_ICON = "com.x.x.activitys.WelcomeActivity";

//活动桌标

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;

}

/**

* 启动组件

*

* @param componentName 组件名

*/

private void enableComponent(ComponentName componentName) {

//此方法用以启用和禁用组件,会覆盖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);

}

/**

* 当前组件的状态,判断当前enable状态

* 即使xml里面设置enable=false 标志位第一次获取时 还是COMPONENT_ENABLED_STATE_DEFAULT

* 所以这里判断是否为enable

*

* @param componentName return true 未被应用为可显示

*/

private boolean isComponentState(ComponentName componentName) {

//默认图标且为默认状态则返回false

return (!DEFAULT_ICON.equals(componentName.getClassName()) && mPackageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_DEFAULT) && mPackageManager.getComponentEnabledSetting(componentName) != PackageManager.COMPONENT_ENABLED_STATE_ENABLED;

}

/**

* 更换app图标

*

* @param 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);

//判断状态

if (DEFAULT_ICON.equals(changeIcon)) {//设置默认icon

boolean componentState = isComponentState(defaultIcon);

if (componentState) {//如果不一样则设置

enableComponent(defaultIcon);

disableComponent(otherIcon); //restartSystemLauncher(context, mPackageManager);

}

} else {//其它icon

boolean componentState = isComponentState(otherIcon);

if (componentState) {

enableComponent(otherIcon);

disableComponent(defaultIcon); //restartSystemLauncher(context, mPackageManager);

}

}

}

/**

* 没用,有的rom不会让你杀掉Launcher进程,例如华为,VIVO

* @param context 上下文

* @param pm PackageManager对象

*/

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 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 (TAGNAME_CHANGE_ICON2.equals(tagName)) {//icon2表示更改为其他图标,anther表示更改为默认图标。根据传入参数来执行相应操作。调用changeIconState方法。并传入相应的参数。如果没有传入参数,那么就默认使用默认图标。即调用changeIconState方法时只传入一个参数。否则传入两个参数。分别代表要使用的图标名称。如果传入的不是这两个字符串中的任意一个。那么就会抛出异常。因为这两个字符串是唯一的。不能有其他的字符串来代替它们。所以在调用changeIconState方法时必须传入这两个字符串中的任意一个。否则就会抛出异常。

在Android开发中,如果你想更改应用的启动图标,你可以使用`ChangeAppIconUtils`类。这个类需要一个`PackageManager`实例,并且你需要在主Activity的`onDestroy()`方法中调用这个类的方法来更改图标。

但是在使用过程中,可能会遇到一些问题。例如,如果在更改图标后尝试使用AS再次启动应用,可能会发现应用无法启动。这是因为在某些设备上,更改图标可能会导致应用的重启。解决这个问题的方法是关闭快速启动功能。

另外,当你更改应用图标后,可能需要等待一段时间,才能看到图标的变化。对于华为手机来说,点击图标之前可能会提示“该应用未安装”,但当桌面更新后,就可以正常点击进入应用了。

以下是如何使用`ChangeAppIconUtils`类来更改应用图标的示例代码:

```java

public class MainActivity extends AppCompatActivity {

@Override

protected void onCreate(Bundle savedInstanceState) {

super.onCreate(savedInstanceState);

setContentView(R.layout.activity_main);

}

@Override

protected void onDestroy() {

super.onDestroy();

new ChangeAppIconUtils(getPackageManager()).setAppLauncherIcon(getApplicationContext(), sharePreUtils.getStringValue(ChangeAppIconUtils.KEY_LAUNCHER_ICON, ""));

}

}

```

在这个示例中,我们在`MainActivity`的`onDestroy()`方法中调用了`ChangeAppIconUtils`的`setAppLauncherIcon()`方法来更改应用的启动图标。注意,我们需要传递当前应用程序的上下文和新的图标资源ID作为参数。

使用以下方法可以加快图标的切换,但请注意,某些设备可能无法重启桌面。例如,华为、Vivo和Oppo等品牌的部分设备可能无法实现此功能。然而,小米手机可以实现这一功能,尽管在1至3秒的时间里,系统仍会关闭应用一次。

需要注意的是,这种方法仅修改图标本身,而不会改变快捷方式(如果有的话)。此外,您还可以使用代码动态更新快捷方式。但是,这种方法无法实现动态加载网络图片,仅适用于本地资源修改的桌标。