如何在 Android 中以编程方式更改应用程序图标?

是否可以直接从程序中更改应用程序图标?
我的意思是,改变 res\drawable文件夹中的 icon.png
我想让用户从程序中更改应用程序的图标,这样下次他们将在启动程序中看到以前选择的图标。

204594 次浏览

假设您的意思是更改主屏幕上显示的图标,那么可以通过创建一个具有这种功能的小部件轻松实现。下面这篇文章展示了类似于 iPhone 的“新消息”类型应用程序如何实现这一点:

Http://www.cnet.com/8301-19736_1-10278814-251.html

除非通过软件升级,否则不能更改已签名并密封的 APK 中的清单或资源。

通过编程,您可能希望自己发布应用程序启动程序:

注意: 这个方法不再适用于 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);

试试这个,对我很有效:

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);

注意,任何时候都必须至少启用一个。

为了通过马库斯的工作得到解决方案,我需要第一个意图,所以是:

Intent myLauncherIntent = new Intent(Intent.ACTION_MAIN);
myLauncherIntent.setClassName(this,  this.getClass().getName());
myLauncherIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

这是一个老问题,但仍然活跃,因为没有明确的 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。所以每次我改变图标,我都会得到两个 Toast。这并不完美,但只要我的应用程序的其他部分是完美的。

@ P-A 的解决方案部分对我有效,我的发现详情如下:

1)第一个代码片段不正确,见下文:

<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)注意图标不会立即改变,可能需要几秒钟的时间。如果您在更改之后立即单击它,您可能会得到一个错误消息: “应用程序没有安装”。

因此,恕我直言,这个解决方案只适用于在应用程序启动程序中更改图标,而不适用于快捷方式(即主屏幕上的图标)

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);

试试这个方法

<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);

应用提到的建议,我已经面临的问题,应用程序被杀死每当默认图标被更改为新的图标。因此,已经通过一些调整实现了代码。 步骤1)。在 AndroidManifest.xml 文件中,使用 android 创建默认活动: enable = “ true”; 使用 android 创建其他别名: enable = “ false”。在 android 中不会包含但是会附加这些内容: enable = “ 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);

希望这将有助于那些谁面临的问题,应用程序被杀死的图标变化。 快乐编码:)

如前所述,您需要使用 <activity-alias>来更改应用程序图标。 为了避免在启用适当的活动别名之后终止应用程序,您需要在终止应用程序之后执行此操作。要查明应用程序是否被关闭,可以使用 < a href = “ https://stackoverflow. com/a/26882533/8816298”> 这个方法

  1. 在 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>
  1. 重新创建一个服务,该服务将在终止应用程序后更改活动活动-别名。您需要将新活动活动别名的名称存储在某个地方(例如,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
)
}
}
}
  1. 向 AndroidManifest.xml 添加服务
<service
android:name=".ChangeAppIconService"
android:stopWithTask="false"
/>
  1. MainActivity.onCreate中启动 ChangeAppIconService
class MainActivity: Activity {


...


override fun onCreate(savedInstanceState: Bundle?) {
...


startService(Intent(this, ChangeAppIconService::class.java))


...
}


...


}