由于使用 MaterialComponents 的 ThemeEnforcement 错误,偶尔会出现 InflateException。怎么解决?
Occasional InflateException due to ThemeEnforcement errors using MaterialComponents. How to solve?
在花了一整天试图解决这个问题之后,我几乎可以肯定这是 Material 组件库中的一个错误。以下是我配置的相关部分(删除了不相关的元素):
styles.xml:
<style name="MaterialAppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryVariant">@color/colorPrimary900</item>
<item name="colorSecondary">@color/colorSecondary</item>
<item name="colorSecondaryVariant">@color/colorSecondary900</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
AndroidManifest:
<application
android:name=".AndroidApplication"
android:theme="@style/MaterialAppTheme">
主要活动:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/MaterialAppTheme">
依赖版本:
materialComponents: '1.1.0-alpha02',
constraintLayout : '2.0.0-alpha3',
appCompat : '1.1.0-alpha01'
我在登录页面添加 Material 按钮没有问题。问题从我的 MainActivity 开始,在我的第一个列表片段中。尝试显示以 MaterialCardView 作为每个项目的根的回收站视图项目时,出现 ThemeEnforcement 错误。当我尝试添加 MaterialTextButton 时也是如此。这是我的 MaterialCardView xml 声明的样子:
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tripCard"
style="@style/Widget.MaterialComponents.CardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
card_view:cardBackgroundColor="@color/colorSurface"
card_view:cardCornerRadius="@dimen/widget_corner_radius"
card_view:cardElevation="@dimen/card_elevation">
在 MaterialComponents 库中设置断点时,我注意到它有时无法验证主题。以下是库的一些代码,显示了失败的地方(在 ThemeEnforcement.java 中):
if (enforceMaterialTheme) {
TypedValue isMaterialTheme = new TypedValue();
boolean resolvedValue =
context.getTheme().resolveAttribute(R.attr.isMaterialTheme, isMaterialTheme, true);
if (!resolvedValue
|| (isMaterialTheme.type == TypedValue.TYPE_INT_BOOLEAN && isMaterialTheme.data == 0)) {
// If we were unable to resolve isMaterialTheme boolean attribute, or isMaterialTheme is
// false, check for Material Theme color attributes
checkMaterialTheme(context);
}
}
public static void checkMaterialTheme(Context context) {
checkTheme(context, MATERIAL_CHECK_ATTRS, MATERIAL_THEME_NAME);
}
private static final int[] MATERIAL_CHECK_ATTRS = {R.attr.colorPrimaryVariant};
据我所知,Theme Enforcer 会尝试解析 isMaterialTheme 属性(它 有时 可以解析 尽管在这种情况下不是)然后它继续检查 material 主题颜色属性的存在(检查 colorPrimaryVariant)并再次失败(我也在我的应用程序主题中定义了它。)
我会继续提交错误,如果有人有解决方案或任何建议,我将不胜感激!
要跟进评论:使用从使用正确主题的 Context
检索到的 LayoutInflater
很重要。应用程序上下文不能满足此要求。
好消息是您不必担心将上下文注入适配器。这不是很明显,但适配器提供了您获得正确上下文所需的一切。
看看onCreateViewHolder()
骨架:
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// ...
}
所有 View
实例都有一个 getContext()
方法,并且 parent
参数保证为非空...因此您可以从父级检索上下文并使用获取 LayoutInflater:
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.my_item_view, parent, false);
return new MyViewHolder(itemView);
}
同样,在 ViewHolder
中,您可以使用相同的技术来获取上下文对象。每个 ViewHolder
实例都有一个保证为非空的 itemView
字段,因此您可以从中检索上下文。
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Context context = holder.itemView.getContext();
// ...
}
在花了一整天试图解决这个问题之后,我几乎可以肯定这是 Material 组件库中的一个错误。以下是我配置的相关部分(删除了不相关的元素):
styles.xml:
<style name="MaterialAppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryVariant">@color/colorPrimary900</item>
<item name="colorSecondary">@color/colorSecondary</item>
<item name="colorSecondaryVariant">@color/colorSecondary900</item>
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
AndroidManifest:
<application
android:name=".AndroidApplication"
android:theme="@style/MaterialAppTheme">
主要活动:
<activity
android:name=".MainActivity"
android:label="@string/app_name"
android:launchMode="singleTop"
android:theme="@style/MaterialAppTheme">
依赖版本:
materialComponents: '1.1.0-alpha02',
constraintLayout : '2.0.0-alpha3',
appCompat : '1.1.0-alpha01'
我在登录页面添加 Material 按钮没有问题。问题从我的 MainActivity 开始,在我的第一个列表片段中。尝试显示以 MaterialCardView 作为每个项目的根的回收站视图项目时,出现 ThemeEnforcement 错误。当我尝试添加 MaterialTextButton 时也是如此。这是我的 MaterialCardView xml 声明的样子:
<com.google.android.material.card.MaterialCardView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/tripCard"
style="@style/Widget.MaterialComponents.CardView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="8dp"
card_view:cardBackgroundColor="@color/colorSurface"
card_view:cardCornerRadius="@dimen/widget_corner_radius"
card_view:cardElevation="@dimen/card_elevation">
在 MaterialComponents 库中设置断点时,我注意到它有时无法验证主题。以下是库的一些代码,显示了失败的地方(在 ThemeEnforcement.java 中):
if (enforceMaterialTheme) {
TypedValue isMaterialTheme = new TypedValue();
boolean resolvedValue =
context.getTheme().resolveAttribute(R.attr.isMaterialTheme, isMaterialTheme, true);
if (!resolvedValue
|| (isMaterialTheme.type == TypedValue.TYPE_INT_BOOLEAN && isMaterialTheme.data == 0)) {
// If we were unable to resolve isMaterialTheme boolean attribute, or isMaterialTheme is
// false, check for Material Theme color attributes
checkMaterialTheme(context);
}
}
public static void checkMaterialTheme(Context context) {
checkTheme(context, MATERIAL_CHECK_ATTRS, MATERIAL_THEME_NAME);
}
private static final int[] MATERIAL_CHECK_ATTRS = {R.attr.colorPrimaryVariant};
据我所知,Theme Enforcer 会尝试解析 isMaterialTheme 属性(它 有时 可以解析 尽管在这种情况下不是)然后它继续检查 material 主题颜色属性的存在(检查 colorPrimaryVariant)并再次失败(我也在我的应用程序主题中定义了它。)
我会继续提交错误,如果有人有解决方案或任何建议,我将不胜感激!
要跟进评论:使用从使用正确主题的 Context
检索到的 LayoutInflater
很重要。应用程序上下文不能满足此要求。
好消息是您不必担心将上下文注入适配器。这不是很明显,但适配器提供了您获得正确上下文所需的一切。
看看onCreateViewHolder()
骨架:
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
// ...
}
所有 View
实例都有一个 getContext()
方法,并且 parent
参数保证为非空...因此您可以从父级检索上下文并使用获取 LayoutInflater:
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.my_item_view, parent, false);
return new MyViewHolder(itemView);
}
同样,在 ViewHolder
中,您可以使用相同的技术来获取上下文对象。每个 ViewHolder
实例都有一个保证为非空的 itemView
字段,因此您可以从中检索上下文。
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
Context context = holder.itemView.getContext();
// ...
}