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


153

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

Answers:


79

这是一个古老的问题,但由于没有明确的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每次更改图标都会得到2 s。只要我的应用程序的其余部分是完美的,这并不是完美的,但是...


9
如今的“日历”应用每天都无需添加面包就可以包装图标。
2014年

1
@ Jim(我认为)这实际上是一个小部件
JacksOnF1re 2014年

3
不幸的是,shortcutDel在棉花糖中不再起作用,另请参见stackoverflow.com/a/33731620/1545993
Taifun

2
这将替换快捷方式图标,而不是启动器图标
user1506104

3
在下面的几行中,什么是Play.class和Constants.ACTION_PLAY。Intent ShortcutIntent = new Intent(getApplicationContext(),Play.class); shortcutIntent.setAction(Constants.ACTION_PLAY);
Dasharath Singh Bajroliya

136

试试这个,对我来说很好:

1。修改中的MainActivity部分AndroidManifest.xml,将其删除,并与MAINintent-filter部分中的类别一致

<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。以编程方式设置:为适当的设置ENABLE属性activity-alias

 getPackageManager().setComponentEnabledSetting(
        new ComponentName("ru.quickmessage.pa", "ru.quickmessage.pa.MainActivity-Red"), 
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP);

注意,必须始终启用至少一个。


3
可悲的是,在我尝试过的设备上,它们的工作方式不同。在HTC Desire 2.2上运行,但在Galaxy Nexus 4.2.2和Nexus 7 4.3上不可靠。在Galaxy Nexus上,可能导致该应用程序的所有图标消失,以及所有小部件都被删除。可惜,所以我不得不在以后的设备上删除此功能。
理查德·勒·梅修里尔

7
我删除了以下代码后无法启动我的应用程序:<action android:name =“ android.intent.action.MAIN” /> <category android:name =“ android.intent.category.LAUNCHER” />
noobProgrammer 2014年

1
对于在应用程序托盘和主屏幕中的启动器图标之间进行切换,这非常适合我,但是我发现在操作系统级别使用的图标(例如用于多任务处理,卸载弹出窗口或在“应用程序管理器”列表中)仍然存在如果您未在清单中的应用程序级别设置一个图标,则为原始图标,或成为默认的通用Android图标。您是否找到了解决方案,还是只是容忍操作系统级别的图标不匹配(或不存在)?
jokeefe 2014年

2
运行应用程序时出错。找不到默认活动。
CopsOnRoad

1
运行应用程序时出错。找不到默认活动
卡米尔·伊巴多夫

36

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


2
@Nanne:这是应用程序小部件或主屏幕功能,而不是应用程序图标。除非通过软件升级,否则您仍然无法更改清单或资源。
CommonsWare

1
?不,我的意思是相反:它不是(宣传为)小部件。我将其添加为应用程序快捷方式。但是,正如您所说,只是因为这些非库存的东西暗示着它只是一个图标,并不意味着它是:)
Nanne

2
@NeTeInStEEN:不适用于所有主屏幕(例如,那些不关注启用组件更改的主屏幕)。
CommonsWare

1
不再是真的。Android 6+上的Google日历每天都会在启动器中进行更改。今天,该图标为“ 2”,昨天为“ 1”。通常,图标上有一个“ 31”。但现在不改变了。有人知道这怎么可能吗?
UeliDeSchwert

1
@Bobby:我的意思是,除了存在的数千种Android设备型号中的数百种不同的预安装主屏幕外,Play商店上还有数百种(甚至数千种)主屏幕实现。欢迎那些主屏幕实现具有允许动态启动器图标替换的挂钩。但是,并非所有主屏幕都必须提供此功能。仅仅因为您在一个设备的一个主屏幕上看到一个应用程序的这种行为,并不意味着它可用于所有设备的所有主屏幕上的所有应用程序。
CommonsWare'1

17

以编程方式,您可能想要自己发布应用程序启动器:

注意:此方法从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);

这将替换快捷方式图标,而不是启动器图标
user1506104


9

@PA的解决方案部分适用于我。在下面详细说明我的发现:

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)请注意,图标不会立即更改,可能需要几秒钟。如果在更改后立即单击它,可能会收到错误消息:“未安装应用程序”。

因此,恕我直言,此解决方案仅适用于仅更改应用启动器中的图标,不适用于快捷方式(即主屏幕上的图标)


2
运行应用程序时出错。找不到默认活动。
CopsOnRoad '17

删除启动器后,它将如何找到默认活动@Deqing?
j2emanue

5

试试这个解决方案

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

4

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例外了。在这里,我用您的清单名称来证明我的问题
Tejas Pandya

0

为了获得Markus的解决方案,我需要第一个Intent,因此:

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

0

应用提到的建议,每当默认图标更改为新图标时,我都面临应用程序被杀死的问题。因此,通过一些调整实现了代码。第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);

希望这将帮助那些面对应用程序问题的人因图标更改而丧命。快乐编码:)

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.