最近在开发中一直被这个问题困扰,排查了一天,摸索出来两种规律。
一 主题要使用AppCompat系列的,也就是Theme.AppCompat及其衍生的主题
二 加载资源文件时的上下文要注意。作者使用activity的getApplicationContext时就报错,但是使用getBaseContext加载就正常。目前问题得以解决,但是原理还未研究清楚。
更新20180802
随着项目的进展,个人管理模块的重构接近尾声,现在回头再看一下这个问题,又有了新的认识。
大部分XML inflate的报错,都是加载这个xml报错所指行数所用的控件引起的。有的可能是自定义view,有的可能是官方新出的控件如MD系列的coordinatorLayout等。
还是回归到本人所做的项目来谈。在使用协调者布局时,加载XML文件报错。其原因在于加载该布局的Activity的主题里缺少了某些协调者布局用到的属性或者参数。协调者布局在初始化时,由于找不到该属性或参数,可能引起空指针或者其他异常。这时候就需要我们把这些属性或参数补充上。常见的处理方法有两种。
一、使用官方支持的主题
如Theme.AppCompat系列主题,这类主题一般兼容性都比较好。常见的如Theme.AppCompat.Light.NoActionBar,这个主题就是可以正常加载协调者布局的。
二、自定义主题
还是以协调者布局为例,我们可以阅读源码,来找到协调者布局初始化时都用到了那些属性和参数。在对照查看自己的所使用的主题里是不是没有这些参数。把这些属性参数设置到主题中,再次调试,协调者布局应该就可以正常加载了。
三、使用ContextThemeWrapper
这个东西真的太棒了。为什么?因为当你的Activity不光你自己的模块使用时,主题的修改牵扯出的问题可能比较多。比如最常见的各类电商的首页,底下有很多tab,都是fragment实现的不同页面功能。但是背后的Activity却是同一个。这个时候如果修改Activity的主题,涉及的页面和功能就太多了。那么怎么办呢?这里就可以使用ContextThemeWrapper了。它是针对加载布局时,使用的上下文Context,进行了一次封装,把加载时的Context的主题动态改变了。这个类带来的好处不言而喻。我们可以通过它,在不修改Activity主题的前提下,只针对某一页面模块,使用其他主题来实现我们的需求。使用方法也很简单,如下所示。
Context context = new ContextThemeWrapper(activity, R.style.your_theme);
其中activity是你当前加载时用的上下文,后面的主题是你想要使用的主题。
之后再用这个context作为参数,通过LayoutInflater.from(activity).cloneInContext(context).inflate(R.layout.your_layout, null)的方式来加载布局,就能够正确加载了。
额外补充一点。在一些插件化开发的项目中,有时候两个不同插件之间的调用加载模块,如果不使用全局的上下文getApplicationContext,可能会找不到资源,从而引起报错。
写的可能有些乱,最后总结一下。XML加载的报错,可以从两个方面去排查,一是主题的使用,看看是不是缺少某些属性或参数。二是上下文的使用,使用当前Activity的上下文如果找不到资源,可以试试全局的整个APP的上下文,应该就能找到了。