Android 使用Overlay现实主题切换

news2024/12/15 23:46:11

最近项目上,想做一个主题切换的功能,整理了一下发布出来,主要使用的是IOverlayManager,大体思路如下:

1、想切换的应用,各自做overlay apk(简称皮肤包)

2、将overlay apk push 到vendor/overlay目录下(如果没有这个目录,push到system/app里面也可以),这个主要目的是为了让overlay 找到这个overlay apk。

3、重启设备

4、点击切换主题

5、各个应用重走生命周期,然后显示出来新的资源

这个主题切换的原理,我自己感觉,就是将overlay apk里面的资源替换到原来apk里面,然后重新加载。

一、app准备,制作overlay apk皮肤包

1、AS内新建项目    文件命名方式:xxxx.Red/xxxx.Young/xxxx.Gold

例如:LocalSetting   新建替换资源的模块名可命名为 LocalSetting.Red /LocalSetting.Young/LocalSetting.Gold,其实就和新建的app 项目一样。

 仅保留AndroidManifest.xml 和res目录下仅保留需要替换的资源信息(values下的colors.xml , strings.xml , dimens.xml themes.xml等 和 drawables/xml目录下的资源, 不支持替换布局文件),想替换那个文件,就在对应的目录下用相同的资源名字放进去。

下图是launcher的主题app目录。

AndroidManifest.xml仅包含以下信息即可,其中targetPackage为要替换资源的app包名。

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.test.launcher.young">

    <overlay
        android:priority="9"
        android:targetPackage="com.test.launcher" />
</manifest>

 2、生成overlay apk;
        overlay apk 中 AndroidManifest.xml 包名命名方式:xxxx.red/xxxx.young/xxxx.gold,如上示例
        eg: package="com.neusoft.localsetting.red"
        替换  红色(酷炫)主题的apk名为localsetting_red.apk
        替换  金色(酷睿)主题的apk名为localsetting_gold.apk
        替换  年轻色(酷潮)主题的apk名为 localsetting_young.apk
注意:其中red、gold、young是需要打合定义的,目的是为了在切换主题时,获取对应主题的包名

 3、每套主题如果对应2套UI(日夜模式)大家正常在对应的主题中建-night文件夹即可。
 本地测试:将生成的overlay apk签名  push 到vendor/overlay/red/xxxx.apk目录下; 然后调用切换主题即可。

二、实现主题切换流程

主题切换流程:
主要使用原生方法IOverlayManager,调用setEnabledExclusive方法切换主题。

1、开机遍历获取/vendor/overlay下的主题包
使用如下代码可以把不同主题的包名方法一个list中,等后续切换时直接使用。
注意:这个包名是之前和app打合的,必须所有的app都要按照这个要求规则命名apk。

private List<String> mRed1List = new ArrayList<String>(); //存放红色主题包名list
private List<String> mYoungList = new ArrayList<String>(); //存放young主题包名list
private IOverlayManager mOverlayManager = IOverlayManager.Stub.asInterface(ServiceManager.getService("overlay"));


    public void getThemeOverlayList() {
        IOverlayManager mOverlayManager = IOverlayManager.Stub.asInterface(ServiceManager.getService("overlay"));
        mRed1List.clear();
        mYoungList.clear();
        try {
            mOverlayInfoMap = mOverlayManager.getAllOverlays(0);
            for (List<OverlayInfo> overlayInfos : mOverlayInfoMap.values()) {
                for (int j = 0; j < overlayInfos.size(); j++) {
                    String overlayPackageName = overlayInfos.get(j).packageName;
                    if (overlayPackageName.contains("red")) {
                        mRed1List.add(overlayPackageName);
                    } else if (overlayPackageName.contains("young")) {
                        mYoungList.add(overlayPackageName);
                    }
                }
            }
        } catch (RemoteException e) {
            e.printStackTrace();
        }
    }

2、切换时遍历list,调用原生方法mOverlayManager.setEnabledExclusive切换主题。

mOverlayManager.setEnabledExclusive(mYoungList.get(i), true, 0);方法是把对应的主题包资源替换进去。
mOverlayManager.setEnabled(mRed1List.get(i), false, 0);方法是把主题包禁用,从而显示显示源代码里面的资源。
    private void changeThemeColor(int themeChange) {
        List<Boolean> changeThemeResult = new ArrayList<>();
        List<String> changeResultFail = new ArrayList<>();
        try {
            if (themeChange == SettingConfig.THEME_RED) {
                if (mRed1List.size() != 0) {
                    for (int i = 0; i < mRed1List.size(); i++) {
                        boolean isSuccess = mOverlayManager.setEnabledExclusive(mRed1List.get(i), true, 0);
                        if (!isSuccess) {
                            changeThemeResult.add(isSuccess);
                            changeResultFail.add(mRed1List.get(i));
                        }
                    }
                    Log.d(TAG, "---mRed1List.get(i):" + mRed1List.toString());
                }
            } else if (themeChange == SettingConfig.THEME_YOUNG) {
                if (mYoungList.size() != 0) {
                    for (int i = 0; i < mYoungList.size(); i++) {
                        boolean isSuccess = mOverlayManager.setEnabledExclusive(mYoungList.get(i), true, 0);
                        if (!isSuccess) {
                            changeThemeResult.add(isSuccess);
                            changeResultFail.add(mYoungList.get(i));
                        }
                    }
                    Log.d(TAG, "---mYoungList.get(i):" + mYoungList.toString());
                }
            } else if (themeChange == SettingConfig.THEME_GOLD) {
                if (mRed1List.size() != 0) {
                    for (int i = 0; i < mRed1List.size(); i++) {
                        boolean isSuccess = mOverlayManager.setEnabled(mRed1List.get(i), false, 0);
                        if (!isSuccess) {
                            changeThemeResult.add(isSuccess);
                            changeResultFail.add(mRed1List.get(i));
                        }
                    }
                    Log.d(TAG, "---mRed1List.get(i):" + mRed1List.toString());
                }
                if (mYoungList.size() != 0) {
                    for (int i = 0; i < mYoungList.size(); i++) {
                        boolean isSuccess = mOverlayManager.setEnabled(mYoungList.get(i), false, 0);
                        if (!isSuccess) {
                            changeThemeResult.add(isSuccess);
                            changeResultFail.add(mYoungList.get(i));
                        }
                    }
                    Log.d(TAG, "---mYoungList.get(i):" + mYoungList.toString());
                }
            }

            if (changeThemeResult != null && changeThemeResult.size() > 0
                    && changeResultFail != null && changeResultFail.size() > 0) {
                Log.d(TAG, "---changeThemeResult:" + changeThemeResult.toString());
                Log.d(TAG, "---changeResultFail:" + changeResultFail.toString());
            } else {
                Log.d(TAG, "---changeThemeResult   无");
                Log.d(TAG, "---changeResultFail    无");
            }

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

总结一下,留着以后用。

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

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

相关文章

apk反编译修改教程系列-----超简单修改apk中名称 包名 布局文本以及其中的文字选项 手机设置中apk对应修改演示【三十三】

💝💝💝在反编译apk中,每个初学者可能最感兴趣入门的就是修改包名 去更新以及其中选项文本的修改。这样循序渐进来激发学习的兴趣。了解一些apk中常见的修改方法。对于修改手机rom中的 系统类等等的apk原理都是一样的。这篇是应粉丝需要的修改apk基础教程. 通过博文了解…

02HBuilder工具准备

一、下载网址&#xff1a; HBuilder 使用教程 | 菜鸟教程 DCloud - 数字天堂官网、HBuilderX、HBuilder、uni-app、uniapp、5、5plus、mui、wap2app、流应用、HTML5、小程序开发、跨平台App、多端框架 二、下载 三、解压 四、启动程序

记一个framebuffer显示混乱的低级错误

记一个framebuffer显示混乱的低级错误 由于framebuffer的基础知识不扎实&#xff0c;这个任务上我多卡了两天&#xff0c;差点把我搞死&#xff0c;于此记录为后鉴。 打算用awtk做一个多进程项目&#xff0c;计划把framebuffer的内容通过websocket输出到浏览器上去显示画面, …

C++---入门

C补充了上的不足&#xff0c;使用各方面更加便捷&#xff0c;在C的基础上面向对象进行编译&#xff0c;学过C之后&#xff0c;对学习C的一定的帮助。 命名空间&#xff1a; namesp的定义&#xff1a;定义命名空间&#xff0c;需要使⽤到namespace关键字&#xff0c;后⾯跟命…

boost电路的同步和异步模式 及CCM、DCM模式 介绍

一、同步 异步 1.一般区别 电路结构 异步升压 IC&#xff1a;采用传统的 Boost 电路结构&#xff0c;主要由电感、开关管、二极管和输出电容等组成。同步升压 IC&#xff1a;与异步升压 IC 的 Boost 电路相比&#xff0c;其将二极管替换成了一个同步整流 MOS 管&#xff0c;…

yolov7源码解读1-训练前准备

一、怎么解决图片输入尺度不统一的问题 YOLOv7的矩形训练是指在训练时对输入图片进行尺寸调整&#xff0c;以提高模型处理长宽比差异较大的图片时的性能&#xff0c;同时避免过多的图像变形。具体来说&#xff0c;以下是矩形训练的处理过程&#xff1a; 1. 矩形训练的核心目标…

用梗营销来启动市场

目录 为什么梗营销适合初创公司 有效的梗营销技巧 梗不仅仅是有趣的图片&#xff0c;它们是包裹在幽默中的文化时刻。对于小企业家&#xff08;以及大企业家&#xff09;&#xff0c;梗代表了一种强大且性价比高的市场推广方式。让我们分解一下为什么梗营销有效&#xff0c;以…

职业院校人工智能实验室解决方案

随着人工智能技术的迅猛发展&#xff0c;企事业单位对具备高素质技术应用能力的人才需求愈发迫切&#xff0c;目前人工智能已经逐步从感知理解阶段转变为生成创造阶段&#xff0c;可以为各行各业提供多维的智能化应用服务。2024年的《政府工作报告》中首次提出了“人工智能”行…

java之集合(详细-Map,Set,List)

1集合体系概述 1.1集合的概念 集合是一种容器&#xff0c;用来装数据的&#xff0c;类似于数组&#xff0c;但集合的大小可变&#xff0c;开发中也非常常用。 1.2集合分类 集合分为单列集合和多列集合 Collection代表单列集合&#xff0c;每个元素&#xff08;数据&#xff…

ansible自动化运维(一)简介及清单,模块

相关文章ansible自动化运维&#xff08;二&#xff09;playbook模式详解-CSDN博客ansible自动化运维&#xff08;三&#xff09;jinja2模板&&roles角色管理-CSDN博客ansible自动化运维&#xff08;四&#xff09;运维实战-CSDN博客 ansible自动化运维工具 1.什么是自…

期末复习-编译原理

一、引论 1.编译、翻译、解释的概念&#xff08;掌握&#xff09; 翻译程序&#xff1a;将一种语言程序&#xff08;源&#xff09;转换成另一种语言程序&#xff08;目标&#xff09;&#xff0c;两者在逻辑上是等价的。 如果源语言是高级语言&#xff0c;目标语言是低级语言…

pytorch bilstm crf的教程,注意 这里不支持批处理,要支持批处理 用torchcrf这个。

### Bi-LSTM Conditional Random Field ### pytorch tutorials https://pytorch.org/tutorials/beginner/nlp/advanced_tutorial.html ### 模型主要结构&#xff1a; ![title](sources/bilstm.png) pytorch bilstm crf的教程&#xff0c;注意 这里不支持批处理 Python version…

docker安装、升级、以及sudo dockerd --debug查看启动失败的问题

1、docker安装包tar下载地址 Index of linux/static/stable/x86_64/ 2、下载tgz文件并解压 tar -zxvf docker-24.0.8.tgz 解压后docker文件夹下位docker相关文件 3、将老版本docker相关文件&#xff0c;备份 将 /usr/bin/docker下docker相关的文件&#xff0c;mv到备份目录…

hive—炸裂函数explode/posexplode

1、Explode炸裂函数 将hive某列一行中复杂的 array 或 map 结构拆分成多行&#xff08;只能输入array或map&#xff09; 语法&#xff1a; select explode(字段) as 字段命名 from 表名; 举例&#xff1a; 1&#xff09;explode(array)使得结果中将array列表里的每个元素生…

【Android学习】RxJava

文章目录 资料连接1. Merge & Zip操作符: 合并数据源2. Map & FlapMap & ConcatMap & Buffer: 变换操作符3. retry & retryUntil & retryWhen : 错误处理操作符4. Transformer & Compose 转换符 资料连接 Android RxJava&#xff1a; 这是一份全面…

浅谈Kubernetes(K8s)之RC控制器与RS控制器

1.RC控制器 1.1RC概述 Replication Controller 控制器会持续监控正在运行的Pod列表&#xff0c;并保证相应类型的Pod的数量与期望相符合&#xff0c;如果Pod数量过少&#xff0c;它会根据Pod模板创建新的副本&#xff0c;反之则会删除多余副本。通过RC可实现了应用服务的高可用…

直流开关电源技术及应用

文章目录 1. 开关电源概论1.1 开关电源稳压原理1.1.1 开关电源稳压原理核心组成部分及其作用工作过程稳压原理 1. 开关电源概论 1.1 开关电源稳压原理 为了提高效率&#xff0c;必须使功率调整器件处于开关工作状态。 作为开关而言&#xff0c;导通时压降很小&#xff0c;几乎…

解决 MyBatis 中空字符串与数字比较引发的条件判断错误

问题复现 假设你在 MyBatis 的 XML 配置中使用了如下代码&#xff1a; <if test"isCollect ! null"><choose><when test"isCollect 1">AND exists(select 1 from file_table imgfile2 where task.IMAGE_SEQimgfile2.IMAGE_SEQ and im…

如何windows命令行使用kali?ssh连接高效又快捷

一、打开虚拟机kali进入cmd中 输入vim /etc/ssh/sshd_config&#xff0c;&#xff08;注意这里需要使用root权限&#xff09; 二、进入编辑文件页面 找到PermitRootLogin prohibit-password和 PasswordAuthentication no两行 将“prohibit-password”修改为“yes”&#xff0…

自动化测试之单元测试框架

单元测试框架 一、单元测试的定义 1&#xff1a;什么是单元测试&#xff1f; 还记不记得我们软件测试学习的时候&#xff0c;按照定义&#xff1a;单元测试就是对单个模块或者是单个函数进行测试&#xff0c;一般是开发做的&#xff0c;按照阶段来分&#xff0c;一般就是单元…