热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

怎么在Android应用中实现一个换肤功能

今天就跟大家聊聊有关怎么在Android应用中实现一个换肤功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根

今天就跟大家聊聊有关怎么在Android 应用中实现一个换肤功能,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。

Android换肤技术总结

背景

纵观现在各种Android app,其换肤需求可以归为

- 白天/黑夜主题切换(或者别的名字,通常2套),如同花顺/自选股/天天动听等,UI表现为一个switcher。
- 多种主题切换,通常为会员特权,如QQ/QQ空间。

对于第一种来说,目测应该是直接通过本地theme来做的,即所有图片/颜色的资源都在apk里面打包了。

而对于第二种,则相对复杂一些,由于作为一种线上服务,可能上架新皮肤,且那么多皮肤包放在apk里面实在太占体积了,所以皮肤资源会在选择后再进行下载,也就不能直接使用android的那套theme。

技术方案

内部资源加载方案和动态下载资源下载两种。

动态下载可以称为一种黑科技了,因为往往需要hack系统的一些方法,所以在部分机型和新的API上有时候可能有坑,但相对好处则很多

- 图片/色值等资源由于是后台下发的,可以随时更新
- APK体积减小
- 对应用开发者来说,换肤几乎是透明的,不需要关心有几套皮肤
- 可以作为增值服务卖钱!!

内部资源加载方案

内部资源加载都是通过android本身那套theme来做的,相对业务开发来说工作量更大(需要定义attr和theme),不同方案类似地都是在BaseActivity里面做setTheme,差别主要在解决以下2个问题的策略:

- setTheme后如何实时刷新,而不用重新创建页面(尤其是listview里面的item)。
- 哪些view需要刷新,刷新什么(背景?字体颜色?ImageView的src?)。

自定义view

MultipleTheme

做自定义view是为了在setTheme后会去立即刷新,更新页面UI对应资源(如TextView替换背景图和文字颜色),在上述项目中,则是通过对rootView进行遍历,对所有实现了ColorUiInterface的view/viewgroup进行setTheme操作来实现即使刷新的。

显然这样太重了,需要把应用内的各种view/viewgroup进行替换。

手动绑定view和要改变的资源类型

Colorful

这个…我们看看用法吧….

ViewGroupSetter listViewSetter = new ViewGroupSetter(mNewsListView);
// 绑定ListView的Item View中的news_title视图,在换肤时修改它的text_color属性
listViewSetter.childViewTextColor(R.id.news_title, R.attr.text_color);

// 构建Colorful对象来绑定View与属性的对象关系
mColorful = new Colorful.Builder(this)
  .backgroundDrawable(R.id.root_view, R.attr.root_view_bg)
  // 设置view的背景图片
  .backgroundColor(R.id.change_btn, R.attr.btn_bg)
  // 设置背景色
  .textColor(R.id.textview, R.attr.text_color)
  .setter(listViewSetter) // 手动设置setter
  .create(); // 设置文本颜色

动态资源加载方案

resource替换

覆盖application的getResource方法,实现自己的resource,优先加载本地皮肤包文件夹下的资源包,对于性能问题,可以通过attribute或者资源名称规范(如需要换肤则用skin_开头)来优化,从而不对不换肤的资源进行额外检查开销。

不过由于Android5.1源码里,drawable初始化的时候调用的是loadDrawable,而不是resource.getDrawable,而loadDrawable是私有的方法,无法覆盖,所以虽然很方便,却无法继续使用(不用关心任何皮肤相关的事情,android:color指定颜色就行了,神奇滴会自动换肤)。

自定义LayoutInflator.Factory

开源项目可参照Android-Skin-Loader。

即setFactory使用自定义的LayoutInflator.Factory,可以重点关注该项目中的SkinInflaterFactory和SkinManager(实现了自己的getColor、getDrawable、getBitmap、getColorStateList等等方法)。

需要自定义一个tag比如app:customStyle,重写所有的style,转成set方法,这样带来的牺牲就是增加了换肤的成本,要写很多style,自己去set,并不完全透明了。

Hack Resources internally

黑科技方法,直接对Resources进行hack,Resources.Java:

// Information about preloaded resources. Note that they are not
// protected by a lock, because while preloading in zygote we are all
// single-threaded, and after that these are immutable.
private static final LongSparseArray[] sPreloadedDrawables;
private static final LongSparseArray sPreloadedColorDrawables
  = new LongSparseArray();
private static final LongSparseArray sPreloadedColorStateLists
  = new LongSparseArray();

直接对Resources里面的这三个LongSparseArray进行替换,由于apk运行时的资源都是从这三个数组里面加载的,所以只要采用interceptor模式:

public class DrawablePreloadInterceptor extends LongSparseArray

自己实现一个LongSparseArray,并通过反射set回去,就能实现换肤,具体getDrawable等方法里是怎么取preload数组的,可以自己看Resources的源码。

等等,就这么简单?,NONO,少年你太天真了,怎么去加载xml,9patch的padding怎么更新,怎么打包/加载自定义的皮肤包,drawable的状态怎么刷新,等等。这些都是你需要考虑的,在存在插件的app中,还需要考虑是否会互相覆盖resource id的问题,进而需要修改apt,把resource id按位放在2个range。

总结

尽管动态加载方案比较黑科技,可能因为系统API的更改而出问题,但相对来说

好处有

- 灵活性高,后台可以随时更新皮肤包
- 相对透明,开发者几乎不用关心有几套皮肤,不用去定义各种theme和attr,甚至连皮肤包的打包都可以交给设计或者专门的同学
- apk体积节省

存在的问题

没有完善的开源项目,如果我们采用动态加载的第二种方案,需要的项目功能包括:

- 自定义皮肤包结构
- 换肤引擎,加载皮肤包资源并load,实时刷新。
- 皮肤包打包工具
- 对各种rom的兼容

如果有这么一个项目的话,就一劳永逸了,有兴趣的同学可以联系一下,大家一起搞一搞。

内部加载方案大同小异,主要解决的都是即时刷新的问题,然而从目前的一些开源项目来看,仍然没有特别简便的方案。让我选的话,我宁愿让界面重新创建,比如重启activity,或者remove所有view再添加回来(或者你可能想遍历rootview,然后一个个检查是否需要换肤然后set…)。

看完上述内容,你们对怎么在Android 应用中实现一个换肤功能有进一步的了解吗?如果还想了解更多知识或者相关内容,请关注编程笔记行业资讯频道,感谢大家的支持。


推荐阅读
  • 优化ListView性能
    本文深入探讨了如何通过多种技术手段优化ListView的性能,包括视图复用、ViewHolder模式、分批加载数据、图片优化及内存管理等。这些方法能够显著提升应用的响应速度和用户体验。 ... [详细]
  • 本文将介绍如何编写一些有趣的VBScript脚本,这些脚本可以在朋友之间进行无害的恶作剧。通过简单的代码示例,帮助您了解VBScript的基本语法和功能。 ... [详细]
  • 本文探讨了Hive中内部表和外部表的区别及其在HDFS上的路径映射,详细解释了两者的创建、加载及删除操作,并提供了查看表详细信息的方法。通过对比这两种表类型,帮助读者理解如何更好地管理和保护数据。 ... [详细]
  • 1.如何在运行状态查看源代码?查看函数的源代码,我们通常会使用IDE来完成。比如在PyCharm中,你可以Ctrl+鼠标点击进入函数的源代码。那如果没有IDE呢?当我们想使用一个函 ... [详细]
  • 本文详细介绍了macOS系统的核心组件,包括如何管理其安全特性——系统完整性保护(SIP),并探讨了不同版本的更新亮点。对于使用macOS系统的用户来说,了解这些信息有助于更好地管理和优化系统性能。 ... [详细]
  • ASP.NET MVC中Area机制的实现与优化
    本文探讨了在ASP.NET MVC框架中,如何通过Area机制有效地组织和管理大规模应用程序的不同功能模块。通过合理的文件夹结构和命名规则,开发人员可以更高效地管理和扩展项目。 ... [详细]
  • 本文详细介绍如何在IntelliJ IDEA 14中打包Android应用APK文件,并提供查询SHA1值的具体步骤。 ... [详细]
  • 本文详细介绍了如何在现有的Android Studio项目中集成JNI(Java Native Interface),包括下载必要的NDK和构建工具,配置CMakeLists.txt文件,以及编写和调用JNI函数的具体步骤。 ... [详细]
  • 本文介绍了在Android项目中实现时间轴效果的方法,通过自定义ListView的Item布局和适配器逻辑,实现了动态显示和隐藏时间标签的功能。文中详细描述了布局文件、适配器代码以及时间格式化工具类的具体实现。 ... [详细]
  • 深入解析Android自定义View面试题
    本文探讨了Android Launcher开发中自定义View的重要性,并通过一道经典的面试题,帮助开发者更好地理解自定义View的实现细节。文章不仅涵盖了基础知识,还提供了实际操作建议。 ... [详细]
  • 本文详细介绍了如何使用 Yii2 的 GridView 组件在列表页面实现数据的直接编辑功能。通过具体的代码示例和步骤,帮助开发者快速掌握这一实用技巧。 ... [详细]
  • 解决微信电脑版无法刷朋友圈问题:使用安卓远程投屏方案
    在工作期间想要浏览微信和朋友圈却不太方便?虽然微信电脑版目前不支持直接刷朋友圈,但通过远程投屏技术,可以轻松实现在电脑上操作安卓设备的功能。 ... [详细]
  • 高效解决应用崩溃问题!友盟新版错误分析工具全面升级
    友盟推出的最新版错误分析工具,专为移动开发者设计,提供强大的Crash收集与分析功能。该工具能够实时监控App运行状态,快速发现并修复错误,显著提升应用的稳定性和用户体验。 ... [详细]
  • 本文介绍了两种方法,用于检测 Android 设备是否开启了开发者模式。第一种方法通过检查 USB 调试模式的状态,第二种方法则直接判断开发者选项是否启用。这两种方法均提供了代码示例和详细解释。 ... [详细]
  • Python 异步编程:ASGI 服务器与框架详解
    自 Python 3.5 引入 async/await 语法以来,异步编程迅速崛起,吸引了大量开发者的关注。本文将深入探讨 ASGI(异步服务器网关接口)及其在现代 Python Web 开发中的应用,介绍主流的 ASGI 服务器和框架。 ... [详细]
author-avatar
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有