【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

且听下回分解!

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

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

相关文章

独立开发者系列(20)——扫码登录

(1)网页端的安全登录设计 很多大型网站都有登录限制。这里以一个案例作为例子完整解析。理解安全的登录设计方式,无论对于以后做自动化,自动登录网站,获取数据,还是自己开发月租类型的系统非常有用。当前一…

从草图到现实:SketchUp 在建筑项目中的独特优势

Sketchup 是全球最受欢迎的建筑可视化平台之一。借助该平台提供的各种工具,您可以创建可供市场使用的逼真项目。Sketchup为什么如此优秀?它对建筑项目有哪些优势?下面,你将看到什么是 Sketchup 以及这个工具的一些重要的优势。 关…

新手如何尽快入门性能测试?

一、学习基础知识 性能测试的基本概念: 性能测试的定义:使用自动化工具,模拟不同的场景,对软件各项性能指标进行测试和评估的过程。 性能测试的目的:评估当前系统的能力、寻找性能瓶颈以优化性能、评估软件是否能够满…

高阶算法班从入门到精通之路课程

本课程旨在帮助学员深入理解算法与数据结构的核心概念,从而掌握高级算法设计与分析技能。每集课程内容精心设计,涵盖了常用数据结构、经典算法及其应用场景等方面的深度讲解,同时通过大量实例演练,帮助学员提升解决实际编程难题的…

【Dell R730 折腾记录】风扇调速--在 Ubuntu 系统上开机自启动并每隔30分钟执行一次风扇定速脚本

前段时间升级了一下机柜里的服务器,替换掉了一台旧的 Dell 服务器,换上了这台 R730。但是无奈于噪音的袭扰,搁置了一段时间。我在这台机器上目前安装了一块 Intel Xeon E5-2630v3 芯片以及一张改过散热的 NVIDIA Tesla P4 计算卡。结果就是散…

使用工业自动化的功能块实现大语言模型应用

大语言模型无所不能? 以chatGPT为代表的大语言模型横空出世,在世界范围内掀起了一场AI革命。给人的感觉似乎大模型语言无所不能。它不仅能够生成文章,图片和视频,能够翻译文章,分析科学和医疗数据,甚至可以…

golang与以太坊交互

文章目录 golang与以太坊交互什么是go-ethereum与节点交互前的准备使用golang与以太坊区块链交互查询账户的余额使用golang生成以太坊账户使用golang生成以太坊钱包使用golang在账户之间转移eth安装使用solc和abigen生成bin和abi文件生成go文件使用golang在测试网上部署智能合约…

《昇思25天学习打卡营第12天|onereal》

CycleGAN图像风格迁移互换 模型简介 CycleGAN(Cycle Generative Adversarial Network) 即循环对抗生成网络,来自论文 Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks 。该模型实现了一种在没有配对示例的情况下学习将图像从源域…

C++中的引用——引用做函数参数

作用:函数传参时,可以利用引用的技术让形参修饰实参 优点:可以简化指针修改实参 示例: 1.值传递 运行结果: 2.地址传递 运行结果: 3.引用传递 运行结果:

SQL注入方法

文章目录 前言如何测试与利用注入点手工注入思路工具sqlmap-r-u-m--level--risk-v-p--threads-batch-smart--os-shell--mobiletamper插件获取数据的相关参数 前言 记录一些注入思路和经常使用的工具,后续有用到新的工具和总结新的方法再继续补充。 如何测试与利用注…

windows下使用编译opencv在qt中使用

记录一下:在windows下qt使用opencv 1、涉及需要下载的软件 CMake 下载地址opecnv下载地址mingw(需要配置环境变量) 这个在下载qt的时候可以直接安装一般在qt的安装路径下的tool里比如我的安装路径 (C:\zz\ProgramFiles\QT5.12\Tools\mingw730_64) 2、在安装好CMake…

【IT领域新生必看】探索Java中的对象创建:深入理解`new`与`clone`的对比

文章目录 引言什么是new关键字?使用new关键字的基本语法示例: 什么是clone方法?使用clone方法的基本语法示例: new与clone的区别内存分配与初始化调用方式适用场景性能 new关键字的优缺点优点缺点 clone方法的优缺点优点缺点 深入…

大华设备接入GB28181视频汇聚管理平台EasyCVR安防监控系统的具体操作步骤

智慧城市/视频汇聚/安防监控平台EasyCVR兼容性强,支持多协议接入,包括国标GB/T 28181协议、GA/T 1400协议、部标JT808协议、RTMP、RTSP/Onvif协议、海康Ehome、海康SDK、大华SDK、华为SDK、宇视SDK、乐橙SDK、萤石云SDK等,并能对外分发RTMP、…

Pseudo-Label : The Simple and Efficient Semi-Supervised Learning Method--论文笔记

论文笔记 资料 1.代码地址 https://github.com/iBelieveCJM/pseudo_label-pytorch 2.论文地址 3.数据集地址 论文摘要的翻译 本文提出了一种简单有效的深度神经网络半监督学习方法。基本上,所提出的网络是以有监督的方式同时使用标记数据和未标记数据来训练的…

加法器的基本操作

基本单元 与门(AND) 全1为1,有0为0 或门(OR) 全0为0,有1为1 非门(NOT) 为1则0,为0则1 异或门(XOR) 两个输入端,相同为0,不同为1 与非门(NADD) 全1为0,有0为1 或非门(NOR) 全0为1,有1为0。刚…

redis 如何使用 scan, go语言

建议用方案乙 文章目录 场景方案方案甲方案乙 拓展 场景 redis 中存在大量 key。 其中有一部分是用户登陆的 session_id, 结构是 : session_id:1session_id:2session_id:3需求: 有多少用户在线 方案 方案甲 keys session_id:*这种方式简…

项目部署_持续集成_Jenkins

1 今日内容介绍 1.1 什么是持续集成 持续集成( Continuous integration , 简称 CI )指的是,频繁地(一天多次)将代码集成到主干 持续集成的组成要素 一个自动构建过程, 从检出代码、 编译构建…

数据结构——单向循环链表

文章目录 1. 概念 2. 区别 2.1 结构区别 2.2 访问方式区别 2.3 优缺点对比 3. 流程 4. 基本操作 5. 代码示例 1. 概念 单向循环链表是一种特殊的单链表,其中最后一个节点的后继指针指向头节点,形成一个环。单向循环链表适合用于需要循环访问数据…

Spring Boot集成jacoco实现单元测试覆盖统计

1.什么是jacoco? JaCoCo,即 Java Code Coverage,是一款开源的 Java 代码覆盖率统计工具。支持 Ant 、Maven、Gradle 等构建工具,支持 Jenkins、Sonar 等持续集成工具,支持 Java Agent 技术远程监控 Java 程序运行情况…

【鸿蒙学习笔记】Stage模型工程目录

官方文档:应用配置文件概述(Stage模型) 目录标题 FA模型和Stage模型工程级目录模块级目录app.json5module.json5程序执行流程程序基本结构开发调试与发布流程 FA模型和Stage模型 工程级目录 模块级目录 app.json5 官方文档:app.j…