如何在 Android 中以编程方式更改应用程序图标?
Posted
技术标签:
【中文标题】如何在 Android 中以编程方式更改应用程序图标?【英文标题】:How to change an application icon programmatically in Android? 【发布时间】:2010-11-09 08:04:06 【问题描述】:是否可以直接从程序中更改应用程序图标?
我的意思是,更改 res\drawable
文件夹中的 icon.png
。
我想让用户从程序中更改应用程序的图标,以便下次他们在启动器中看到之前选择的图标。
【问题讨论】:
【参考方案1】:试试这个,对我来说很好用:
1 .修改androidManifest.xml
中的MainActivity
部分,从中删除,与intent-filter
部分中的MAIN
类别对齐
<activity android:name="ru.quickmessage.pa.MainActivity"
android:configChanges="keyboardHidden|orientation"
android:screenOrientation="portrait"
android:label="@string/app_name"
android:theme="@style/CustomTheme"
android:launchMode="singleTask">
<intent-filter>
==> <action android:name="android.intent.action.MAIN" /> <== Delete this line
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
2 。为您的每个图标创建<activity-alias>
。像这样
<activity-alias android:label="@string/app_name"
android:icon="@drawable/icon"
android:name=".MainActivity-Red"
android:enabled="false"
android:targetActivity=".MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
3 .以编程方式设置:为相应的activity-alias
设置 ENABLE 属性
getPackageManager().setComponentEnabledSetting(
new ComponentName("ru.quickmessage.pa", "ru.quickmessage.pa.MainActivity-Red"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
请注意,必须始终启用至少一个。
【讨论】:
遗憾的是,在我尝试过的设备上工作方式不同。适用于 HTC Desire 2.2,但在 Galaxy Nexus 4.2.2 和 Nexus 7 4.3 上不可靠。在 Galaxy Nexus 上,可能会导致应用程序的所有图标消失,并且所有小部件也会被删除。可惜,所以我不得不在以后的设备上删除此功能。 我无法启动我的应用程序,因为我删除了这个:这是一个老问题,但由于没有明确的 Android 功能,它仍然有效。来自 facebook 的人找到了解决办法——不知何故。今天,我找到了一种适合我的方法。不完美(请参阅此答案末尾的备注),但它有效!
主要想法是,我更新了我的应用程序快捷方式的图标,它是由我的主屏幕上的启动器创建的。当我想更改快捷方式图标上的某些内容时,我先将其删除,然后使用新的位图重新创建它。
这里是代码。它有一个按钮increment
。按下时,快捷方式将替换为具有新计数的快捷方式。
首先,您需要在清单中获得这两个权限:
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT" />
<uses-permission android:name="com.android.launcher.permission.UNINSTALL_SHORTCUT" />
那么你需要这两种方法来安装和卸载快捷方式。 shortcutAdd
方法创建一个包含数字的位图。这只是为了证明它确实发生了变化。你可能想在你的应用中用一些你想要的东西来改变那部分。
private void shortcutAdd(String name, int number)
// Intent to be send, when shortcut is pressed by user ("launched")
Intent shortcutIntent = new Intent(getApplicationContext(), Play.class);
shortcutIntent.setAction(Constants.ACTION_PLAY);
// Create bitmap with number in it -> very default. You probably want to give it a more stylish look
Bitmap bitmap = Bitmap.createBitmap(100, 100, Bitmap.Config.ARGB_8888);
Paint paint = new Paint();
paint.setColor(0xFF808080); // gray
paint.setTextAlign(Paint.Align.CENTER);
paint.setTextSize(50);
new Canvas(bitmap).drawText(""+number, 50, 50, paint);
((ImageView) findViewById(R.id.icon)).setImageBitmap(bitmap);
// Decorate the shortcut
Intent addIntent = new Intent();
addIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
addIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
addIntent.putExtra(Intent.EXTRA_SHORTCUT_ICON, bitmap);
// Inform launcher to create shortcut
addIntent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
getApplicationContext().sendBroadcast(addIntent);
private void shortcutDel(String name)
// Intent to be send, when shortcut is pressed by user ("launched")
Intent shortcutIntent = new Intent(getApplicationContext(), Play.class);
shortcutIntent.setAction(Constants.ACTION_PLAY);
// Decorate the shortcut
Intent delIntent = new Intent();
delIntent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
delIntent.putExtra(Intent.EXTRA_SHORTCUT_NAME, name);
// Inform launcher to remove shortcut
delIntent.setAction("com.android.launcher.action.UNINSTALL_SHORTCUT");
getApplicationContext().sendBroadcast(delIntent);
最后,这里有两个监听器来添加第一个快捷方式并使用递增计数器更新快捷方式。
@Override
protected void onCreate(Bundle savedInstanceState)
super.onCreate(savedInstanceState);
setContentView(R.layout.test);
findViewById(R.id.add).setOnClickListener(new OnClickListener()
@Override
public void onClick(View v)
shortcutAdd("changeIt!", count);
);
findViewById(R.id.increment).setOnClickListener(new OnClickListener()
@Override
public void onClick(View v)
shortcutDel("changeIt!");
count++;
shortcutAdd("changeIt!", count);
);
备注:
如果您的应用在主屏幕上控制更多快捷方式,例如在Intent
中有不同的额外内容。它们只需要不同的名称,以便卸载并重新安装正确的名称。
在 Android 中以编程方式处理快捷方式是众所周知的、广泛使用但不受官方支持的 Android 功能。它似乎适用于默认启动器,我从未在其他任何地方尝试过。所以不要怪我,当你收到这个用户电子邮件“它在我的 XYZ 上不起作用,双根,超级破手机”
启动器在安装快捷方式时写入Toast
,在卸载快捷方式时写入。所以每次我更改图标时都会得到两个Toast
s。这并不完美,但只要我的应用程序的其余部分完美......
【讨论】:
今天的日历应用程序每天都会在没有祝酒的情况下改变图标。 @Jim(我认为)这实际上是一个小部件 shortcutDel 很遗憾在 Marshmallow 中不再起作用,另请参阅 ***.com/a/33731620/1545993 这会替换快捷方式图标,而不是启动器图标 在下面几行中什么是 Play.class 和 Constants.ACTION_PLAY Intent shortcutIntent = new Intent(getApplicationContext(), Play.class); shortcutIntent.setAction(Constants.ACTION_PLAY);【参考方案3】:您不能更改已签名并密封的 APK 中的清单或资源,除非通过软件升级。
【讨论】:
@Nanne:这是一个应用程序小部件或主屏幕功能,而不是应用程序图标。除非通过软件升级,否则您仍然无法更改清单或资源。 ?不,我的意思是相反:它不是(宣传为)小部件。我将其添加为应用程序快捷方式。但是,正如你所说,仅仅因为这个非库存的东西意味着它只是一个图标,这并不意味着它是:) @NeTeInStEiN:它不适用于所有主屏幕(例如,那些不注意组件启用更改的屏幕)。 不再是真的了。 Android 6+ 上的 Google 日历每天都会在启动器中更改。今天,图标是“2”,昨天是“1”。通常,图标上有一个“31”。但不再是,它改变了。有谁知道这是怎么可能的? @Bobby:我的意思是,除了数千种 Android 设备型号中的数百个不同的预装主屏幕之外,Play 商店中有数百甚至数千种主屏幕实现存在的。欢迎那些主屏幕实现具有允许动态启动器图标替换的钩子。但是,并非所有主屏幕都必须提供此功能。仅仅因为您在一台设备上的一个主屏幕上看到一个应用程序的这种行为并不意味着它适用于所有设备上所有主屏幕上的所有应用程序。【参考方案4】:以编程方式,您可能希望自己发布应用程序启动器:
注意:此方法不再适用于 Android 8.0 - Oreo
在您的 AndroidManifest.xml 中,添加:
<uses-permission android:name="com.android.launcher.permission.INSTALL_SHORTCUT"/>
然后你需要创建你的应用启动器意图:
Intent myLauncherIntent = new Intent();
myLauncherIntent.setClassName("your.package.name", "YourLauncherActivityName");
myLauncherIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
使用您的应用启动器和自定义图标创建安装快捷方式意图:
Intent intent = new Intent();
intent.putExtra(Intent.EXTRA_SHORTCUT_INTENT, myLauncherIntent);
intent.putExtra(Intent.EXTRA_SHORTCUT_NAME, "Application Name");
intent.putExtra
(
Intent.EXTRA_SHORTCUT_ICON_RESOURCE,
Intent.ShortcutIconResource.fromContext
(
getApplicationContext(),
R.drawable.app_icon
)
);
intent.setAction("com.android.launcher.action.INSTALL_SHORTCUT");
最后启动广播意图:
getApplicationContext().sendBroadcast(intent);
【讨论】:
这会替换快捷方式图标,而不是启动器图标【参考方案5】:假设您的意思是更改主屏幕上显示的图标,这可以通过创建一个完全执行此操作的小部件来轻松完成。这里有一篇文章演示了如何为类似于 iPhone 的“新消息”类型的应用程序实现这一点:
http://www.cnet.com/8301-19736_1-10278814-251.html
【讨论】:
【参考方案6】:@P-A 的解决方案部分适用于我。在下面详细说明我的发现:
1)第一个代码sn-p不正确,见下:
<activity
...
<intent-filter>
==> <action android:name="android.intent.action.MAIN" /> <== This line shouldn't be deleted, otherwise will have compile error
<category android:name="android.intent.category.LAUNCHER" /> //DELETE THIS LINE
</intent-filter>
</activity>
2) 在启用另一个图标之前,应使用以下代码禁用所有图标,否则它将添加一个新图标,而不是替换它。
getPackageManager().setComponentEnabledSetting(
getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
但是,如果您使用上面的代码,那么主屏幕上的快捷方式将被删除!它不会自动添加回来。您也许可以以编程方式将图标添加回来,但它可能不会像以前那样保持在相同的位置。
3) 请注意,图标不会立即更改,可能需要几秒钟。如果更改后直接点击,可能会报错:“App is not installed”。
因此,恕我直言,此解决方案仅适用于更改应用启动器中的图标,不适用于快捷方式(即主屏幕上的图标)
【讨论】:
运行应用程序时出错。未找到默认活动。 你删除了启动器,它会如何找到默认的活动@Deqing?【参考方案7】:试试这个解决方案
<activity android:name=".SplashActivity"
android:label="@string/app_name"
android:icon="@drawable/ic_launcher">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity-alias android:label="ShortCut"
android:icon="@drawable/ic_short_cut"
android:name=".SplashActivityAlias"
android:enabled="false"
android:targetActivity=".SplashActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
当你想改变你的应用图标时添加以下代码
PackageManager pm = getPackageManager();
pm.setComponentEnabledSetting(
new ComponentName(YourActivity.this,
"your_package_name.SplashActivity"),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(
new ComponentName(YourActivity.this,
"your_package_name.SplashActivityAlias"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
【讨论】:
以上代码工作正常。在实时设备连接和调试时,但是当我生成发布 .apk 然后安装它时,应用程序正在关闭而没有任何日志。有人对如何防止此应用被杀有任何想法吗?【参考方案8】:AndroidManifest.xml
示例:
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name="com.pritesh.resourceidentifierexample.MainActivity"
android:label="@string/app_name"
android:launchMode="singleTask">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<!--<category android:name="android.intent.category.LAUNCHER"/>-->
</intent-filter>
</activity>
<activity-alias android:label="RED"
android:icon="@drawable/ic_android_red"
android:name="com.pritesh.resourceidentifierexample.MainActivity-Red"
android:enabled="true"
android:targetActivity="com.pritesh.resourceidentifierexample.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias android:label="GREEN"
android:icon="@drawable/ic_android_green"
android:name="com.pritesh.resourceidentifierexample.MainActivity-Green"
android:enabled="false"
android:targetActivity="com.pritesh.resourceidentifierexample.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias android:label="BLUE"
android:icon="@drawable/ic_android_blue"
android:name="com.pritesh.resourceidentifierexample.MainActivity-Blue"
android:enabled="false"
android:targetActivity="com.pritesh.resourceidentifierexample.MainActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
</application>
然后按照MainActivity
中给定的代码:
ImageView imageView = (ImageView)findViewById(R.id.imageView);
int imageResourceId;
String currentDateTimeString = DateFormat.getDateTimeInstance().format(new Date());
int hours = new Time(System.currentTimeMillis()).getHours();
Log.d("DATE", "onCreate: " + hours);
getPackageManager().setComponentEnabledSetting(
getComponentName(), PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP);
if(hours == 13)
imageResourceId = this.getResources().getIdentifier("ic_android_red", "drawable", this.getPackageName());
getPackageManager().setComponentEnabledSetting(
new ComponentName("com.pritesh.resourceidentifierexample", "com.pritesh.resourceidentifierexample.MainActivity-Red"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
else if(hours == 14)
imageResourceId = this.getResources().getIdentifier("ic_android_green", "drawable", this.getPackageName());
getPackageManager().setComponentEnabledSetting(
new ComponentName("com.pritesh.resourceidentifierexample", "com.pritesh.resourceidentifierexample.MainActivity-Green"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
else
imageResourceId = this.getResources().getIdentifier("ic_android_blue", "drawable", this.getPackageName());
getPackageManager().setComponentEnabledSetting(
new ComponentName("com.pritesh.resourceidentifierexample", "com.pritesh.resourceidentifierexample.MainActivity-Blue"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);
imageView.setImageResource(imageResourceId);
【讨论】:
我收到com.pritesh.resourceidentifierexample.MainActivity-Red doesn't exist in com.pritesh.resourceidentifierexample
异常。在这里,我使用您的清单名称只是为了证明我的问题
@PriteshPatel 上面的代码工作正常。在实时设备连接和调试时,但是当我生成发布 .apk 然后安装它时,应用程序正在关闭而没有任何日志。你知道为什么吗?【参考方案9】:
如前所述,您需要使用<activity-alias>
来更改应用程序图标。
为避免在启用适当的活动别名后终止应用程序,您需要在终止应用程序后执行此操作。要了解应用程序是否被终止,您可以使用this method
-
在 AndroidManifest.xml 中创建活动别名
<activity android:name=".ui.MainActivity"/>
<activity-alias
android:name=".one"
android:icon="@mipmap/ic_launcher_one"
android:targetActivity=".ui.MainActivity"
android:enabled="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<activity-alias
android:name=".two"
android:icon="@mipmap/ic_launcher_two"
android:targetActivity=".ui.MainActivity"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
-
创建一个服务,该服务将在终止应用程序后更改活动活动别名。您需要将新活动活动别名的名称存储在某处(例如 SharedPreferences)
class ChangeAppIconService: Service()
private val aliases = arrayOf(".one", ".two")
override fun onBind(intent: Intent?): IBinder? = null
override fun onTaskRemoved(rootIntent: Intent?)
changeAppIcon()
stopSelf()
fun changeAppIcon()
val sp = getSharedPreferences("appSettings", Context.MODE_PRIVATE)
sp.getString("activeActivityAlias", ".one").let aliasName ->
if (!isAliasEnabled(aliasName))
setAliasEnabled(aliasName)
private fun isAliasEnabled(aliasName: String): Boolean
return packageManager.getComponentEnabledSetting(
ComponentName(
this,
"$BuildConfig.APPLICATION_ID$aliasName"
)
) == PackageManager.COMPONENT_ENABLED_STATE_ENABLED
private fun setAliasEnabled(aliasName: String)
aliases.forEach
val action = if (it == aliasName)
PackageManager.COMPONENT_ENABLED_STATE_ENABLED
else
PackageManager.COMPONENT_ENABLED_STATE_DISABLED
packageManager.setComponentEnabledSetting(
ComponentName(
this,
"$BuildConfig.APPLICATION_ID$aliasName"
),
action,
PackageManager.DONT_KILL_APP
)
-
向 AndroidManifest.xml 添加服务
<service
android:name=".ChangeAppIconService"
android:stopWithTask="false"
/>
-
在
MainActivity.onCreate
中启动ChangeAppIconService
class MainActivity: Activity
...
override fun onCreate(savedInstanceState: Bundle?)
...
startService(Intent(this, ChangeAppIconService::class.java))
...
...
【讨论】:
很好的解决方案,德米特里,谢谢。但是再等一下 - 您应该将此方法添加到服务中: override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int Log.d("ChangeAppIconService", "Service Started") return START_NOT_STICKY This当用户在托盘中滑动应用进程时,将处理这种情况【参考方案10】:应用提到的建议,每当默认图标更改为新图标时,我都会遇到应用程序被杀死的问题。所以已经通过一些调整实现了代码。 第1步)。在文件 AndroidManifest.xml 中,使用 android:enabled="true" 和其他别名使用 android:enabled="false" 创建默认活动。您将不包含但将其附加在 android:enabled="true" 中。
<activity
android:name=".activities.SplashActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:theme="@style/SplashTheme">
</activity>
<!-- <activity-alias used to change app icon dynamically> : default icon, set enabled true -->
<activity-alias
android:label="@string/app_name"
android:icon="@mipmap/ic_launcher"
android:roundIcon="@mipmap/ic_launcher_round"
android:name=".SplashActivityAlias1" <!--put any random name started with dot-->
android:enabled="true"
android:targetActivity=".activities.SplashActivity"> <!--target activity class path will be same for all alias-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
<!-- <activity-alias used to change app icon dynamically> : sale icon, set enabled false initially -->
<activity-alias
android:label="@string/app_name"
android:icon="@drawable/ic_store_marker"
android:roundIcon="@drawable/ic_store_marker"
android:name=".SplashActivityAlias" <!--put any random name started with dot-->
android:enabled="false"
android:targetActivity=".activities.SplashActivity"> <!--target activity class path will be same for all alias-->
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity-alias>
步骤 2)。创建一个方法,用于禁用包含默认图标的第一个活动别名并启用包含需要更改图标的第二个别名。
/**
* method to change the app icon dynamically
*
* @param context
* @param isNewIcon : true if new icon need to be set; false to set default
* icon
*/
public static void changeAppIconDynamically(Context context, boolean isNewIcon)
PackageManager pm = context.getApplicationContext().getPackageManager();
if (isNewIcon)
pm.setComponentEnabledSetting(
new ComponentName(context,
"com.example.dummy.SplashActivityAlias1"), //com.example.dummy will be your package
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(
new ComponentName(context,
"com.example.dummy.SplashActivityAlias"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
else
pm.setComponentEnabledSetting(
new ComponentName(context,
"com.example.dummy.SplashActivityAlias1"),
PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
PackageManager.DONT_KILL_APP);
pm.setComponentEnabledSetting(
new ComponentName(context,
"com.example.dummy.SplashActivityAlias"),
PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
PackageManager.DONT_KILL_APP);
步骤 3)。现在根据您的要求调用此方法,例如在按钮单击或特定日期或特定场合条件下,就像 -
// Switch app icon to new icon
GeneralUtils.changeAppIconDynamically(EditProfileActivity.this, true);
// Switch app icon to default icon
GeneralUtils.changeAppIconDynamically(EditProfileActivity.this, false);
希望这将帮助那些面临应用程序因图标更改而被杀死的问题的人。 快乐编码:)
【讨论】:
以上代码工作正常。在实时设备连接和调试时,但是当我生成发布 .apk 然后安装它时,应用程序正在关闭而没有任何日志。你知道为什么吗?【参考方案11】:为了让 Markus 解决问题,我需要第一个 Intent,所以:
Intent myLauncherIntent = new Intent(Intent.ACTION_MAIN);
myLauncherIntent.setClassName(this, this.getClass().getName());
myLauncherIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
【讨论】:
以上是关于如何在 Android 中以编程方式更改应用程序图标?的主要内容,如果未能解决你的问题,请参考以下文章
在 Android 中以编程方式更改 ImageView 的图像