细说 AppCompat 主题引发的坑:You need to use a Theme.AppCompat theme with this activity!

本文详细探讨了在Android开发中遇到的非AppCompat主题导致的AppCompat框架使用异常,提供了三种解决方案:主题切换、Activity改版及手动兼容AppCompat控件。通过实例演示如何配置windowActionBar属性和AppCompatCheckBox,以确保兼容性并避免遗漏问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

12-widget

一般来说按照文档的建议去做,出现问题的概率很低。但很多人的情况不同,每每会发生意外状况,就比如这次没有使用 AppCompat 主题引发的坑!

AppCompat 框架作为 Jetpack 集合的基石,非常重要。Android Studio 上创建的默认项目都会自动集成 AppCompat 框架,并采用其提供的 AppCompatActivity 作为 Activity Base。

App 侧给 Activity 配置的主题一般扩展自 SDK 提供的系统主题或 AppCompat 提供的主题,前者的话极有可能引发一些 AppCompat 框架的使用异常。

非 AppCompat 主题引发了异常

如果配置的是扩展自 SDK 的主题,Activity 必然无法启动,并发生如下异常:

RuntimeException: Unable to start activity xxx: java.lang.IllegalStateException: You need to use a Theme.AppCompat theme (or descendant) with this activity.

原因很简单,AppCompat 框架的诸多后续处理紧密关联该主题配置的属性。因此在加载画面前将严格检查是否采用了 AppCompat 系主题,否则将抛出异常。

class AppCompatDelegateImpl ... {
    private ViewGroup createSubDecor() {
        TypedArray a = mContext.obtainStyledAttributes(R.styleable.AppCompatTheme);
        if (!a.hasValue(R.styleable.AppCompatTheme_windowActionBar)) {
            a.recycle();
            throw new IllegalStateException(
                    "You need to use a Theme.AppCompat theme (or descendant) with this activity.");
        }
        ...
        return subDecor;
    }
}

如何解决这个问题呢?

  1. 主题改为扩展自 AppCompat 系主题。 但如果自己的主题覆写的地方很多,这将耗费很长时间,而且很多自定义的属性可能还会和 AppCompat 主题产生冲突,需要逐个分析、细细调整

  2. Activity 改用 SDK 版本,即 android.app.Activity 但随着 Jetpack 框架的日渐成熟和流行,很多重要的框架非常依赖于 AppCompatActivity 的支持,比如 Lifecycle 框架、ViewModel 框架、Preference 等。这可能导致其他的框架功能发生问题,也不是很好

  3. AppCompat 哪里有兼容性问题解决哪里的回避方案。 比如上面的异常其实就是检查是否配置了 AppCompat 框架提供的 windowActionBar 属性而已,那么我们在自己的主题里加上该属性的引用就可以了。不好的地方就在于,很多不是异常的 UI 展示问题,如果没有发现的话,很容易被忽略。也就是说,这个方案容易改得不全,产生遗漏

前2个方案没啥好说,我们具体来分析下第3个方案具体怎么操作。

如何使用非 AppCompat 主题

在扩展自 SDK 的主题里额外配置下 windowActionBar 属性即可,true 或者 false 依需而定。

<style name="Theme.MaterialExtension" parent="android:Theme.Material.Light">
    ...
</style>

<style name="Theme.MaterialExtension.Customize">
    <item name="windowActionBar">true</item>
</style>

成功启动后的 Activity 画面:

12-widget

等等,复选框设置条目的 CheckBox 怎么不见了?

12-widget

查看了 CheckBoxPreference 的源码,没有发现什么特别的处理。

通过 Layout Inspector 看了下布局,发现了一点线索:视图当中,CheckBox 的实例是存在的,只是 Width 变成了0。而且 CheckBox 的实现类名变成了 AppCompatCheckBox

突然想起 AppCompat 框架为了让低版本系统能使用上诸如 Auto SizeBackground Tint 的新功能,会给 SDK 的大部分控件重新扩展一个 AppCompat 前缀的同名控件。所以猜测,AppCompatCheckBox 依赖的兼容性属性,我们的主题里没有配置。

如何兼容 AppCompat 控件

来看下 AppCompatCheckBox 控件的源码,我们发现构造函数里针对复选按钮有特别的实现。

public AppCompatCheckBox( ... ) {
    ...
    mCompoundButtonHelper = new AppCompatCompoundButtonHelper(this);
    mCompoundButtonHelper.loadFromAttributes(attrs, defStyleAttr);
}

具体就是通过 AppCompatCompoundButtonHelper 去加载 buttonCompat 属性配置的复选按钮图片。

void loadFromAttributes(@Nullable AttributeSet attrs, int defStyleAttr) {
    TintTypedArray a =
            TintTypedArray.obtainStyledAttributes(mView.getContext(), attrs,
                    R.styleable.CompoundButton, defStyleAttr, 0);
    ViewCompat.saveAttributeDataForStyleable(mView, mView.getContext(),
            R.styleable.CompoundButton, attrs, a.getWrappedTypeArray(), defStyleAttr, 0);
    try {
        boolean buttonDrawableLoaded = false;
        if (a.hasValue(R.styleable.CompoundButton_buttonCompat)) {
            final int resourceId = a.getResourceId(R.styleable.CompoundButton_buttonCompat, 0);
            if (resourceId != 0) {
                try {
                    mView.setButtonDrawable(
                            AppCompatResources.getDrawable(mView.getContext(), resourceId));
                    buttonDrawableLoaded = true;
                } ...
            }
        }
        ...
    } finally {
        a.recycle();
    }
}

很明显,我们的主题里没有配置这个属性,所以 CheckBox 显示不出来。

当然可以直接在我们的主题里配置这个属性,但如果能和 AppCompat 框架设置一样的,省去了提供复选框资源,岂不更好。

<declare-styleable name="CompoundButton">
    <attr name="android:button"/>
    <!-- Compat attr to load backported drawable types -->
    <attr format="reference" name="buttonCompat"/>
    ...
</>

通过搜索发现 AppCompat 主题给 CheckBox 控件配置的 Style 里使用了 buttonCompat 的 Attr。

<style name="Base.Widget.AppCompat.CompoundButton.CheckBox" parent="android:Widget.CompoundButton.CheckBox">
    <item name="android:button">?android:attr/listChoiceIndicatorMultiple</item>
    <item name="buttonCompat">?attr/listChoiceIndicatorMultipleAnimated</item>
    <item name="android:background">?attr/controlBackground</item>
</style>

<style name="Widget.AppCompat.CompoundButton.CheckBox" parent="Base.Widget.AppCompat.CompoundButton.CheckBox"/>

<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
    <item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item>
</style>

这样的话,在我们的主题里同样应用这个 Style 就行了。

<style name="Theme.MaterialExtension.Customize">
    ...
    <item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item>
</style>

更快速的兼容方法

如果发现哪个控件有问题,都像上面的办法一样去查的话着实花时间。其实直接到 AppCompat 主题的实现里搜索控件相关的兼容性 Style,拷贝过来即可。

比如一步步找到 AppCompat 主题的具体实现,在里面搜索得到 CheckBox 关键字的 Style。

<style name="Theme.AppCompat.DayNight.DarkActionBar" parent="Theme.AppCompat.Light.DarkActionBar"/>

<style name="Theme.AppCompat.Light.DarkActionBar" parent="Base.Theme.AppCompat.Light.DarkActionBar"/>

<style name="Base.Theme.AppCompat.Light.DarkActionBar" parent="Base.Theme.AppCompat.Light">

<style name="Base.Theme.AppCompat.Light" parent="Base.V7.Theme.AppCompat.Light">

<style name="Base.V7.Theme.AppCompat.Light" parent="Platform.AppCompat.Light">
    ...
    <item name="editTextStyle">@style/Widget.AppCompat.EditText</item>
    <item name="editTextBackground">@drawable/abc_edit_text_material</item>
    <item name="editTextColor">?android:attr/textColorPrimary</item>
    ...
    <!-- CheckBox 的兼容性 Style 藏在这里 -->
    <item name="checkboxStyle">@style/Widget.AppCompat.CompoundButton.CheckBox</item>
</style>

注意

除了 AppCompatActivity,AppCompat 框架里提供的 AppCompatDialog 同样也有主题的限制,需要留意一下。AppCompatDialog 内视图发生兼容问题的话, 和 Activity 的 AppCompat 控件一样处理。

结语

针对非 AppCompat 主题的使用问题有3种解决方案:

  1. 主题改为扩展自 AppCompat 系主题

  2. Activity 改用 SDK 版本的 android.app.Activity

  3. 手动解决AppCompat 的兼容性问题

毋庸置疑的是 AppCompat 框架对主题的限制源于后续的 UI 逻辑与其紧密相连,所以最佳解决办法肯定是方案1,即提供 AppCompat 系主题。

  • 如果确实不需要 Jetpack 其他框架的协同(虽然这极不可能),那么可以选择方案2
  • 如果现有主题太过庞大,一时半会儿无法切换到 AppCompat 主题,而且影响的画面很少、不复杂。那么可以试试方案3去逐个解决

推荐阅读

AppCompat发布两年了,还没了解?

### 如何选择合适的内核模块类型 在选择适合系统的内核模块类型时,需要考虑操作系统的具体需求以及硬件支持情况。对于 Fedora 系统而言,在安装过程中会自动标记 KVM 模块以便于自加载[^1]。这意味着如果用户的虚拟化环境依赖 KVM,则无需额外配置即可正常使用。 然而,当面临多种内核模块选项时,应依据实际应用场景来决定: - **KVM (Kernel-based Virtual Machine)** 如果目标是运行基于 Linux 的虚拟机或者 Windows 虚拟机,并且主机也运行的是 Linux 系统,那么 KVM 是首选方案。它提供了高效的虚拟化性能并充分利用 CPU 提供的硬件加速功能。 - **VirtualBox 或 VMware** 对于桌面级应用或是跨平台兼容性较高的场景下,可以选用 VirtualBox 或者 VMware 工具自带的内核模块。这些工具通常封装好了大部分复杂设置过程,使得部署更加简便快捷。 - **Xen** Xen 更适用于大型数据中心环境中大规模服务器集群管理下的虚拟化任务。尽管其配置相对复杂一些,但在某些特定条件下能够提供更好的隔离性和安全性保障。 最终的选择取决于具体的使用案例和技术栈偏好等因素综合考量之后得出结论。 另外值得注意的一点是在机器学习领域中,“kernel”一词还可能指代另一种概念——即用于降维算法中的核函数(如 KernelPCA)。这里提到的例子展示了如何通过 Python 实现一个具有五个成分的核主成分分析模型[^2]。虽然这与操作系统层面讨论的内核模块不同,但同样体现了“kernel”这一术语广泛的应用范围。 最后关于全连接层部分描述则进一步扩展到了神经网络架构设计方面的话题[^3],不过这部分内容跟当前主题关系不大,因此不再展开细说。 ```python import numpy as np from sklearn.decomposition import KernelPCA import pandas as pd # 假设我们有一个标准化后的数据集 df_z_components df_z_components = ... # 数据预处理省略 # 创建带有5个组件的KernelPCA实例并训练转换数据 kernel_pca = KernelPCA(n_components=5).fit(df_z_components) transformed_pca_5 = kernel_pca.transform(df_z_components) # 将结果转化为DataFrame形式便于后续可视化或其他操作 pd.DataFrame(transformed_pca_5) ```
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

TechMerger

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值