问题背景
在一个普通的activity中使用 com.google.android.material.bottomsheet.BottomSheetBehavior
报错内容
css
java.lang.UnsupportedOperationException: Failed to resolve attribute at index 3: TypedValue{t=0x2/d=0x7f04013f a=5}
Caused by: java.lang.RuntimeException: Could not inflate Behavior subclass com.google.android.material.bottomsheet.BottomSheetBehavior
at androidx.coordinatorlayout.widget.CoordinatorLayout.parseBehavior(CoordinatorLayout.java:649)
at androidx.coordinatorlayout.widget.CoordinatorLayout$LayoutParams.<init>(CoordinatorLayout.java:2896)
at androidx.coordinatorlayout.widget.CoordinatorLayout.generateLayoutParams(CoordinatorLayout.java:1740)
at androidx.coordinatorlayout.widget.CoordinatorLayout.generateLayoutParams(CoordinatorLayout.java:112)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1150)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1109)
at android.view.LayoutInflater.inflate(LayoutInflater.java:707)
at android.view.LayoutInflater.inflate(LayoutInflater.java:545)
at android.view.LayoutInflater.inflate(LayoutInflater.java:489)
at androidx.appcompat.app.AppCompatDelegateImpl.setContentView(AppCompatDelegateImpl.java:775)
at androidx.appcompat.app.AppCompatActivity.setContentView(AppCompatActivity.java:197)
at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:303)
at androidx.databinding.DataBindingUtil.setContentView(DataBindingUtil.java:284)
at android.app.Activity.performCreate(Activity.java:8891)
at android.app.Activity.performCreate(Activity.java:8856)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1468)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3953)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:4124)
at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:149)
at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:99)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2572)
at android.os.Handler.dispatchMessage(Handler.java:106)
at android.os.Looper.loopOnce(Looper.java:224)
at android.os.Looper.loop(Looper.java:318)
at android.app.ActivityThread.main(ActivityThread.java:8653)
2024-08-05 18:45:03.468 AndroidRuntime E at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:561)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1013)
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at androidx.coordinatorlayout.widget.CoordinatorLayout.parseBehavior(CoordinatorLayout.java:647)
... 32 more
Caused by: java.lang.UnsupportedOperationException: Failed to resolve attribute at index 3: TypedValue{t=0x2/d=0x7f04013f a=15},
at android.content.res.TypedArray.getColorStateList(TypedArray.java:603)
at com.google.android.material.resources.MaterialResources.getColorStateList(MaterialResources.java:81)
at com.google.android.material.bottomsheet.BottomSheetBehavior.<init>(BottomSheetBehavior.java:353)
使用的GoogleMaterial版本
makefile
com.google.android.material:material:1.10.0
布局文件代码:
ini
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools">
<data>
</data>
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/settings_standard_bottom_sheet"
style="@style/Widget.Material3.BottomSheet"
android:layout_width="match_parent"
android:orientation="vertical"
android:layout_height="match_parent"
android:background="@color/black"
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:layout_gravity="center_horizontal"
android:src="@drawable/top_bottom_arrow"
android:id="@+id/drag_handle"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_20"
android:paddingVertical="@dimen/dp_20"
android:text="@string/about_ChatBird"
android:textSize="@dimen/sp_22"
android:textStyle="bold" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_20"
android:layout_marginTop="@dimen/dp_60"
android:paddingVertical="@dimen/dp_20"
android:text="@string/account_management"
android:textSize="@dimen/sp_22" />
</LinearLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</layout>
代码是从material design官方copy过来的,肯定不会有问题
原因分析
看报错内容,首先就是 CoordinatorLayout.parseBehavior,看源码
ini
static Behavior parseBehavior(Context context, AttributeSet attrs, String name) {
if (TextUtils.isEmpty(name)) {
return null;
}
final String fullName;
if (name.startsWith(".")) {
// Relative to the app package. Prepend the app package name.
fullName = context.getPackageName() + name;
} else if (name.indexOf('.') >= 0) {
// Fully qualified package name.
fullName = name;
} else {
// Assume stock behavior in this package (if we have one)
fullName = !TextUtils.isEmpty(WIDGET_PACKAGE_NAME)
? (WIDGET_PACKAGE_NAME + '.' + name)
: name;
}
try {
Map<String, Constructor<Behavior>> constructors = sConstructors.get();
if (constructors == null) {
constructors = new HashMap<>();
sConstructors.set(constructors);
}
Constructor<Behavior> c = constructors.get(fullName);
if (c == null) {
final Class<Behavior> clazz =
(Class<Behavior>) Class.forName(fullName, false, context.getClassLoader());
c = clazz.getConstructor(CONSTRUCTOR_PARAMS);
c.setAccessible(true);
constructors.put(fullName, c);
}
return c.newInstance(context, attrs);
} catch (Exception e) {
throw new RuntimeException("Could not inflate Behavior subclass " + fullName, e);
}
}
根据报错内容,可以确定是 c.newInstance
实例化 BottomSheetBehavior
时抛异常,接着我们看下BottomSheetBehavior
的构造函数,报错的方法在于 TypedArray.getColorStateList
,而BottomSheetBehavior
中对应的调用是在MaterialResources.getColorStateList
ini
this.peekHeightGestureInsetBuffer = context.getResources().getDimensionPixelSize(dimen.mtrl_min_touch_target_size);
TypedArray a = context.obtainStyledAttributes(attrs, styleable.BottomSheetBehavior_Layout);
if (a.hasValue(styleable.BottomSheetBehavior_Layout_backgroundTint)) {
this.backgroundTint = MaterialResources.getColorStateList(context, a, styleable.BottomSheetBehavior_Layout_backgroundTint);
}
从报错的源头java.lang.UnsupportedOperationException: Failed to resolve attribute at index 3: TypedValue{t=0x2/d=0x7f04013f a=5}
这一段可以初步分析出,原因在于BottomSheetBehavior
构造函数在从主题或XML属性集中获取某个自定义属性的值时,解析失败,而这个属性的id,就是 0x7f04013f
那么我们就可以从这个属性入手,找到对应的属性名称,再给它赋值。
如何找到这个属性id对应的名称,以及对应的定义源文件呢?
可以参考这篇文章:Android R文件详细介绍、瘦身方案和原理 - 掘金 (juejin.cn)
关键在于 app\build\intermediates\compile_and_runtime_not_namespaced_r_class_jar\debug\processReleaseResources
下面的R.jar。使用jadx反编译,查看R文件定义的属性集合,找到0x7f04013f
对应的属性名称
可以看到 colorSurface
这个属性就是我们要找的。
解决方案
接下来我们要在Application或者Activity的主题中,加入colorSurface
这个属性,并在colors.xml
中定义其内容。
styles.xml
xml
<style name="AppTheme" parent="Theme.AppCompat.DayNight.NoActionBar">
......
<!-- Material Components 默认背景色 -->
<item name="colorSurface">@color/colorSurface</item>
......
</style>
colors.xml
xml
<resources>
......
<color name="colorSurface">#ffffff</color>
......
</resources>
问题解决。