【Android】自定义换肤框架02之自定义AssetManager和Resource
ResourceId是如何变成对应Resource的
在上一章中,我们已经讲过,apk中有个资源索引文件
其中保存了每个资源对应的id,name,type,path
资源文件的解析,主要涉及两个类,AssetManager和Resource
- AssetManager,用于管理apk中的原生资源文件,包括asset和resource
- AssetManager通过调用addAssetPath方法,来添加提供资源的apk
- addAssetPath默认使用的是context.packageResourcePath,及当前安装包的位置
- 如果想加载其它apk里面的资源,就得自定义AssetManager
- AssetManager的构造函数是因此功能,必须通过反射才能自己创建新的实例
- Resource,用于管理resource文件夹下的资源,如color,drawable等
- Resource解析资源前,首先要拿到apk中的资源索引文件,和屏幕信息,配置信息
- Resource对象的构建依赖于AssetManager,DisplayMetrics,Configuration三个对象
- 如果我们想从其它apk中加载资源,则需要提供自定义的AssetManager给Resource
- 由于DisplayMetrics和Configuration信息是固定的,因此不需要自定义
设计思路
- 当我们想根据皮肤去替换某个资源时,在skin.apk中创建一份同名,但内容不同的资源
- 自定义SkinnerAssetManager,并绑定skin.apk
- 自定义SkinnerResources,并绑定SkinnerAssetManager
- 相同名称的资源,在不同apk中的id是不一样,但我们可以通过name+type+package的方式去找到对应的id
- 通过OriginResourceId+OriginResources,得到name+type+package
- 通过SkinnerResources,以及name+type+package,拿到SkinnerResourceId
- 通过SkinnerResources+SkinnerResourceId,解析出skin.apk中的color或drawable
- 由于并不是所有属性都会跟随皮肤而变换,因此SkinnerResourceId有可能不存在
- 如果SkinnerResourceId不存在,则使用OriginResources去加载原来的资源,这样大致实现了资源的自动加载
自定义SkinnerAssetManager
package com.android.library.skinnerimport android.app.Application
import android.content.res.AssetManager
import android.content.res.Resources
import android.graphics.drawable.Drawable@Suppress("Deprecated")
object SkinnerAssetManager {lateinit var context: Applicationlateinit var assetManager: AssetManagerlateinit var skinnerResources: Resourceslateinit var originResources: Resourcesfun init(application: Application, resourcePath: String) = apply {context = applicationcreateHookedAssetManager(resourcePath)}private fun createHookedAssetManager(resourcePath: String) {val assetManager = AssetManager::class.java.newInstance()val method = AssetManager::class.java.getDeclaredMethod("addAssetPath", String::class.java)method.invoke(assetManager, resourcePath)this.originResources = context.resourcesval resources = Resources(assetManager, originResources.displayMetrics, originResources.configuration)this.assetManager = assetManagerthis.skinnerResources = resources}fun skinResId(resId: Int): Int {return skinnerResources.getIdentifier(originResources.getResourceName(resId),originResources.getResourceTypeName(resId),originResources.getResourcePackageName(resId))}fun skinColor(resId: Int): Int {val skinResId = skinResId(resId)if (skinResId > 0) {return skinnerResources.getColor(skinResId)}return originResources.getColor(resId)}fun skinDrawable(resId: Int): Drawable {val skinResId = skinResId(resId)if (skinResId > 0) {return skinnerResources.getDrawable(skinResId)}return originResources.getDrawable(resId)}
}
拷贝测试皮肤包到存储卡
这里我们将测试包放在asset文件夹里面,在应用启动时拷贝到存储卡,从而省去人工操作
private fun copySkinPackage() {val fis = application.assets.open("skin.apk")val fos = FileOutputStream("sdcard/skin.apk")val buffer = ByteArray(fis.available())fis.read(buffer)fos.write(buffer)
}
通过指定皮肤包初始化SkinnerAssetManager
SkinnerAssetManager.init(application, "sdcard/skin.apk")
使用自定义的SkinnerAssetManager加载资源
val drawable = SkinnerAssetManager.skinDrawable(R.drawable.icon_app)
binding.image.setImageDrawable(drawable)
十万个为什么
到目前为止,我们已经实现了从指定apk中加载同名资源
下一步问题是,如何让Activity/Fragment/View/Xml使用SkinnerResources,而不是默认的OriginResources
且听下回分解!
相关文章:
【Android】自定义换肤框架02之自定义AssetManager和Resource
ResourceId是如何变成对应Resource的 在上一章中,我们已经讲过,apk中有个资源索引文件 其中保存了每个资源对应的id,name,type,path 资源文件的解析,主要涉及两个类,AssetManager和Resource …...
熵权法、熵值法、熵权TOPSIS三种方法的实用场景及优劣比较
在统计分析与决策分析中,熵权法、熵值法和熵权TOPSIS是三种常用的基于信息熵理论的方法。这些方法在处理多属性或多指标决策问题时,能够客观地反映数据的内在特性和各指标的相对重要性,为科学决策提供了有力支持。 熵权法 实用场景 熵权法是一种基于信息熵的客观赋权方法…...
无人机人员搜救
人员搜救-水域救援 水域搜救:快速水面搜查 物资抛投:救生物资抛投 绳索牵引:牵引救援绳索 领航船艇:水面侦察领航 人员搜救 昼夜搜救,精准定位 水域搜救 经纬 M300 RTK 搭载禅思 H20T 能够满足全天候作业需求&a…...
目标检测算法
讯飞星火 目标检测算法是计算机视觉领域的一项核心技术,用于在数字图像或视频中识别和定位一个或多个特定对象实例。这些算法通常需要确定对象的位置(例如通过边界框)和类别(例如人、汽车、狗等)。以下是一些流行的目…...
SpringSecurity 三更草堂学习笔记
0.简介 Spring Security是Spring家族中的一个安全管理框架。相比与另外一个安全框架Shiro,它提供了更丰富的功能,社区资源也比Shiro丰富。 一般来说中大型的项目都是使用SpringSecurity来做安全框架。小项目有Shiro的比较多,因为相比与Spring…...
鸿蒙生态应用开发白皮书V3.0
来源:华为: 近期历史回顾:...
CSS - 深入理解选择器的使用方式
CSS基本选择器 通配选择器元素选择器类选择器id 选择器 通配选择器 作用:可以选中所有HTML元素。语法: * {属性名;属性值; }举例: /* 选中所有元素 */ * {color: orange;font-size: 40px; }在清除样式方面有很大作用 元素选择器…...
动手学深度学习(Pytorch版)代码实践 -循环神经网络-54~55循环神经网络的从零开始实现和简洁实现
54循环神经网络的从零开始实现 import math import torch from torch import nn from torch.nn import functional as F from d2l import torch as d2l import matplotlib.pyplot as plt import liliPytorch as lp# 读取H.G.Wells的时光机器数据集 batch_size, num_steps 32, …...
Python酷库之旅-第三方库Pandas(006)
目录 一、用法精讲 10、pandas.DataFrame.to_excel函数 10-1、语法 10-2、参数 10-3、功能 10-4、返回值 10-5、说明 10-6、用法 10-6-1、数据准备 10-6-2、代码示例 10-6-3、结果输出 11、pandas.ExcelFile类 11-1、语法 11-2、参数 11-3、功能 11-4、返回值 …...
智慧矿山:EasyCVR助力矿井视频多业务融合及视频转发服务建设
一、方案背景 随着矿井安全生产要求的不断提高,视频监控、数据传输、通讯联络等业务的需求日益增长。为满足矿井生产管理的多元化需求,提高矿井作业的安全性和效率,TSINGSEE青犀EasyCVR视频汇聚/安防监控综合管理平台,旨在构建一…...
Unix/Linux shell实用小程序1:生字本
前言 在日常工作学习中,我们会经常遇到一些不认识的英语单词,于时我们会打开翻译网站或者翻译软件进行查询,但是大部分工具没有生词本的功能,而有生字本的软件又需要注册登陆,免不了很麻烦,而且自己的数据…...
springboot2.7.6 集成swagger
在 Spring Boot 2.7.6 版本中集成 Swagger 的步骤相对直接,主要涉及添加依赖、编写配置以及在控制器中添加文档注解几个环节。 下面是集成 Swagger 的基本步骤: 1. 添加依赖 首先,在pom.xml文件中添加 Swagger 相关依赖。 对于 Spring Boot…...
面试篇-系统设计题总结
文章目录 1、设计一个抢红包系统1.1 高可用的解决方案:1.2 抢红包系统的设计1.3 其他 2、秒杀系统设计 这里记录一些有趣的系统设计类的题目,一般大家比较喜欢出的设计类面试题目会和高可用系统相关比如秒杀和抢红包等。欢迎大家在评论中评论自己遇到的题…...
如何摆脱反爬虫机制?
在网站设计时,为了保证服务器的稳定运行,防止非法数据访问,通常会引入反爬虫机制。一般来说,网站的反爬虫机制包括以下几种: 1. CAPTCHA:网站可能会向用户显示CAPTCHA,要求他们在访问网站或执行…...
68745
877454...
github仓库的基本使用-创建、上传文件、删除
1.第一步 先点击左侧菜单栏的远程仓库 2.点击NEW 3.创建仓库 然后点击右下角的 CREATE 4.点击code 点击SSH,然后我出现了You don’t have any public SSH keys in your GitHub account. You can add a new public key, or try cloning this repository via HTTPS. 1ÿ…...
[课程][原创]opencv图像在C#与C++之间交互传递
opencv图像在C#与C之间交互传递 课程地址:https://edu.csdn.net/course/detail/39689 无限期视频有效期 课程介绍课程目录讨论留言 你将收获 学会如何封装C的DLL 学会如何用C#调用C的DLL 掌握opencv在C#和C传递思路 学会如何配置C的opencv 适用人群 拥有C#…...
科研绘图系列:R语言双侧条形图(bar Plot)
介绍 双侧条形图上的每个条形代表一个特定的细菌属,条形的高度表示该属的LDA得分的对数值,颜色用来区分不同的分类群或组别,它具有以下优点: 可视化差异:条形图可以直观地展示不同细菌属在得分上的差异。强调重要性:较高的条形表示某些特征在区分不同组别中具有重要作用…...
计算机未来大方向的选择
选专业要了解自己的兴趣所在。 即想要学习什么样的专业,如果有明确的专业意向,就可以有针对性地选择那些专业实力较强的院校。 2.如果没有明确的专业意向,可以优先考虑一下院校。 确定一下自己想要选择综合性院校还是理工类院校或是像财经或者…...
AndroidKille不能用?更新apktool插件-cnblog
AndroidKiller不更新插件容易报错 找到apktool管理器 填入apktool位置,并输入apktool名字 选择默认的apktool版本 x掉,退出重启 可以看到反编译完成了...
Day131 | 灵神 | 回溯算法 | 子集型 子集
Day131 | 灵神 | 回溯算法 | 子集型 子集 78.子集 78. 子集 - 力扣(LeetCode) 思路: 笔者写过很多次这道题了,不想写题解了,大家看灵神讲解吧 回溯算法套路①子集型回溯【基础算法精讲 14】_哔哩哔哩_bilibili 完…...
五年级数学知识边界总结思考-下册
目录 一、背景二、过程1.观察物体小学五年级下册“观察物体”知识点详解:由来、作用与意义**一、知识点核心内容****二、知识点的由来:从生活实践到数学抽象****三、知识的作用:解决实际问题的工具****四、学习的意义:培养核心素养…...
C# 类和继承(抽象类)
抽象类 抽象类是指设计为被继承的类。抽象类只能被用作其他类的基类。 不能创建抽象类的实例。抽象类使用abstract修饰符声明。 抽象类可以包含抽象成员或普通的非抽象成员。抽象类的成员可以是抽象成员和普通带 实现的成员的任意组合。抽象类自己可以派生自另一个抽象类。例…...
在鸿蒙HarmonyOS 5中使用DevEco Studio实现录音机应用
1. 项目配置与权限设置 1.1 配置module.json5 {"module": {"requestPermissions": [{"name": "ohos.permission.MICROPHONE","reason": "录音需要麦克风权限"},{"name": "ohos.permission.WRITE…...
Web 架构之 CDN 加速原理与落地实践
文章目录 一、思维导图二、正文内容(一)CDN 基础概念1. 定义2. 组成部分 (二)CDN 加速原理1. 请求路由2. 内容缓存3. 内容更新 (三)CDN 落地实践1. 选择 CDN 服务商2. 配置 CDN3. 集成到 Web 架构 …...
OPENCV形态学基础之二腐蚀
一.腐蚀的原理 (图1) 数学表达式:dst(x,y) erode(src(x,y)) min(x,y)src(xx,yy) 腐蚀也是图像形态学的基本功能之一,腐蚀跟膨胀属于反向操作,膨胀是把图像图像变大,而腐蚀就是把图像变小。腐蚀后的图像变小变暗淡。 腐蚀…...
Angular微前端架构:Module Federation + ngx-build-plus (Webpack)
以下是一个完整的 Angular 微前端示例,其中使用的是 Module Federation 和 npx-build-plus 实现了主应用(Shell)与子应用(Remote)的集成。 🛠️ 项目结构 angular-mf/ ├── shell-app/ # 主应用&…...
人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
NPOI Excel用OLE对象的形式插入文件附件以及插入图片
static void Main(string[] args) {XlsWithObjData();Console.WriteLine("输出完成"); }static void XlsWithObjData() {// 创建工作簿和单元格,只有HSSFWorkbook,XSSFWorkbook不可以HSSFWorkbook workbook new HSSFWorkbook();HSSFSheet sheet (HSSFSheet)workboo…...
