android 动态添加资源,Android 动态加载 layout 资源

由于 LayoutInflater 在 inflate 的过程中会调用 Context 的 getResources()、getTheme() 方法,所以在进行动态加载 layout 资源前,我们需要修改上述方法的返回值。根据不同的情况,存在两种修改方法。注:getTheme() 的返回值不一定要修改,只是出于各种考虑,建议将 getTheme() 也一并修改。视个人情况而定吧……

如果宿主 apk 是我们自己控制的,那么可以直接重写 getResources()、getTheme() 方法,使宿主 Activity 直接返回客体 apk 的资源以及 Theme。public class HostActivity extends Activity {

Resources mPluginRes;

Resources.Theme mPluginTheme;    @Override

public Resources getResources() {        return mPluginRes != null ? mPluginRes : super.getResources();

}    @Override

public Resources.Theme getTheme() {        return mPluginTheme != null ? mPluginTheme : super.getTheme();

}

}

特殊情况下,如果宿主 apk 不在我们控制范围内,我们只能通过反射达到目的。public class ClientView extends View {

Theme mTheme;

Theme mOriginTheme;

Resource mResources;

Resource mOriginResources;

Field mResourcesField;

Field mThemeField;

Activity mHostActivity;

// do something ...

private void setup() {        // initialize mResources ...

mTheme = mResources.newTheme();        // 如果需要使用其他 Theme 只需将 '.' 改为 '_',例 Theme.DeviceDefault 对应的变量名为 Theme_DeviceDefault

mTheme.applyStyle(Class.forName("com.android.internal.R$style").getDeclaredField("Theme").getInt(null), true);

setupField();

inflate();

}

private void setupField() {

mThemeField = Class.forName("android.view.ContextThemeWrapper").getDeclaredField("mTheme");

mThemeField.setAccessible(true);

mResourcesField = Class.forName("android.app.ContextImpl").getDeclaredField("mResources");

mResourcesField.setAccessible(true);

}

private void inflate() {

beforeInflate();

// inflating ...

afterInflate();

}

/**

* 在 inflate 前,由于 LayoutInflater 需要调用 Context 的 getResources() 以及 getTheme()

* 方法,所以我们需要反射修改对应的变量的值

*/

private void beforeInflate() {

Context baseContext = mHostActivity.getBaseContext();

mOriginResources = (Resources) mResourcesField.get(baseContext);

mOriginTheme = (Theme) mThemeField.get(mHostActivity);

mResourcesField.set(baseContext, mResources);

mThemeField.set(mHostActivity, mTheme);

}

/**

* 在 inflate 后,因为宿主之后也需要使用到自身的 getResources() 以及 getTheme()

* 所以我们在执行完 inflate 后需要还原这两个变量的值

*/

private void afterInflate() {

mResourcesField.set(baseContext, mOriginResources);

mThemeField.set(mHostActivity, mOriginTheme);

}

}

总结

一般的动态资源加载场景中,宿主 apk 以及客体 apk 都是遵从某种守则进行开发的,此时方法一即可满足动态加载 layout 的需求。

方法二的应用场景则是以假设宿主不可能满足条件而使用,比较典型的就是高德地图以及百度地图的导航 SDK。这两家提供的都只是 jar 包,而且不能依赖开发者重写自己 app 的 Context 对象的 getResources()、getTheme() 方法,所以只能通过反射来达到 inflate 自定义 View。

喜欢的话请帮忙转发一下能让更多有需要的人看到吧,有些技术上的问题大家可以多探讨一下。

AAffA0nNPuCLAAAAAElFTkSuQmCC

AAffA0nNPuCLAAAAAElFTkSuQmCC

作者:Android进阶开发

链接:https://www.jianshu.com/p/93777d7cc49c

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值