使用BottomSheetBehavior时报错 :java.lang.UnsupportedOperationException

问题背景

在一个普通的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过来的,肯定不会有问题

material-components-android/docs/components/BottomSheet.md at master · material-components/material-components-android (github.com)

原因分析

看报错内容,首先就是 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>

问题解决。

相关推荐
miao_zz1 小时前
基于react native的锚点
android·react native·react.js
安卓美女1 小时前
Android自定义View性能优化
android·性能优化
Dingdangr2 小时前
Android中的四大组件
android
mg6683 小时前
安卓玩机工具-----无需root权限 卸载 禁用 删除当前机型app应用 ADB玩机工具
android
安卓机器3 小时前
Android架构组件:MVVM模式的实战应用与数据绑定技巧
android·架构
码龄3年 审核中4 小时前
MySQL record 05 part
android·数据库·mysql
服装学院的IT男4 小时前
【Android 13源码分析】WindowContainer窗口层级-1-初识窗口层级树
android
技术无疆5 小时前
Hutool:Java开发者的瑞士军刀
android·java·开发语言·ide·spring boot·gradle·intellij idea
qluka8 小时前
Android 应用安装-提交阶段
android
ansondroider8 小时前
Android MediaPlayer + GLSurfaceView 播放视频
android·opengl·mediaplayer·glsurfaceview