Android:主题切换
一.概述
正在开发的应用做了一版新UI,原打算将新版UI按项目名做成资源包,再在build.gradle里productFlavors{ }多渠道打包实现
但被告知新旧两个项目共用一个分支,那就做成两个主题(Theme1/Theme2)来适配了
如果只是变更UI,做成多主题来适配不同项目也是比较合适的方式
一.Android App实现多主题的主要有如下几种:
- 从服务器下载或提前放置不同主题资源包到设备指定目录,在代码里引用主题资源
- 定义不同的<style>、使用Android的setTheme()机制设置不同主题
- 将主题资源做成APK,使用远程Context的方式访问Apk中的主题资源。
第1、第3种方法的实现原理,是用户从服务器下载 或 提前放置主题资源,然后在代码中进行引用
第2种方法是接下来先要讲的, 也是这次新旧UI开发适配完成的方式。
第2种讲完后,也会同步介绍第1、第3种方法
除了这三种常用的方式外,还有一种通过"修改framework中Resources类获取资源的流程,将资源重定向到主题包中"的实现方式,这种方式暂不做讨论
二.setTheme()实现主题切换
2.1 定义属性(attr)
App的UI由各种布局文件实现,布局文件中要定义Android各类UI属性(drawable、colors、mipmap、dimension、string......)引用UI资源。
先将Theme1、Theme2各布局文件中都有定义但需要引用不同值的UI属性挑出来,按照不同类型在/values/attrs.xml中进行定义:
<?xml version="1.0" encoding="utf-8"?> <resources> <attr name="ColorAttrName" format="color" /> <attr name="integerAttrName" format="integer" /> <attr name="booleanAttrName" format="boolean" /> <attr name="dimensionAttrName" format="dimension" /> <item name="floatAttrName" format="dimension" /> <attr name="stringAttrName" format="string" /> <attr name="referenceAttrName" format="reference" /> </resources>
reference主要对应drawable、mipmap资源,其他的看字面意思
2.2 定义主题(Theme1/Theme2)
/values/style.xml中定义Theme1、Theme2
<style name="ThemeBlack" parent="@android:style/Theme.Black"> <item name="colorAttrName">#FF00FF00</item> <item name="integerAttrName">33</item> <item name="booleanAttrName">true</item> <item name="dimensionAttrName">76dp</item><item name="floatAttrName">0.35</item> <item name="stringAttrName">@string/hello_world</item> <item name="referenceAttrName">@drawable/hand</item> </style><style name="ThemeLight" parent="@android:style/Theme.Light"> <item name="colorAttrName">#FFFFFF00</item> <item name="integerValue">55</item> <item name="booleanAttrName">false</item> <item name="dimensionValue">76px</item> <item name="floatAttrName">0.15</item> <item name="stringAttrName">@string/action_settings</item> <item name="referenceAttrName">@drawable/ic_launcher</item> </style>
各属性的具体值可以在Theme定义时配置,但是最好定义在各自的xml配置文件中(color.xml、string.xml、dimens.xml.....),然后在Theme定义时"@类型/..."格式引用
Theme中如果string类型不是引用而是字符串,在布局文件中使用正常,但代码里获取会有问题
2.3 布局文件中引用属性
使用过程也很简单,原本布局文件中引用UI属性的方式如下:
android:color="@color/colorAttrName" android:duration="@integer/integerValue" android:adjustViewBounds="@bool/booleanAttrName" android:layout_height="@dimen/dimensionValue" android:text="@string/stringAttrName" android:background="@drawable/referenceAttrName"
现在改为这样进行引用:
android:color="?attr/colorAttrName" android:duration="?attr/integerValue" android:adjustViewBounds="?attr/booleanAttrName" android:layout_height="?attr/dimensionValue" android:text="?attr/stringAttrName" android:background="?attr/referenceAttrName"
float比较特殊,一般它会在代码中被引用(后面会讲到),配置文件中多在dimens.xml中定义变量
2.4 代码中切换主题
要配置的都配置好了,需要注意的是:
- 最好写一个BaseActivity,其他的Activitiy都继承至它
- 主题设置需要在setContentView()之前
接下来就在代码中进行主题切换:
package com.android.example.ui;import android.os.Bundle;import com.android.example.R; import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity;public class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (setThemeBlack) {setTheme(R.style.ThemeBlack);} else {setTheme(R.style.ThemeLight);}} }
主题设置写在BaseActivity的onCreate()函数中,它会在其他所有继承至它的Activity的onCreate()之前运行。
2.5 代码中引用属性
如果要在代码中引用 integerValue、floatAttrName、booleanAttrName,使用Context.obtainStyledAttributes()就可以了
package com.android.example.ui;import android.os.Bundle;import com.android.example.R;import android.content.res.TypedArray;import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity;public class BaseActivity extends AppCompatActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);if (setThemeBlack) {setTheme(R.style.ThemeBlack);} else {setTheme(R.style.ThemeLight);}TypedArray tyar = obtainStyledAttributes(new int[]{R.attr.colorValue,R.attr.integerValue,R.attr.booleanValue,R.attr.dimensionValue,R.attr.floatValue,R.attr.stringValue,R.attr.referenceValue});int colorArrtValue = tyar.getColor(0, Color.BLACK);int integerArrtValue = tyar.getInt(1, Integer.MAX_VALUE);boolean booleanArrtValue = tyar.getBoolean(2, false);float dimensionArrtValue = tyar.getDimensionPixelSize(3, 66);float floatArrtValue = tyar.getFloat(4, 99.99f);String stringArrtValue = tyar.getString(5);Drawable drawableArrtValue = tyar.getDrawable(6);} }
到此,通过setTheme()切换主题方式就讲述完毕了
这种切换主题方式的优点是:不需要修改太多代码,大部分修改只要针对布局文件就行。
弊端就是:主题只能内置配置好,一旦程序发布后,应用支持的主题类型就固定了,要想增加更多主题,只能发布新的版本。
如果想要动态配置主题,可以使用前文中提到过第1、第3种主题切换方式。
就是接下来要讲的内容了
三.主题资源打包成Apk进行切换
Apk主题切换和第1种将主题包整体下载或提前放置的方案很像。
共同点是:都需要在代码中动态设置图片、文字、颜色等UI信息。
区别是:第1种方法需要自行编写资源解析的方法。而Apk主题切换,可以使用Android自带Api
APK主题方案的基本思路是:
在Android中,所有的资源都是基于包的。资源以id进行标识,在同一个应用中,每个资源都有唯一标识。但在不同的应用中,可以有相同的id。
因此,只要获取到了其他应用的Context对象,就可以通过它的 getRsources() 获取到其绑定的资源对象。
然后,就可以使用 Resources 的 getXXX 方法获取字符串、颜色、dimension、图片等。
要想获取其他应用的Context对象,Android已经为我们提供好了接口:
android.content.ContextWrapper.createPackageContext(String packageName, int flags)
package com.android.example.ui;import android.os.Bundle; import androidx.annotation.Nullable;public class MainActivity extends BaseActivity {@Overrideprotected void onCreate(@Nullable Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);TextView text = (TextView) findViewById(R.id.remoteText);TextView color = (TextView) findViewById(R.id.remoteColor);ImageView image = (ImageView) findViewById(R.id.remote_image);try {//主题资源Apk包名String remotePackage = "com.xxx.themepackage";//主题资源Apk ContextContext remoteContext = createPackageContext(remotePackage,CONTEXT_IGNORE_SECURITY);//主题资源Apk ResourcesResources remoteResources = remoteContext.getResources();text.setText(remoteResources.getText(remoteResources.getIdentifier("application_name", "string", remotePackage)));color.setTextColor(remoteResources.getColor(remoteResources.getIdentifier("delete_target_hover_tint", "color", remotePackage)));image.setImageDrawable(remoteResources.getDrawable(remoteResources.getIdentifier("ic_launcher_home", "drawable", remotePackage)));} catch (NameNotFoundException e) {e.printStackTrace();}} }
四.综述
3.高级应用
内置主题实现简单、配置方便,但可扩展性不强。
Apk切换主题扩展性高,但要在代码里设置所有可变资源,一旦界面布局改变,需要较长时间进行代码改写。
实际运用中,可以将以上两种结合起来使用:
涉及到界面风格,如整体色调、不同的文字颜色,使用内置主题。图片资源则在APK主题包中提供。
相关文章:
Android:主题切换
一.概述 正在开发的应用做了一版新UI,原打算将新版UI按项目名做成资源包,再在build.gradle里productFlavors{ }多渠道打包实现 但被告知新旧两个项目共用一个分支,那就做成两个主题(Theme1/Theme2)来适配了 如果只是变更UI,做成…...

terminalworks ASP.NET Core PDF 浏览器-Crack
ASP.NET Core 的 PDF 查看器 terminalworks在 ASP.NET Core 网页或应用程序中添加可靠的 PDF 查看器的简单方法。 我们的 Web PDF 查看器基于经过验证和测试的 Mozilla PdfJS 解决方案,该解决方案在 Firefox 中用作默认 PDF 查看器。我们专门设计了我们的查看器&…...

Rust每日一练(Leetday0020) 最后单词的长度、螺旋矩阵II、排列序列
目录 58. 最后一个单词的长度 Length of Last Word 🌟 59. 螺旋矩阵 II Spiral Matrix II 🌟🌟 60. 排列序列 Permutation Sequence 🌟🌟🌟 🌟 每日一练刷题专栏 🌟 Rust每日…...

短视频矩阵源码如何做应用编程?
短视频矩阵源码, 短视频矩阵系统技术文档: 可以采用电子文档或者纸质文档的形式交付,具体取决于需求方的要求。电子文档可以通过电子邮件、远程指导交付云存储等方式进行传输、 短视频矩阵{seo}源码是指将抖音平台上的视频资源进行筛选、排…...
【运维知识进阶篇】Ansible实现一套完整LNMP架构
前面介绍了PlayBook怎么写服务部署,把服务部署上后,我们来用Ansible来部署项目,实现一套完整的LNMP架构。我们部署wordpress、wecenter、phpshe、phpmyadmin这四个项目。将其所有的剧本都写入lnmp.yml中,相关备份数据都放入root/a…...
Spring Boot 自动配置一篇概览
一、什么是自动配置 bean 自动配置类通过添加 AutoConfiguration 注解实现。 因为 AutoConfiguration 注解本身是以 Configuration 注解的,所以自动配置类可以算是一个标准的基于 Configuration 注解的类。 Conditional 注解可以用于声明自动配置启用条件&#x…...

深入理解设计原则之接口隔离原则(ISP)【软件架构设计】
系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 深入理解设计模式系列 高级C并发线程编程 LSP:接口隔离原则 系列文章目录1、接口隔离原则的定义和解读2、案例解读3、如何判断一个接口是否符合接口隔离原则?小结 1、接口隔离原则的定义和…...

IMX6ULL裸机篇之I2C实验主控代码说明二
一. I2C实验 I2C实验内容: 学习如何使用 I.MX6U 的 I2C 接口来驱动 AP3216C,读取 AP3216C 的传感器数据。 I2C读写数据时序图: I2C写数据时序图如下: I2C读数据时序图如下: 二. I2C主控读写时序 1. 读数据与写数…...

【计算机组成原理与体系结构】数据的表示与运算
目录 一、进位计数制 二、信息编码 三、定点数数据表示 四、校验码 五、定点数补码加减运算 六、标志位的生成 七、定点数的移位运算 八、定点数的乘除运算 九、浮点数的表示 十、浮点数的运算 一、进位计数制 整数部分: 二进制、八进制、十六进制 --…...

如何入门编程
随着信息技术的快速发展,编程已经成为一个越来越重要的技能。那么,我们该如何入门编程呢?欢迎大家积极讨论 一、自学编程需要注意什么? 对于我个人的理解,其实自学编程最重要的就是兴趣。你得培养编程兴趣。 所以在学…...
SQL中CONVERT转化日期函数的使用方法
SQL中CONVERT转化日期函数的使用方法 SQL中CONVERT函数最常用的是使用convert转化长日期为短日期,如果只要取yyyy-mm-dd格式时间, 就可以用convert(nvarchar(10),field,120) 120 是格式代码, nvarchar(10) 是指取出前10位字符. 例如 SELECT CONVERT(nvarchar(10),…...

SpringBoot2-核心技术(一)
SpringBoot2-核心技术(一) 了解SpringBoot配置文件的使用 文章目录 SpringBoot2-核心技术(一)了解SpringBoot配置文件的使用一、文件类型1. properties2. yaml 二、yaml的基本使用1. 基本语法2. 数据类型2.1 字面量 2.2 对象2.3 …...

mac host学习
参考: SSH中known_hosts文件作用和常见问题及解决方法 https://blog.csdn.net/luduoyuan/article/details/130070120在 Mac 上更改 DNS 设置 https://support.apple.com/zh-cn/guide/mac-help/mh14127/mac mac中有时候你输入的域名,但会跳转到与期望ip不…...
Java之~指定String日期时间,5分钟一截取时间
// 截取5分钟时间Testpublic void timeCutForDay() throws ParseException {String startTime "2023-03-28 09:16:03";String endTime "2023-03-31 23:59:59";SimpleDateFormat dateFormat new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");D…...

【chatGPT4结对编程】chatGPT4教我做图像分类
开始接触深度学习 大语言模型火了之后,我也想过是否要加入深度学习的行业当中来,一开始的想法就是AI大模型肯定会被各大厂垄断,我们作为普通应用型软件工程师直接调用api就完事,另外对自己的学历也自卑(刚刚够线的二本࿰…...
Different romantic
001 他暗恋上我们班上的一个女生。 He has a crush on a girl in our class. crush n. 迷恋 have a crush on (someone) 暗恋(某人) crush 也可以指“暗恋的对象”。例如,“他在大学曾经暗恋过两个人”,英语就是He had two crushe…...

learn C++ NO.7——C/C++内存管理
引言 现在是5月30日的正午,图书馆里空空的,也许是大家都在午休,也许是现在37摄氏度的气温。穿着球衣的我已经汗流浃背,今天热火战胜了凯尔特人,闯入决赛。以下克上的勇气也激励着我,在省内垫底的大学中&am…...
SDUT数据库原理——第十章作业(参考答案)
1. 简述使用检查点方法进行数据恢复的一般步骤。 答: (1)使用检查点方法进行数据恢复,首先从重新开始文件(见P302页图10.3)中找到最后一个检查点记录在日志文件中的地址,由该地址在日志文件中找到最后一个检查点记录。 (2)由该检查点记录得到检查点建立时刻所有正在…...

My Note of Diffusion Models
Diffusion Models Links: https://theaisummer.com/diffusion-models/ Markovian Hierachical VAE rvs: data: x 0 x_{0} x0,representation: x T x_{T} xT ( p ( x 0 , x 1 , ⋯ , x T ) , q ( x 1 , ⋯ , x T ∣ x 0 ) ) (p(x_0,x_1,\cdots,x_T),q(x_1,\cdots,x_{T…...

【P37】JMeter 仅一次控制器(Once Only Controller)
文章目录 一、仅一次控制器(Once Only Controller)参数说明二、测试计划设计2.1、测试计划一2.1、测试计划二 一、仅一次控制器(Once Only Controller)参数说明 可以让控制器内部的逻辑只执行一次;单次的范围是针对某…...

IDEA运行Tomcat出现乱码问题解决汇总
最近正值期末周,有很多同学在写期末Java web作业时,运行tomcat出现乱码问题,经过多次解决与研究,我做了如下整理: 原因: IDEA本身编码与tomcat的编码与Windows编码不同导致,Windows 系统控制台…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
Vue记事本应用实现教程
文章目录 1. 项目介绍2. 开发环境准备3. 设计应用界面4. 创建Vue实例和数据模型5. 实现记事本功能5.1 添加新记事项5.2 删除记事项5.3 清空所有记事 6. 添加样式7. 功能扩展:显示创建时间8. 功能扩展:记事项搜索9. 完整代码10. Vue知识点解析10.1 数据绑…...

STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
[Java恶补day16] 238.除自身以外数组的乘积
给你一个整数数组 nums,返回 数组 answer ,其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在 32 位 整数范围内。 请 不要使用除法,且在 O(n) 时间复杂度…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
CMake控制VS2022项目文件分组
我们可以通过 CMake 控制源文件的组织结构,使它们在 VS 解决方案资源管理器中以“组”(Filter)的形式进行分类展示。 🎯 目标 通过 CMake 脚本将 .cpp、.h 等源文件分组显示在 Visual Studio 2022 的解决方案资源管理器中。 ✅ 支持的方法汇总(共4种) 方法描述是否推荐…...

RabbitMQ入门4.1.0版本(基于java、SpringBoot操作)
RabbitMQ 一、RabbitMQ概述 RabbitMQ RabbitMQ最初由LShift和CohesiveFT于2007年开发,后来由Pivotal Software Inc.(现为VMware子公司)接管。RabbitMQ 是一个开源的消息代理和队列服务器,用 Erlang 语言编写。广泛应用于各种分布…...
NPOI操作EXCEL文件 ——CAD C# 二次开发
缺点:dll.版本容易加载错误。CAD加载插件时,没有加载所有类库。插件运行过程中用到某个类库,会从CAD的安装目录找,找不到就报错了。 【方案2】让CAD在加载过程中把类库加载到内存 【方案3】是发现缺少了哪个库,就用插件程序加载进…...