常用的一些电商应用每到节日时,应用的icon会换成相应节日的,今天研究一下这个技术点,之前还以为android 会有这方面的api ,可以在代码中动态设置,这样从服务端下载图片后,直接调用这个api不就可以了,但是目前没有找到这个方法。今天在网上看到有人用 <activity-alias> 来动态修改app icon成功的 。
<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>
以下是这些属性的解释
- 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>
属性,动态启动即可。
在menifest.xml添加多个activity-alias、删除主Activity标签
<category android:name="android.intent.category.LAUNCHER" />属性
<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=".icon_tag"
android:enabled="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
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=".icon_tag_1212"
android:enabled="false"
android:icon="@mipmap/ic_logo2"
android:label="活动图标"
android:targetActivity=".MainActivity">
<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()
private void switchIcon(int useCode) {
try {
//要跟manifest的activity-alias 的name保持一致
String icon_tag = "demo.com.demo0104.icon_tag";
String icon_tag_1212 = "demo.com.demo0104.icon_tag_1212";
if (useCode != 3) {
PackageManager pm = getPackageManager();
ComponentName normalComponentName = new ComponentName(getBaseContext(), icon_tag);
//正常图标新状态
int normalNewState = useCode == 2 ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
: PackageManager.COMPONENT_ENABLED_STATE_DISABLED;
if (pm.getComponentEnabledSetting(normalComponentName) != normalNewState) {//新状态跟当前状态不一样才执行
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) {
}
}
这个只是一个测试demo ,实际中肯定是调用服务端接口来判断是否需要修改app icon 。这样修改后 ,过一会桌面上的icon才会改变, 如果想立即改变需要使用intent重启luncher。
Intent intent = new Intent(Intent.ACTION_MAIN);
intent.addCategory(Intent.CATEGORY_HOME);
intent.addCategory(Intent.CATEGORY_DEFAULT);
List<ResolveInfo> resolves = mPm.queryIntentActivities(intent, 0);
for (ResolveInfo res : resolves) {
if (res.activityInfo != null) {
ActivityManager am = (ActivityManager) getSystemService(ACTIVITY_SERVICE);
am.killBackgroundProcesses(res.activityInfo.packageName);
}
}
这个操作需要添加相应的权限
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
遗留问题
1、这种方式只能修改luncher上的icon和名称,属于应用级别 ,但是在系统设置—— 应用管理 查看,还是以前的icon。
参考文档
Android 利用 <activity-alias> 动态改变 App 桌面图标
动态更换应用Icon
Android动态修改icon--让你的app浪起来