Android 中的动态应用程序图标

news2024/11/17 15:39:51

Android 中的动态应用程序图标

  • 一、需求
  • 二、解决方案
  • 三、方案实现
  • 四、结论

一、需求

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

二、解决方案

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

三、方案实现

<?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类来做到这一点。

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

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

    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类访问它们。当然,我们需要更新清单文件,以便它使用占位符。

<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类。

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

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

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 中的动态应用程序图标)

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

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

相关文章

websocket 通信协议

websocket是什么 答: 它是一种网络通信协议&#xff0c;是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。 意思就是服务器可以主动向客户端推送信息&#xff0c;客户端也可以主动向服务器发送信息 属于服务器推送技术的一种. 为什么需要websocket? 疑问?…

Java 面试题之 IO(一)

字节流 文章目录 字节流InputStream&#xff08;字节输入流&#xff09;OutputStream&#xff08;字节输出流&#xff09; 文章来自Java Guide 用于学习如有侵权&#xff0c;立即删除 InputStream&#xff08;字节输入流&#xff09; InputStream用于从源头&#xff08;通常是…

【command】使用nr简化npm run命令

参考文章 添加 alias nrnpm run通过alias启动命令可以帮助我们节省运行项目输入命令的时间 $ cd ~ $ vim .bash_profile $ source ~/.bashrc

【51单片机Keil+Proteus8.9】门锁控制电路

门锁控制电路 二、设计思路 电路设计 1.电源部分&#xff1a;使用BATTERY为整个电路提供电源&#xff0c;可以在电路中加入一个电 源开关&#xff0c;以便控制电源的开启和关闭。 2.处理器部分&#xff1a;使用AT89C51芯片作为主处理器&#xff0c;通过编写程序实现门锁的 …

【Java IO 源码详解】: InputStream

本文主要从JDK 11 源码角度分析InputStream。 Java IO - 源码: InputStream InputStream 类实现关系InputStream 抽象类源码实现InputStreamFilterInputStreamByteArrayInputStreamBufferedInputStream 参考文章 InputStream 类实现关系 InputStream是输入字节流&#xff0c;具…

来聊聊大厂面试题:求Java对象的大小

写在文章开头 日常使用Java进行业务开发时&#xff0c;我们基本不关心一个Java对象的大小&#xff0c;所以经常因为错误的估算导致大量的内存空间在无形之间被浪费了&#xff0c;所以今天笔者就基于这篇文章来聊聊一个Java对象的大小。 你好&#xff0c;我叫sharkchili&#x…

网络体系结构 和网络原理之UDP和TCP

目录 网络分层 一. 应用层 http协议 二. 传输层 1. 介绍 2.UDP协议 (1)组成 (2)细节 3.TCP协议 (1)特性如下链接&#xff1a; (2)组成 (3)特点 三. 网络层 四. 数据链路层 1.介绍 2.以太网协议 3.mac地址和ip地址 五. 物理层 DNS 网络分层 一. 应用层 应用程序 现成的…

【深度优先搜索】【组合数学】【动态规划】1467.两个盒子中球的颜色数相同的概率

作者推荐 【动态规划】【字符串】【行程码】1531. 压缩字符串 本文涉及知识点 动态规划汇总 深度优先搜索 组合数学 LeetCode1467 两个盒子中球的颜色数相同的概率 桌面上有 2n 个颜色不完全相同的球&#xff0c;球上的颜色共有 k 种。给你一个大小为 k 的整数数组 balls …

数据写入HBase(scala)

package sourceimport org.apache.hadoop.hbase.{HBaseConfiguration, TableName} import org.apache.hadoop.hbase.client.{ConnectionFactory, Put} import org.apache.hadoop.hbase.util.Bytesobject ffff {def main(args: Array[String]): Unit {//hbase连接配置val conf …

c++连接mysql

c连接mysql 安装mysql以及c对应的库进入数据库&#xff0c;创建数据库&#xff0c;表&#xff0c;并新建管理员用户编写c代码编译运行&#xff0c;测试结果头文件解释 安装mysql以及c对应的库 sudo apt-get update sudo apt-get install mysql-server sudo apt-get install li…

2023年算法CDO-CNN-BiLSTM-ATTENTION回归预测(matlab)

2023年算法CDO-CNN-BiLSTM-ATTENTION回归预测&#xff08;matlab&#xff09; CDO-CNN-BiLSTM-Attention切诺贝利灾难优化器优化卷积-长短期记忆神经网络结合注意力机制的数据回归预测 Matlab语言。 切诺贝利灾难优化器Chernobyl Disaster Optimizer (CDO)是H. Shehadeh于202…

新书推荐——《趣读数字经济》

文章目录 缘起:“躺嬴”的一天/ 001 第1章 名花解语,石心铁肠&#xff0c;当属“人工智能”/ 009 1.1 自学成才的人工智能/ 011 1.2 狂飙的话病ChatGPT / 017 1.3 算力、算法与数据:人工智能的核心/ 026 1.4 人工智能会抢走我们的饭碗吗/032 1.5 人工智能有多能/ 036 1.6 AI…

地址解析工具---AddressParseUtil

一、工具源码 package com.rural_vibration.common.utils;import java.util.Iterator; import java.util.LinkedHashMap; import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern;/*** description: 地址解析工具 <…

FTP服务之WindowsServer2019中搭建私有FTP服务器

WindowsServer2019搭建FTP服务器 文章目录 WindowsServer2019搭建FTP服务器1. 查看FTP服务是否开启2. 配置FTP服务站点3. 访问 1. 查看FTP服务是否开启 WindowsServer2019默认是开启FTP服务的&#xff0c;如果未开启&#xff0c;则按下面步骤开启即可 打开服务器管理 添加角色和…

java OA办公自动化系统

java OA办公自动化系统&#xff0c;java项目&#xff0c;springboot项目。eclipse和idea都能打开运行。 前端技术&#xff1a;Bootstrap&#xff0c;Jquery&#xff0c;My97 DatePicker&#xff0c;kindeditor&#xff0c;freemarker 后端技术&#xff1a;SpringBoot&#xf…

KVM 内存概述

KVM 内存概述 CPU缓存基本概念内存基本概念EPT和VPID内存过载使用大页透明大页透明大页使用 KSM NUMA CPU缓存基本概念 CPU工作过程中会直接读取内存的数据&#xff0c;而大部分同学对内存的感觉是内存条的一个概念&#xff0c;其实CPU中也有内存的概念&#xff0c;称之为L1-L…

langchain + hugginface入门体验

简介 本文记录一次使用langchain调用openai并部署在huggingface上的经历 安装环境依赖 我的python版本是3.9 pip install langchain pip install openai代码 app.py import streamlit as st # from langchain_community.chat_models import ChatOpenAI from langchain_openai …

qt5-入门

参考&#xff1a; qt学习指南 Qt5和Qt6的区别-CSDN博客 Qt 学习之路_w3cschool Qt教程&#xff0c;Qt5编程入门教程&#xff08;非常详细&#xff09; 本地环境&#xff1a; win10专业版&#xff0c;64位 技术选择 Qt5力推QML界面编程。QML类似HTML&#xff0c;可以借助CSS进…

【大数据】Flink SQL 语法篇(二):WITH、SELECT WHERE、SELECT DISTINCT

Flink SQL 语法篇&#xff08;二&#xff09; 1.WITH 子句2.SELECT & WHERE 子句3.SELECT DISTINCT 子句 1.WITH 子句 应用场景&#xff08;支持 Batch / Streaming&#xff09;&#xff1a;With 语句和离线 Hive SQL With 语句一样的&#xff0c;语法糖 1&#xff0c;使用…

15.Golang中的反射机制及应用

目录 概述实践基本应用复杂应用 结束 概述 Golang中的反射用法还是比较简单的 reflect.TypeOf(arg)reflect.ValueOf(arg) 实践 基本应用 package mainimport ("fmt""reflect" )func reflectNum(arg interface{}) {fmt.Println("type ", re…