转:http://vbill.github.io/2015/02/12/Xposed-replacing-resources/
这是Xposed在github上的最后一篇教程。原文地址
Xposed使得替换资源,比如:图像和字符串之类的事变得容易。下面是做法:
这就是“简单”的替换,通过这种方式你可以直接替换值。这种方式可以用于:Boolean, Color, Integer, int[], String and String[]。
如你所见,有几种不同的设置替换资源的方式。对于构成安卓框架一部分的所有地方都应该被替换的资源(对所有的app都可用),你应在initZygote中调用XResources.setSystemWideReplacement(...)方法。对于特定app的资源,你需要核实过你确实在正确的app之后在hookInitPackageResources中调用res.setReplacement。这时你不应使用setSystemWideReplacement因为可能会产生你无法预料的副作用。
替换Drawable也采用相似的办法。然而你不能只使用Drawable作为替换物,因为这可能导致同一个Drawable实例被不同的ImageViews引用。因此,你需要使用包装器:
resparam.res.setReplacement("com.android.systemui", "drawable", "status_bar_background", new XResources.DrawableLoader() { @Override public Drawable newDrawable(XResources res, int id) throws Throwable { return new ColorDrawable(Color.WHITE); } });更加复杂的资源(比如动画类型的Drawable)必须从你的模块资源当中被引用。我们假设你想要替换电池图标。下面是代码:
package de.robv.android.xposed.mods.coloredcirclebattery; import android.content.res.XModuleResources; import de.robv.android.xposed.IXposedHookInitPackageResources; import de.robv.android.xposed.IXposedHookZygoteInit; import de.robv.android.xposed.callbacks.XC_InitPackageResources.InitPackageResourcesParam; public class ColoredCircleBattery implements IXposedHookZygoteInit, IXposedHookInitPackageResources { private static String MODULE_PATH = null; @Override public void initZygote(StartupParam startupParam) throws Throwable { MODULE_PATH = startupParam.modulePath; } @Override public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { if (!resparam.packageName.equals("com.android.systemui")) return; XModuleResources modRes = XModuleResources.createInstance(MODULE_PATH, resparam.res); resparam.res.setReplacement("com.android.systemui", "drawable", "stat_sys_battery", modRes.fwd(R.drawable.battery_icon)); resparam.res.setReplacement("com.android.systemui", "drawable", "stat_sys_battery_charge", modRes.fwd(R.drawable.battery_icon_charge)); } }你可以随意命名你的替换资源。我选择 batterry_icon 替代 stat_sys_battery 让它们在本文中更好区分。
之后把 “battery_icon” 和 “battery_icon_charge” 这两个Drawable添加到你的模块当中。最简单的情况是添加 “res/drawables/battery_icon.png” 和 “res/drawables/battery_icon_charge.png”。但是你可以使用Android提供的所有方式来定义资源。所以对于动画图标,你可使用带有animation-list和其它Drawable资源引用的XML文件。当然这个XML文件也必须放在你的模块内。
通过这些替换,你能要求Xposed将所有指向特定资源的请求跳到你自己的模块。这同样意味着你可以利用qualifier,比如:如果你对landscape或更低的屏幕密度需要不同的资源。翻译也可以以相同的方式提供。同样,你也许需要这么做如果原来的资源使用qualifier。你不能仅仅替换一段文字的西班牙语版本。正如前面提到的,请求是向前的,所以它会完全被你的模块资源处理,并且不会察觉其它翻译的存在。
这个技巧基本上对所有资源类型都有效。除了极个别的像主题之类的。
尽管理论上你可以用之前提到的技巧彻底替换布局,但这有很多坏处。你必须从原有布局中复制整个布局,这会降低对其它ROM的兼容性。主题也许回丢失。只有一个模块可以代替布局。如果两个模块都尝试这么做,后者将会胜出。最重要的是,其它资源的ID和引用是很难定义的。因此,我不推荐这样做。
作为一种好的选择,你可以使用后填充的hook。下面是你怎么做:
@Override public void handleInitPackageResources(InitPackageResourcesParam resparam) throws Throwable { if (!resparam.packageName.equals("com.android.systemui")) return; resparam.res.hookLayout("com.android.systemui", "layout", "status_bar", new XC_LayoutInflated() { @Override public void handleLayoutInflated(LayoutInflatedParam liparam) throws Throwable { TextView clock = (TextView) liparam.view.findViewById( liparam.res.getIdentifier("clock", "id", "com.android.systemui")); clock.setTextColor(Color.RED); } }); }每当“status_bar”布局被填充时,回调方法handleLayoutInflated就被调用。在你以参数形式得到的LayoutInflatedParam对象中,你可以找到刚被创建的View并在需要时修改它。你同样会得到resNames用来鉴别这个方法因哪个布局而被调用(万一你对多个布局使用同一种方法),还有variant,比如可能包含layout-land,如果它就是被加载的布局的版本的话。res帮助你以布局从同一个来源得到ID或额外的资源。
分享到 評論