主题更换的实现实录

主题更换的实现实录

  1. 主题更换的的实现方案。

采用截断请求,资源重定位的方式来达到更换系统全套资源的目的。

  1. 实现思路及过程:

通过分析andrid里面setting的语言切换机制,决定采用Configurationskin属性的改变对应用户切换操作,并将由用户操作引起Configurationskin的变化值传到ActivityManagerService里,并把变化值传给Resources对象,Resources对象根据configurationskin值做资源的重定位加载。这样做的好处是:系统已经做好了Configuration的变化响应事件,不用开发者去理会,应用的重启并重新加载资源。


2.1用户接口的设计

对于用户来讲,需要设计一个用户接口,这里写写个简单的主题切换管理工具,用户可以通过对apk的操作,改变系统样式,具体界面简单设计如下:

 图中的defaultTheme1分别代表了两个不同的主题,放在一个listView中,数据的来源是通过解析我指定目录下的Theme包。当用户点击Theme1,让系统重新去加载我自定义的主题包中的资源。

2.2资源包格式设定

对应要换资源的apk,在自己定义的Theme目录下提供相同包名的目录文件,drawable资源保持跟原始apk相同的路径及资源名,另外在自定义的目录提供统一其他资源的文件,文件名字统一命名为“theme_values.xml”,这个文件提供除drawable资源以外的其他资源。

例如:

   <Boyue_Theme_Values>

<colorname="divider_color">#00ff00</color>

<colorname="testColor">#ff0000</color>

<stringname="app_name">BoyueTheme1</string>

<stringname="hello_world">Hello world!1</string>

<stringname="action_settings">Settings1</string>

<stringname="default_name">Default1</string>

<stringname="custom_theme_name">New [%1$s]1</string>

<stringname="loading">Loading...1</string>

<stringname="test"> hello world1</string>

<dimenname="activity_horizontal_margin">160dp</dimen>

<dimenname="activity_vertical_margin">160dp</dimen>

<dimenname="textsize">40sp</dimen>

</Boyue_Theme_Values>

 


  1. 3 对framework进行修改,截断资源请求,实现资源请求的重定位。

这里主要修改Resources,AssetManager,TypedArray.

注意:Resources中获取资源的方法,有些只针对手写的代码生效,对于xmlview的属性的资源的解析在TypedArray中。



  1. 编码实现。

App:

1.指定theme的存放目录,并解析指定目录下的Theme数,并存放到contentProvider中。(contentProvider的设计是因为原来主题包采用的是apk的方式,为了不同进程之间的共享设定,现在没有必要)

2.查询指定URIcontentProviderTheme数据添加到用户接口ListView中。

3.listview添加Item的点击事件,并将用户点击的Item数据进行一定的数据整理,将用户的点击的Item对应的Theme的全路径作为skin,并把变化的skin传到ActivityManagerService中进行处理。


Framework:

  1. 判断当前应用的Configurationskin的值是否为系统的默认值,如果不是加载skin(用户选择Theme)目录下的对应应用的资源(包括:color,string,dimens,保存到3个不同HashMap中。另外要注意一点:当传过来的skin与当前的skin不同时,需要清空原来的缓存的map,一遍让3map加载最新传进来的skin的资源。

  2. 截断请求,对于get资源方法的修改,当get通过id获取到原有资源时,判断skin是否已经改变了,如已改变,让他加载skin目录下自己包下的资源达到资源切换。

根据以上步骤只能完成的效果基本上是:

资源只有在代码中明确指示用Resourcesget资源的方法得到时,才能更换成我们想要的资源。可能产生的问题有:

  1. xml里面view子节点的属性的值,不同通过修改Resources类的资源请求方法达到重定位。
    2.launcher
    里面的应用的icon不能实时更新(这里基本可以确定应用是有重新启动去加载新的资源,但惟有launcher没有效果)


下面继续:

问题一的解决思路:

通过测试,只有在.java代码中明确view的资源加载,resources类写的重定位资源的方法才能生效。而界面布局xml里面配置的属性值却不能。这就需要去了解界面布局xml的解析以及xml下各个节点view的创建和初始化过程,xml的解析详细了解可以去看罗升阳-android应用程序资源的查找过程看完之后你可能只是能了解到xml的解析及各个节点view的创建,在LayoutInflater中有“return(View) constructor.newInstance(args);”这里他结束了分析。但是如果要实现资源的替换,我们还必须继续深究,最后这句话的意思其实就是view对象通过args参数描述的属性值进行初始化。了解这个我们就可以明白自定义的View必须传递两个参数contextAttributeSet这两个参数了.接着我们就可以去找带上面两个参数的view的构造函数了,这里我们需要了解android

View的继承关系如下图所示:

Android各个view之间的关系清晰明了。

回到找view构造函数的过程:我们可以找到下面这段代码:


finalint N = a.getIndexCount();

for(int i = 0; i < N; i++) {

intattr = a.getIndex(i);

switch(attr) {

casecom.android.internal.R.styleable.View_background:

background= a.getDrawable(attr);

break;

casecom.android.internal.R.styleable.View_padding:


由于代码过长,我就截取了一小部分,读者可以自己找到这段代码玩下看。这里主要是显示view类处理了哪些属性值,这里是没有我们需要改变的属性的,读者看完就会知道,我们对于高级属性像textColor,TextSize是基于TextView这层,找到TextView,可以很快定位到两个参数的构造方法,下面就以textSizt为例:

找到TextView的构造函数,可以很快定位到下面这段代码:

casecom.android.internal.R.styleable.TextView_textSize:

textSize= a.getDimensionPixelSize(attr, textSize);

break;

A是一个TypedArray对象,通过调用getDimensionPixelSize()来获取xmL里面属性的设置的值。通过这段代码我们就明白,view创建的属性的加载是TypedArray类来管理的,找到TypedArray对应的方法进行自己的逻辑代码处理即可了。


问题二的解决思路:

  1. 在传递Configuration.skin这个变量值到ActivityManagerServiceupdateConfiguration方法中这里处理configuration的改变情况.按照这个思路修改完framework后,各个app都会重新启动,但是launcherapp由于对Iconlabel有缓存,所以需要判断当Configuration.skin改变后要清除其缓存。

在修改framework上有个地方没有理解,附上代码:

  1. acitiviytManagerservice类中updateConfigurationLocked方法最后发送的广播的意图。以上没弄懂的地方不影响整个系统的执行过程.所以

总的情况就是当应用的Configuration对象的值有发生变化,即更新Configuration,并重新启动app,加载资源.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值