踩坑
背景
项目要适配夜间模式,因此要添加深色主题(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协议,复制请保留原文出处。