是否可以直接从程序更改应用程序图标?
我的意思是,在 res\drawable
文件夹中更改 icon.png
。
我希望让用户能够从程序中更改应用程序的图标,这样下次他们在启动器中看到的将是之前选择的图标。
是否可以直接从程序更改应用程序图标?
我的意思是,在 res\drawable
文件夹中更改 icon.png
。
我希望让用户能够从程序中更改应用程序的图标,这样下次他们在启动器中看到的将是之前选择的图标。
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);
注意,至少必须始终启用一个。
这是一个老问题,因为没有明确的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
。这并不完美,但只要我的应用程序的其余部分是完美的...
除非通过软件升级,否则您无法更改已签名和封装的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);
@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)请注意,图标不会立即更改,可能需要几秒钟时间。如果您在更改后立即单击它,可能会收到一个错误,显示“应用程序未安装”。
因此,在我看来,这个解决方案只适用于更改应用程序启动器中的图标,而不适用于快捷方式(即主屏幕上的图标)。
如前所述,您需要使用<activity-alias>
来更改应用程序图标。
为了避免在启用适当的activity-alias后终止应用程序,您需要在应用程序被终止后执行此操作。要找出应用程序是否已被终止,您可以使用此方法
<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>
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
)
}
}
}
<service
android:name=".ChangeAppIconService"
android:stopWithTask="false"
/>
MainActivity.onCreate
中启动ChangeAppIconService
class MainActivity: Activity {
...
override fun onCreate(savedInstanceState: Bundle?) {
...
startService(Intent(this, ChangeAppIconService::class.java))
...
}
...
}
onTaskRemoved()
:( - Jemshit Iskenderov尝试这个解决方案
<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);
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);
应用上述建议后,我遇到了一个问题,当默认图标更改为新图标时,应用程序会崩溃。因此,我进行了一些调整并实现了代码。
第一步)在文件AndroidManifest.xml中,创建具有android:enabled="true"和其他alias的默认活动的<activity>标记,并使用android:enabled="false"为它们创建<activity-alias>标记。您的<application>标记将不包含任何<activity>标记,但是请将这些标记附加到具有android:enabled="true"的<activity-alias>标记中。
<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>
/**
* 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);
}
}
步骤三)。根据您的需求调用此方法,例如在按钮单击、日期特定或场合特定条件下,只需像这样简单地调用:
// Switch app icon to new icon
GeneralUtils.changeAppIconDynamically(EditProfileActivity.this, true);
// Switch app icon to default icon
GeneralUtils.changeAppIconDynamically(EditProfileActivity.this, false);