Android 动态加载 layout 资源
来源:Android进阶开发     阅读:755
灰太狼
发布于 2019-01-26 01:55
查看主页

在阅读前,请确保你已经理解动态加载资源的基本知识,相关知识请阅读 《从高德 SDK 学习 Android
动态加载资源》https://www.songma.com/p/d8bf5b79b1ce

因为 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。

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

以上Android资料以及更多Android相关资料及面试经验可在QQ群里获取:936903570。有加群的朋友请记得备注上简书,谢谢

免责声明:本文为用户发表,不代表网站立场,仅供参考,不构成引导等用途。 系统环境 服务器应用
相关推荐
我对tableView的少量了解
HTML、CSS、JavaScript、浏览器大战的前世今生
iOS-谈一谈自适应Cell的高度缓存
教程-如何用travis-ci实现自动化部署前台项目
人工智能_数据分析_信号_心理学_生物学等重要术语: ROC接收者操作特征曲线
首页
搜索
订单
购物车
我的