首页 > 其他分享 >android动态换肤使用本地资源原理分析

android动态换肤使用本地资源原理分析

时间:2022-11-24 11:31:49浏览次数:47  
标签:换肤 resId String int attrs 本地 android id view


大致原理:

在application里面注册所有activity回调这样可以实现很少的改动侵入性

  1. 给LayoutFactory设置自己的​​factory2​​,工厂2 使activity在​​setContentView​​调用inflate的时候触发自己的factory的创建view方法.,为什么可以呢?因为​​LayoutFactory.from(this)​​当前activity填充的时候调用多次实际上还是同样的对象,所以可以这么简单的进行了hook 不需要进行更高级的hook from返回值类似的方法操作。
  2. 创建的时候根据属性和节点判断是否需要更换皮肤,比如#开头引用的可以忽略,对于属性 ```?``修饰或者是@引用的进行记录,在view构造方法创建完毕之后设置一次皮肤就完成了更改. 先通过原来apk的id转换为资源名称然后 用这个资源名称在自己构建的Resource里面进行加载如果能加载就用自己的,否则还是用系统的.其实如果没加载到干脆不操作也是可以的.
  3. 比如对背景颜色进行换肤,实际上一个view的setBackground方法也许会调用2次,因为第一次是构造的时候new ImageView(context,attrs)的时候进行了一次初始化,构造完成之后更换加载默认皮肤又进行了一次替换.

大致经典代码:

创建自己的Resource

AssetManager assetManager = AssetManager.class.newInstance();
// 添加资源进入资源管理器
Method addAssetPath = assetManager.getClass().getMethod("addAssetPath", String
.class);
addAssetPath.setAccessible(true);
addAssetPath.invoke(assetManager, path);

Resources resources = application.getResources();

Resources skinResource = new Resources(assetManager, resources.getDisplayMetrics(),
resources.getConfiguration());

PackageManager mPm = application.getPackageManager();
PackageInfo info = mPm.getPackageArchiveInfo(path, PackageManager
.GET_ACTIVITIES);
String packageName = info.packageName;

} catch (Exception e) {
e.printStackTrace();
}

上面的方法是拿到皮肤apk 然后自己构造一个Resources.并调用资源管理器的addAssetPath添加进去,避免不合法,但是此时id是不相同的,

view的属性节点操作

/**
*
* @param view
* @param attrs
*/
public void parseViewInfo(View view, AttributeSet attrs) {
List<SkinPair> skinPairs = new ArrayList<>();
for (int i = 0; i < attrs.getAttributeCount(); i++) {

String attributeName = attrs.getAttributeName(i);

if (mAttributes.contains(attributeName)) {
String attributeValue = attrs.getAttributeValue(i);
if (attributeValue.startsWith("#")) {
continue;
}
//资源id
int resId;
if (attributeValue.startsWith("?")) {
//得到属性id 解析成具体值id
int attrId = Integer.parseInt(attributeValue.substring(1));

resId = SkinThemeUtils.getResIdByAttr(view.getContext(), new int[]{attrId})[0];
} else {
// @694886526
resId = Integer.parseInt(attributeValue.substring(1));
}
if (resId != 0) {
//对于匹配的成功找到的进行添加 方便更换
}
}
}


}

换肤的操作

换肤的时候只需要查找是否匹配,成功通过资源名找到资源id才进行替换

public int getIdentifier(int resId) {
if (isDefaultSkin) {
return resId;
}

//R.drawable.ic_launcher
String resName = mAppResources.getResourceEntryName(resId);//ic_launcher
String resType = mAppResources.getResourceTypeName(resId);//如 drawable color
int skinId = mSkinResources.getIdentifier(resName, resType, mSkinPkgName);
return skinId;
}
public int getColor(int resId) {
if (isDefaultSkin) {
return mAppResources.getColor(resId);
}
int skinId = getIdentifier(resId);
if (skinId == 0) {
return mAppResources.getColor(resId);
}
return mSkinResources.getColor(skinId);
}

上面的方法拿颜色值是先判断是否启用皮肤如果没有启用皮肤那么根据​​getIdentifier(resid)​​​方法先传递原始属性id得获取资源名称,然后把资源名称和类型传递进去再取调用自己创建的皮肤​​Resources​​类

mSkinResources.getIdentifier(resName, resType, mSkinPkgName);

如果能拿到自然就从自己的皮肤资源里面获取颜色值了,那么对于属性替换又是如何操作呢?

public static int[] getResIdFromAttrs(Context context, int[] attrs) {
int[] resIds = new int[attrs.length];
TypedArray typedArray = context.obtainStyledAttributes(attrs);
for (int i = 0; i < typedArray.length(); i++) {
resIds[i] = typedArray.getResourceId(i, 0);
}
typedArray.recycle();
return resIds;
}

标签:换肤,resId,String,int,attrs,本地,android,id,view
From: https://blog.51cto.com/u_15458814/5882979

相关文章

  • [每日分享]android获取文件目录总大小实现清除缓存
    跟大家讲一个笑话,我维护老项目,看到有一个清除缓存功能,我看了一下怎么实现的,结果就一个​​Toast​​太逗比了/***获取指定文件夹的大小**@paramf......
  • 本地和远程仓库使用git命令管理项目代码
    #gitlabhttp://xxxxx:8085/user/pwd###########################################################################初始化cd/home/xx/projectsgitclonehttp:/......
  • Android网络请求(终) 网络请求框架Retrofit
    Android网络请求(终)网络请求框架RetrofitRetrofit底层是由OkHttp封装的,Retrofit对于注解的使用十分频繁,所以不了解注解的同学们可以去查查资料什么的。这里有一个小细......
  • Android Studio 连接 MySQL数据库
    1、下载MySQL-connector-jave.jar包地址如下:https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.46 2、将jar包移到如图所示的位置,然后右键addasl......
  • HarmonyOS ArkTS 本地库&三方库的用法
    项目介绍项目内容:自定义基础组件和容器组件、依赖库概念、本地依赖库的创建和引用、三方库的引用。工具版本:DevEcoStudio3.1Canary1SDK版本:3.2.1.4(APIVersion9C......
  • android 修改 system 目录下的文件
    在超级终端输入命令:su busyboxmount可以看到/system挂接的分区如: /dev/block/mtdblock6然后重新挂接并设置为可写:busyboxmount-orw,re......
  • android 隐藏声音文件-不让音乐播放器查找到
    有时候某些程序自带的声音文件,不想被音乐播放找到,如何实现呢?很简单,在需要隐藏的目录下面添加 .nomedia 文件。内容为空即可.可以用vi来添加这个空文件.在超级端下......
  • 基于飞书通讯录搭建本地LDAP服务(钉钉、企业微信配置后续更新)
    目前飞书社交办公应用成为公司日常沟通办公的协作工具,以及作为各种流程的审批处理系统,HR 也会在飞书上去管理所有员工的状态及组织架构。随着公司内新部署的业务系统越来越......
  • mkdocs启动本地项目
    想在本地启动OWASPtop10项目,发现使用这个命令就能在其他电脑访问,但是0.0.0.0换成本机IP就不行mkdocsserve-a0.0.0.0:8000 这是启动成功:为什么呢?有知道的大神......
  • # Android网络请求(4) 网络请求框架Volley
    Android网络请求(4) 网络请求框架VolleyVolley是Google在2013年5月15日到17日在旧金山Moscone中心举办网络开发者年会中推出的Android异步网络加载框架和图片加载框架,它特......