Android 中的动态应用程序图标

Android 中的动态应用程序图标

一、需求

您可能遇到过那些可以实现巧妙技巧的应用程序 - 更改应用程序图标(也许是在您的生日那天),然后无缝切换回常规图标。这种功能会激起你的好奇心,让你想知道,他们到底是怎么做到的?好吧,你并不是唯一一个有好奇心的人。许多开发人员,包括我自己,都思考过这个问题。这似乎是看似不可能的任务之一,但你猜怎么着?它不是!在本文中,我们将揭开运行时更改 Android 应用程序图标背后的谜团。我们将一步步为您分解,并向您展示它不仅可行,而且非常易于管理。

二、解决方案

首先,应用程序图标是从清单文件设置的,就像任何其他应用程序组件一样。Android系统读取manifest文件并相应地设置应用程序图标。目前无法在运行时更改应用程序图标。但有一个解决方法。也就是使用一个activity-alias(如果你对activity-alias不熟悉,可以查看这里的官方文档)。

三、方案实现

xml 复制代码
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application
        ...
        android:icon="YOUR_ICON"
        android:roundIcon="YOUR_ICON">
        <activity
            ...
            android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity-alias
            ...
            android:icon="YOUR_ICON_2"
            android:roundIcon="YOUR_ICON_2"
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>
    </application>
</manifest>

如您所见,我们有两项活动。一个是主要活动,另一个是活动别名。默认情况下禁用活动别名。它的图标与主要活动不同。因此,当安装应用程序时,将设置主要活动的图标。activity-alias当我们启用活动别名时,将设置活动的图标。因此,我们可以通过启用和禁用活动别名来在运行时更改应用程序图标。现在让我们看看如何在运行时启用和禁用活动别名。我们可以通过使用PackageManager类来做到这一点。

kotlin 复制代码
fun Activity.changeIcon() {
        packageManager.setComponentEnabledSetting(
            ComponentName(
                this,
                "$packageName.MainActivityAlias"
            ),
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP
        )

        packageManager.setComponentEnabledSetting(
            ComponentName(
                this,
                "$packageName.MainActivity"
            ),
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP
        )
}

正如您所看到的,我们正在使用PackageManager类的setComponentEnabledSetting方法。我们正在传递活动别名和主要活动的组件名称。我们将活动别名设置为启用,将主要活动设置为禁用。因此,当我们调用此方法时,活动别名将被启用,而主活动将被禁用。所以应用程序图标将会改变。

另外,作为一名软件工程师,我不喜欢这种实现方式。我希望事情干净且灵活。因此,我认为最好将上面的函数更新如下:

kotlin 复制代码
fun Activity.changeEnabledComponent(
        enabled: String,
        disabled: String,
    ) {
        packageManager.setComponentEnabledSetting(
            ComponentName(
                this,
                enabled
            ),
            PackageManager.COMPONENT_ENABLED_STATE_ENABLED,
            PackageManager.DONT_KILL_APP
        )

    packageManager.setComponentEnabledSetting(
            ComponentName(
                this,
                disabled
            ),
            PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
            PackageManager.DONT_KILL_APP
    )
}

因此,更改应用程序图标就像使用组件名称调用函数一样简单。例如:

kotlin 复制代码
changeEnabledComponent(
    enabled = "$packageName.MainActivityAlias",
    disabled = "$packageName.MainActivity"
)

另外,作为一名软件工程师,我们仍然使用硬代码这一事实让我很困扰。我什至希望事情变得更加灵活,更加愿意改变。所以我想对组件名称进行更多抽象。但这是一个挑战,因为我们需要以某种方式获得与清单文件中使用的相同的名称。为了解决这个问题,我们可以使用BuildConfig和manifestPlaceholders. 在应用程序级 build.gradle 文件中,我们可以添加以下代码:(我假设您正在为 build.gradle 文件使用Kotlin DSL 。)

kotlin 复制代码
    private val mainActivity = "YOURPATH.MainActivity"
    private val mainActivityAlias = "YOURPATH.MainActivityAlias"
    android {
        defaultConfig {
            ...
            manifestPlaceholders.apply {
                set("main_activity", mainActivity)
                set("main_activity_alias", mainActivityAlias)
            }
        }

        buildTypes {
            release {
                isMinifyEnabled = false
                buildConfigField("String", "main_activity", "\"${mainActivity}\"")
                buildConfigField("String", "main_activity_alias", "\"${mainActivityAlias}\"")
            }

            debug {
                isDebuggable = true
                isMinifyEnabled = false 
                buildConfigField("String", "main_activity", "\"${mainActivity}\"")
                buildConfigField("String", "main_activity_alias", "\"${mainActivityAlias}\"")
            }
        }
    }

这里我们将组件名称设置为manifestPlaceholdersbuildConfigField。因此我们可以从BuildConfig类访问它们。当然,我们需要更新清单文件,以便它使用占位符。

xml 复制代码
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools">

    <application ... >
        <activity
            android:name="${main_activity}"
            ... >
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <activity-alias
            android:name="${main_activity_alias}"
            ...
            android:targetActivity=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity-alias>
    </application>
</manifest>

您现在可能会看到一些错误,指出main_activity和/或main_activity_alias无法找到。但您可以忽略它们,因为它们将与 build.gradle 文件同步生成。现在我们可以更新代码以使用BuildConfig类。

kotlin 复制代码
changeEnabledComponent(
    enabled = BuildConfig.main_activity_alias,
    disabled = BuildConfig.main_activity
)

现在我们有了一个干净且灵活的代码。我们可以通过使用组件名称调用changeEnabledComponent函数来在运行时更改应用程序图标。我们可以更改 build.gradle 文件中的组件名称。因此我们可以在运行时更改应用程序图标,而无需更改代码。作为更广泛的示例,请查看下面的代码。

kotlin 复制代码
val mainActivity = BuildConfig.main_activity
val mainActivityAlias = BuildConfig.main_activity_alias

class MainActivity : ComponentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        setContent {
            DynamicIconTheme {
                Surface(
                    modifier = Modifier.fillMaxSize(),
                    color = MaterialTheme.colorScheme.background
                ) {
                    Screen(
                        on30Click = {
                            changeEnabledComponent(
                                enabled = mainActivityAlias,
                                disabled = mainActivity
                            )
                        },
                        on60Click = {
                            changeEnabledComponent(
                                enabled = mainActivityAlias,
                                disabled = mainActivity
                            )
                        }
                    )
                }
            }
        }
    }
}

四、结论

从本质上讲,我们已经打破了"在运行时动态更改 Android 应用程序图标是一项无法实现的壮举"的神话。通过利用应用程序清单中的功能activity-alias并熟练地使用 PackageManager 类,我们已经揭示了实现这一目标的路径。然而,真正的游戏规则改变者在于我们对更清晰、适应性更强的代码的追求,我们利用占位符和 BuildConfig 来实现最终的灵活性。现在,您可以让用户将自己独特的风格注入到您的应用程序图标中,而不会迷失在代码杂草中。@[toc](Android 中的动态应用程序图标)

相关推荐
花开月满西楼42 分钟前
保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
android·前端·android studio
这儿有一堆花1 小时前
打造你的 Android 图像编辑器:深入解析 PhotoEditor 开源库
android·开源
皮皮高2 小时前
itvbox绿豆影视tvbox手机版影视APP源码分享搭建教程
android·前端·后端·开源·tv
EnzoRay2 小时前
MotionEvent
android
玲小珑3 小时前
Auto.js 入门指南(七)定时任务调度
android·前端
墨狂之逸才3 小时前
adb常用命令调试
android
YoungForYou3 小时前
Android端部署NCNN
android
移动开发者1号3 小时前
Jetpack Compose瀑布流实现方案
android·kotlin
移动开发者1号4 小时前
Android LinearLayout、FrameLayout、RelativeLayout、ConstraintLayout大混战
android·kotlin
移动开发者1号4 小时前
ListView与RecyclerView区别总结
android·kotlin