Як змінити піктограму програми програмно в Android?


154

Чи можливо змінити значок програми безпосередньо з програми?
Я маю на увазі зміни icon.pngв res\drawableпапці.
Я хотів би дозволити користувачам змінювати піктограму програми з програми, щоб наступного разу вони побачили вибраний раніше значок у панелі запуску.

Відповіді:


81

Це старе питання, але все ще активне, оскільки немає чіткої функції Android. А хлопці з фейсбуку знайшли роботу - якось. Сьогодні я знайшов спосіб, який працює для мене. Не ідеально (див. Зауваження в кінці цієї відповіді), але це працює!

Основна ідея полягає в тому, щоб я оновлював ярлик ярлика свого додатка, створений пусковим пристроєм на моєму головному екрані. Коли я хочу щось змінити на піктограмі ярлика, спочатку я його видаляю і відтворюю за допомогою нової растрової карти.

Ось код. У ньому є кнопка 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секунди щоразу, коли змінюю іконку. Це не ідеально, але добре, якщо решта мого додатка ідеальна ...


9
Сьогоднішня програма календаря щодня підбирає піктограми без тостів.
Джим Маккіт

1
@ Jim (я думаю) це насправді віджет тоді
JacksOnF1re

3
shortcutDel, на жаль, більше не працює на Marshmallow, дивіться також stackoverflow.com/a/33731620/1545993
Taifun

2
це замінює піктограму ярлика, а не піктограму запуску
user1506104

3
У рядках нижче, що таке Play.class та Constants.ACTION_PLAY Intent shortcutIntent = новий намір (getApplicationContext (), Play.class); shortcutIntent.setAction (Constants.ACTION_PLAY);
Дашарат Сінгх Байролія

136

Спробуйте це, для мене це добре працює:

1. Змініть свій MainActivityрозділ AndroidManifest.xml, видаліть з нього рядок із MAINкатегорією в intent-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" /> <категорія android: name = "android.intent.category.LAUNCHER" />
noobProgrammer

1
Для мене це ідеально підходить для перемикання між піктограмами запуску в лотку додатків та головному екрані, але я виявив, що піктограма, що використовується на рівні ОС (наприклад, для багатозадачних завдань, видалення спливаючих вікон або в списку диспетчерів додатків), залишається оригінал або стає загальним значком Android за замовчуванням, якщо ви його не встановили на рівні програми в маніфесті. Ви знайшли рішення для цього чи просто ви терпите невідповідність (або відсутність) піктограми на рівні ОС?
jokeefe

2
Помилка запуску програми. Діяльність за замовчуванням не знайдена
CopsOnRoad

1
Помилка запуску програми. Діяльність за замовчуванням не знайдена
Каміль Ібадов

36

Ви не можете змінити маніфест або ресурс у підписаному та запечатаному APK, за винятком оновлення програмного забезпечення.


2
@Nanne: це віджет додатка або головний екран, а не значок програми. Ви все ще не можете змінити маніфест чи ресурс, окрім оновлення програмного забезпечення.
CommonsWare

1
? Ні, я маю на увазі навпаки: його не (рекламується як) віджет. Я додаю це як ярлик програми. Але, як ви кажете, тільки тому, що цей нежиткий матеріал має на увазі його просто ікону, це не означає, що це :)
Нанна

2
@NeTeInStEiN: Він не працюватиме на всіх домашніх екранах (наприклад, на тих, хто не звертає уваги на зміни компонентів).
CommonsWare

1
Неправда більше. Календар Google на Android 6+ щодня змінюється у запуску. Сьогодні ікона - це "2", вчора вона була "1". Зазвичай на іконі було "31". Але вже не це змінюється. Хтось знає, як це можливо?
UeliDeSchwert

1
@Bobby: Я маю на увазі, що в Play Store є сотні, якщо не тисячі, реалізацій домашнього екрану, а також сотні різних попередньо встановлених домашніх екранів у тисячах моделей пристроїв Android. Ці реалізації домашнього екрану можуть мати гачки, що дозволяють проводити динамічну заміну піктограм запуску. Однак, не всі домашні екрани повинні пропонувати це. Тільки тому, що ви бачите цю поведінку для одного додатка на одному головному екрані на одному пристрої, не означає, що він доступний для всіх додатків на всіх домашніх екранах на всіх пристроях.
CommonsWare

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

10

Якщо припустити, що ви маєте на увазі зміну піктограми, показаної на головному екрані, це можна легко зробити, створивши віджет, який робить саме це. Ось стаття, яка демонструє, як це можна досягти для програми типу "нові повідомлення", подібної до iPhone:

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


9

@ Рішення ПА частково працює для мене. Детальніше про мої висновки:

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) Зауважте, що значок не буде змінено відразу, це може зайняти кілька секунд. Якщо натиснути його відразу після зміни, можливо, з’явиться помилка із записом: "Додаток не встановлено".

Отже, IMHO це рішення підходить лише для зміни піктограми лише у запуску програм, а не для ярликів (тобто піктограми на головному екрані)


2
Помилка запуску програми. Діяльність за замовчуванням не знайдена
CopsOnRoad

ви видалите запуск, як він знайде активність за замовчуванням @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

Для того, щоб Маркус працював над рішенням, мені знадобився перший Намір, так що:

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: 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). Створіть метод, який буде використовуватися для відключення псевдоніму 1-ї діяльності, який містить піктограму за замовчуванням та включення 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);

Сподіваємось, це допоможе тим, хто стикається з проблемою, коли програму вбивають при зміні піктограм. Щасливе кодування :)

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.