返回

踩坑记录 | Android中LayoutInflater读取attr颜色属性报错:Failed to resolve attribute at index 77: TypedValue{t=0x2/d=0x7f030107 a=-1}

使用LayoutInflater加载布局时,布局中控件引用attr自定义属性无法正确甚至报错

踩坑

背景

项目要适配夜间模式,因此要添加深色主题(Dark Theme),也就是Android开发中很常见的开发要求——多主题

在查找资料的时候发现多主题似乎在select和drawable上存在很大的坑,不过这里估计是因为使用了Androidx、AppCompat、Material3,所以并没有跳进去他们的坑,反倒是自己找了一处偏僻的小坑钻进去了。

发生这个问题的时候,我正在尝试使用Databinding获取自定义的Toast布局:

val binding = ToastBinding.inflate(LayoutInflater.from(context))

attrs.xml 添加自定义属性、在 theme 中赋值、在布局和 Drawable 中引用。

values/attrs.xml

<resources>
    <attr name="toastColor" format="reference|color" />
    ...
</resources>

values/themes.xml

<resources>
    <style name="AppTheme" parent="Theme.Material3.Light.NoActionBar">
        <item name="toastColor">@color/toast_light</item>
        ...
    </style>

    <style name="Theme.MyApp" parent="AppTheme" />
</resources>

drawable/check_circle.xml

<vector android:autoMirrored="false" android:height="24dp" android:viewportHeight="24"
    android:viewportWidth="24" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
    <path android:fillColor="?attr/toastIconColor" android:pathData="M10,17l-5,-5 1.41,-1.41L10,14.17l7.59,-7.59L19,8l-9,9z"/>
    <!-- 这是一个svg格式的矢量图,重要的是↑这个fillColor里引用了attr,后面的pathData是图片绘制路径 -->
</vector>

layout/my_toast.xml

<?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">

    <androidx.constraintlayout.widget.ConstraintLayout
        ...
        android:background="?attr/toastColor" >

        <ImageView
            ...
            android:src="@drawable/check_circle" />

        <TextView
            ...
            android:textColor="?attr/toastTextColor" />
    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

编译时一切顺利,然后一运行,问题就嘎嘎找上门了……

问题描述

正如题目所说,运行时报了以下错误:

Failed to resolve attribute at index 77: TypedValue{t=0x2/d=0x7f030107 a=-1}

一开始我以为这里的 attribute 是属性的意思而不是特指 ?attr/ ,看了半天没看懂,于是上网一顿猛搜,发现跟Theme的自定义属性有关。

然而查来查去,都没有解决办法,勉强知道问题是出在控件没有正确地获取到 ?attr/ 的值

然而我适配多主题,靠的就是 attr 呀,那不完蛋了?

解决方法

在查找资料的过程中,我发现其他发生相似问题的,大多是 Application 没有设置好Theme、Activity 没有设置好Theme、Fragment 没有设置好Theme。一句话总结相同点,就是用于 LayoutInflater.from(context)context 没有正确地配置Theme

在尝试过各种办法都无效过后,走投无路的我大胆的猜测:难道是因为这个 View 没有配置好Theme吗?

于是我在 layout/my_toast.xml 的根布局的属性中加上了一行:

android:theme="@style/Theme.MyApp"

也就是变成下面的这个样子:

...
<androidx.constraintlayout.widget.ConstraintLayout
        ...
        android:theme="@style/Theme.MyApp"
        android:background="?attr/toastColor" >

        ...

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>

然后问题就神奇的解决了。

既没有在 inflate 的时候报错,Drawable 也正常显示出来了。

分析原因

似乎由于传入 LayoutInflater.from(context)context 是一个我初始化时传入的 Application 对象,因此布局的View并没有自动获取到 AndroidManifest.xml 中的Theme配置。

而我们知道,attr 是与当前的 theme 完全关联的。

你当前是什么Theme,就获取到对应的 attr 。而如果当前的Theme使用的是默认的Theme或其他Theme,而这个Theme中并没有为对应的 attr 赋值,自然而然就要报错或者出现显示问题了。

总结

attr 是与当前的 theme 完全关联的。出现此问题,大概率是因为用于 LayoutInflater.from(context)context 没有正确地配置Theme

在对应的xml文件的根布局加入:

android:theme="@style/配置了对应attr的Theme"

应该能解决大部分同类情况。

附录

参考文献

说实话,看了很多博客之后才使我受到启发找到解决方法,但是还是感谢他们。虽然他们现在没有帮助我解决问题,但也使我在未来不会遇到同样问题或是遇到了能够解决。

看过的实在太多,但都没有实质性帮助,这里就不列参考文献了。

版权信息

本文原载于reincarnatey.net,遵循CC BY-NC-SA 4.0协议,复制请保留原文出处。

本博客所有内容无特殊标注均为失迹原创内容,复制请保留原文出处。
Built with Hugo
Theme Stack designed by Jimmy, mod by Korita
© Licensed Under CC BY-NC-SA 4.0