在Android中实现动态应用图标

在Android中实现动态应用图标

你可能已经遇到过那些能够完成一个神奇的技巧的应用程序——在你的生日时改变他们的应用图标,然后无缝切换回常规图标。这是一种引发你好奇心的功能,让你想知道,“他们到底是如何做到的?”。嗯,你对这个好奇并不孤单。很多开发者,包括我自己,也曾思考过这个问题。它似乎是一项看似不可能实现的任务,但猜猜怎么着?它并不是!在本文中,我们将解开在运行时更改Android应用图标的奥秘。我们将逐步介绍并展示给你,这不仅是可行的,而且还相当容易管理。

首先,应用图标是从类似于其他应用组件的清单文件中设置的。Android系统读取清单文件并相应地设置应用图标。目前没有办法在运行时更改应用图标。但有个变通方法。那就是使用activity-alias(如果你对activity-alias不熟悉,可以在官方文档中查看)。

https://developer.android.com/guide/topics/manifest/activity-alias-element

<?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>

如你所见,我们有两个activities。一个是主要activity,另一个是activity alias。activity alias默认处于禁用状态,并且具有与主要活动不同的图标。因此,当应用程序安装完成后,将设置主要活动的图标。而当我们启用活动别名时,将设置活动别名的图标。因此,我们可以通过启用和禁用活动别名来在运行时更改应用程序图标。现在,让我们看看如何在运行时启用和禁用活动别名。我们可以使用PackageManager类来实现这一点。

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方法。我们正在传递活动别名和主要活动的组件名称,并将活动别名设置为启用状态,将主活动设置为禁用状态。因此,当我们调用此方法时,活动别名将启用,主要活动将禁用。因此,应用程序图标将被更改。

作为一名软件工程师,我不喜欢这种实现方式。我更希望事情变得简洁和灵活。因此,我认为最好将上述函数更新如下:

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

因此,更改应用程序图标将仅需要使用组件名称调用该函数即可。例如:

changeEnabledComponent(enabled = "$packageName.MainActivityAlias",disabled = "$packageName.MainActivity"
)

作为一名软件工程师,我们仍在使用硬编码的事实让我很不满意。我甚至希望事情变得更灵活,更容易改变。所以我想更抽象一些组件名称。但是这里有一个挑战,因为我们必须以某种方式获取与清单文件中使用的相同名称。为了解决这个问题,我们可以使用BuildConfigmanifestPlaceholders,而不是使用硬编码的字符串。在应用程序级别的build.gradle文件中,我们可以添加以下代码:

  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 = falsebuildConfigField("String", "main_activity", "\"${mainActivity}\"")buildConfigField("String", "main_activity_alias", "\"${mainActivityAlias}\"")}debug {isDebuggable = trueisMinifyEnabled = false buildConfigField("String", "main_activity", "\"${mainActivity}\"")buildConfigField("String", "main_activity_alias", "\"${mainActivityAlias}\"")}}}

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

<manifest xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"><application...><activityandroid:name="${main_activity}"...><intent-filter><action android:name="android.intent.action.MAIN" /><category android:name="android.intent.category.LAUNCHER" /></intent-filter></activity><activity-aliasandroid: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_activitymain_activity_alias。但是您可以忽略它们,因为它们将与build.gradle文件同步生成。现在,我们可以更新我们的代码以使用BuildConfig类。

changeEnabledComponent(enabled = BuildConfig.main_activity_alias,disabled = BuildConfig.main_activity
)

现在我们有了一个干净而灵活的代码。我们可以通过调用changeEnabledComponent函数并传递组件名称来在运行时更改应用程序图标。我们还可以在build.gradle文件中更改组件名称。因此,我们可以在运行时更改应用程序图标而无需更改代码。如果需要更详细的示例,请查看下面的代码片段。

val mainActivity = BuildConfig.main_activity
val mainActivityAlias = BuildConfig.main_activity_aliasclass 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实现了极高的灵活性。现在,您可以让用户为您的应用程序图标注入自己独特的风格,而无需迷失在代码的泥沼中。

Github

https://github.com/oguzhanaslann/DynamicIcon

参考

https://www.geeksforgeeks.org/how-to-change-app-icon-of-android-programmatically-in-android/
https://developer.android.com/guide/topics/manifest/activity-alias-element

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/98225.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Unity实现设计模式——模板方法模式

Unity实现设计模式——模板方法模式 模板模式(Template Pattern)&#xff0c; 指在一个抽象类公开定义了执行它的方法的模板。它的子类可以按需要重写方法实现&#xff0c;但调用将以抽象类中定义的方式进行。 简单说&#xff0c; 模板方法模式定义一个操作中的算法的骨架&…

【1++的Linux】之进程(五)

&#x1f44d;作者主页&#xff1a;进击的1 &#x1f929; 专栏链接&#xff1a;【1的Linux】 文章目录 一&#xff0c;什么是进程替换二&#xff0c;替换函数三&#xff0c;实现我们自己的shell 一&#xff0c;什么是进程替换 我们创建出来进程是要其做事情的&#xff0c;它可…

Leetcode 2895. Minimum Processing Time

Leetcode 2895. Minimum Processing Time 1. 解题思路2. 代码实现 题目链接&#xff1a;2895. Minimum Processing Time 1. 解题思路 这一题整体上来说其实没啥难度&#xff0c;就是一个greedy算法&#xff0c;只需要想明白耗时长的任务一定要优先执行&#xff0c;不存在某个…

Tomcat 介绍与 jspgou 部署

一、虚拟机简介 1、Java 虚拟机 (1) 虚拟机&#xff1a; 虚拟机&#xff08;Virtual Machine&#xff09;是一种软件或硬件实体&#xff0c;它模拟了一个独立的计算环境&#xff0c;可以在其上运行应用程序。 虚拟机可分为系统虚拟机和程序虚拟机&#xff1a; ● 系统虚拟…

自然语言处理的分类

动动发财的小手&#xff0c;点个赞吧&#xff01; 简介 作为理解、生成和处理自然语言文本的有效方法&#xff0c;自然语言处理&#xff08;NLP&#xff09;的研究近年来呈现出快速传播和广泛采用。鉴于 NLP 的快速发展&#xff0c;获得该领域的概述并对其进行维护是很困难的。…

leetcode - 229. Majority Element II

Description Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. Example 1: Input: nums [3,2,3] Output: [3]Example 2: Input: nums [1] Output: [1]Example 3: Input: nums [1,2] Output: [1,2]Constraints: 1 < nu…

Nginx搭建Rtmp流媒体服务,并使用Ffmpeg推流

文章目录 1.rtmp流媒体服务框架图2.nginx配置3.配置nginx4.使用ffmpeg推流5.实时推摄像头流 本项目在开发板上使用nginx搭建流媒体服务&#xff0c;利用ffmpeg进行推流&#xff0c;在pc上使用vlc media进行拉流播放。 1.rtmp流媒体服务框架图 2.nginx配置 下载&#xff1a;wge…

【GIT版本控制】--远程仓库

一、连接远程仓库 连接到远程仓库是在GIT中进行协作和备份的关键步骤。以下是连接到远程仓库的基本步骤&#xff1a; 获取远程仓库的URL&#xff1a;首先&#xff0c;你需要获得远程仓库的URL。通常&#xff0c;这是远程仓库提供给你的&#xff0c;可以是HTTPS或SSH URL。例如…

AI:10-基于TensorFlow的玉米病害识别

玉米是世界上最重要的粮食作物之一,然而,玉米病害对其产量和质量造成了严重威胁。传统的病害识别方法通常依赖于人工观察和经验判断,效率低下且易受主观因素影响。近年来,基于深度学习的图像识别技术在农业领域取得了显著进展,为玉米病害的快速、准确识别提供了新的解决方…

Android---Class 对象在执行引擎中的初始化过程

一个 class 文件被加载到内存中的步骤如下图所示&#xff1a; 装载 装载是指 Java 虚拟机查找 .class 文件并生成字节流&#xff0c;然后根据字节流创建 java.lang.Class 对象的过程。 1. ClassLoader 通过一个类的全限定名&#xff08;包名类名&#xff09;来查找 .class 文件…

手写能任务窃取的线程池

目录 function_wrapper.hpp: stealing_queue.hpp thread_pool_steal_hpp 参考&#xff1a;《C并发编程实战》 对于thread_pool_steal.hpp的代码有改动&#xff0c;不然运行不了 function_wrapper.hpp: //包装可调用对象&#xff0c;对外消除对象型别&#xff0c;还需要有一…

线性表相关知识

1.简述 线性表&#xff0c;全名为线性存储结构。使用线性表存储数据的方式可以这样理解&#xff0c;即“把所有数据按照顺序&#xff08;线性&#xff09;的存储结构方式&#xff0c;存储在物理空间”。 按照空间分类&#xff1a; 顺序存储结构&#xff1a;数据依次存储在连续…

【Zookeeper专题】Zookeeper经典应用场景实战(一)

目录 前置知识课程内容一、Zookeeper Java客户端实战1.1 Zookeeper 原生Java客户端使用1.2 Curator开源客户端使用快速开始使用示例 二、Zookeeper在分布式命名服务中的实战2.1 分布式API目录2.2 分布式节点的命名2.3 分布式的ID生成器 三、zookeeper实现分布式队列3.1 设计思路…

screen命令

screen命令 安装screen状态介绍帮助查询查看已经存在的screen终端创建一个叫liu的虚拟终端回到主终端回到虚拟终端清除终端 安装screen # CentOS yum install screen # Debian/Ubuntu apt install screen状态介绍 Attached&#xff1a;表示当前screen正在作为主终端使用&…

完美解决 flex 实现一行三个,显示多行,左对齐

效果图 代码 <body><section class"content"><div class"item">元素</div><div class"item">元素</div><div class"item">元素</div><div class"item">元素</di…

代理IP与Socks5代理的技术奇妙之旅

随着数字化时代的崛起&#xff0c;网络工程师们日益承担着维护网络稳定性和保护数据安全的重任。在这个充满挑战的世界里&#xff0c;代理IP与Socks5代理技术成为了他们的秘密武器&#xff0c;本文将带您踏上一段技术奇妙之旅&#xff0c;深入了解这两项技术在不同领域中的应用…

【MySQL】Linux 中 MySQL 环境的安装与卸载

文章目录 Linux 中 MySQL 环境的卸载Linux 中 MySQL 环境的安装 Linux 中 MySQL 环境的卸载 在安装 MySQL 前&#xff0c;我们需要先将系统中以前的环境给卸载掉。 1、查看以前系统中安装的 MySQL rpm -qa | grep mysql2、卸载这些 MySQL rpm -qa | grep mysql | args yum …

关于Jupyter markdown的使用

一级标题 #空格 标题1 二级标题 ## 空格 标题2 三级标题 ###空格 标题3 无序&#xff1b; 有序&#xff1a; 数学符号&#xff1a;

链表的基本操作(数据结构)

单链表 #include <stdlib.h> #include <iostream> #include <stdio.h> typedef struct LNode{int data;struct LNode *next; }LNode,*LinkList;打印链表 void PrintList(LNode *p) {LNode *temp;temp p->next;printf("链表的顺序:");while(t…

Rust Http 性能测试框架/工具

在Rust中&#xff0c;有几个常用的性能测试框架和工具可用于对HTTP性能进行测试。以下是其中一些&#xff1a; 1、Criterion&#xff1a;Criterion是一个通用的性能测试框架&#xff0c;可以用于测试各种类型的代码性能&#xff0c;包括HTTP性能。你可以使用Criterion来编写和运…