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)参数说明 可以让控制器内部的逻辑只执行一次;单次的范围是针对某…...
调用支付宝接口响应40004 SYSTEM_ERROR问题排查
在对接支付宝API的时候,遇到了一些问题,记录一下排查过程。 Body:{"datadigital_fincloud_generalsaas_face_certify_initialize_response":{"msg":"Business Failed","code":"40004","sub_msg…...
2.Vue编写一个app
1.src中重要的组成 1.1main.ts // 引入createApp用于创建应用 import { createApp } from "vue"; // 引用App根组件 import App from ./App.vue;createApp(App).mount(#app)1.2 App.vue 其中要写三种标签 <template> <!--html--> </template>…...
新能源汽车智慧充电桩管理方案:新能源充电桩散热问题及消防安全监管方案
随着新能源汽车的快速普及,充电桩作为核心配套设施,其安全性与可靠性备受关注。然而,在高温、高负荷运行环境下,充电桩的散热问题与消防安全隐患日益凸显,成为制约行业发展的关键瓶颈。 如何通过智慧化管理手段优化散…...
OpenLayers 分屏对比(地图联动)
注:当前使用的是 ol 5.3.0 版本,天地图使用的key请到天地图官网申请,并替换为自己的key 地图分屏对比在WebGIS开发中是很常见的功能,和卷帘图层不一样的是,分屏对比是在各个地图中添加相同或者不同的图层进行对比查看。…...
.Net Framework 4/C# 关键字(非常用,持续更新...)
一、is 关键字 is 关键字用于检查对象是否于给定类型兼容,如果兼容将返回 true,如果不兼容则返回 false,在进行类型转换前,可以先使用 is 关键字判断对象是否与指定类型兼容,如果兼容才进行转换,这样的转换是安全的。 例如有:首先创建一个字符串对象,然后将字符串对象隐…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
STM32---外部32.768K晶振(LSE)无法起振问题
晶振是否起振主要就检查两个1、晶振与MCU是否兼容;2、晶振的负载电容是否匹配 目录 一、判断晶振与MCU是否兼容 二、判断负载电容是否匹配 1. 晶振负载电容(CL)与匹配电容(CL1、CL2)的关系 2. 如何选择 CL1 和 CL…...
Linux部署私有文件管理系统MinIO
最近需要用到一个文件管理服务,但是又不想花钱,所以就想着自己搭建一个,刚好我们用的一个开源框架已经集成了MinIO,所以就选了这个 我这边对文件服务性能要求不是太高,单机版就可以 安装非常简单,几个命令就…...
在 Visual Studio Code 中使用驭码 CodeRider 提升开发效率:以冒泡排序为例
目录 前言1 插件安装与配置1.1 安装驭码 CodeRider1.2 初始配置建议 2 示例代码:冒泡排序3 驭码 CodeRider 功能详解3.1 功能概览3.2 代码解释功能3.3 自动注释生成3.4 逻辑修改功能3.5 单元测试自动生成3.6 代码优化建议 4 驭码的实际应用建议5 常见问题与解决建议…...
二维FDTD算法仿真
二维FDTD算法仿真,并带完全匹配层,输入波形为高斯波、平面波 FDTD_二维/FDTD.zip , 6075 FDTD_二维/FDTD_31.m , 1029 FDTD_二维/FDTD_32.m , 2806 FDTD_二维/FDTD_33.m , 3782 FDTD_二维/FDTD_34.m , 4182 FDTD_二维/FDTD_35.m , 4793...
