使用 Material Design 主题进行片段隔离测试

Nik*_*vic 2 android android-fragments android-testing material-design

我有一个单一活动、多功能模块的设置,如下所示:

/app - 包含继承材料设计样式资源的主模块,以及活动 /menu - 仅包含片段和视图模型/其他模块的模块功能...

我试图在 Android 测试中单独运行我的 Fragment,这会导致 Material 组件(在本例中为 CardView)必须将 Material Theme 作为其样式的错误。这确实有道理,但我尝试过。

现在我得到这个错误

android.view.InflateException: Binary XML file line #21 in com.nikolam.menu.test:layout/menu_item: Binary XML file line #21 in com.nikolam.menu.test:layout/menu_item: Error inflating class com.google.android.material.card.MaterialCardView
Caused by: android.view.InflateException: Binary XML file line #21 in com.nikolam.menu.test:layout/menu_item: Error inflating class com.google.android.material.card.MaterialCardView
Caused by: java.lang.reflect.InvocationTargetException
at java.lang.reflect.Constructor.newInstance0(Native Method)
at java.lang.reflect.Constructor.newInstance(Constructor.java:343)
at android.view.LayoutInflater.createView(LayoutInflater.java:854)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:1006)
at android.view.LayoutInflater.createViewFromTag(LayoutInflater.java:961)
at android.view.LayoutInflater.rInflate(LayoutInflater.java:1123)
at android.view.LayoutInflater.rInflateChildren(LayoutInflater.java:1084)
at android.view.LayoutInflater.inflate(LayoutInflater.java:682)
at android.view.LayoutInflater.inflate(LayoutInflater.java:534)
at com.nikolam.menu.ui.menu.adapter.MenuAdapter.onCreateViewHolder(MenuAdapter.kt:27)
at com.nikolam.menu.ui.menu.adapter.MenuAdapter.onCreateViewHolder(MenuAdapter.kt:16)
at androidx.recyclerview.widget.RecyclerView$Adapter.createViewHolder(RecyclerView.java:7266)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6397)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6281)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6277)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2330)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1631)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1591)
at androidx.recyclerview.widget.LinearLayoutManager.onLayoutChildren(LinearLayoutManager.java:668)
at androidx.recyclerview.widget.RecyclerView.dispatchLayoutStep2(RecyclerView.java:4277)
at androidx.recyclerview.widget.RecyclerView.dispatchLayout(RecyclerView.java:3980)
at androidx.recyclerview.widget.RecyclerView.onLayout(RecyclerView.java:4546)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at androidx.constraintlayout.widget.ConstraintLayout.onLayout(ConstraintLayout.java:1915)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at com.android.internal.widget.ActionBarOverlayLayout.onLayout(ActionBarOverlayLayout.java:509)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.widget.FrameLayout.layoutChildren(FrameLayout.java:332)
at android.widget.FrameLayout.onLayout(FrameLayout.java:270)
at com.android.internal.policy.DecorView.onLayout(DecorView.java:779)
at android.view.View.layout(View.java:21912)
at android.view.ViewGroup.layout(ViewGroup.java:6260)
at android.view.ViewRootImpl.performLayout(ViewRootImpl.java:3080)
at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:2590)
at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:1721)
at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:7598)
at android.view.Choreographer$CallbackRecord.run(Choreographer.java:966)
at android.view.Choreographer.doCallbacks(Choreographer.java:790)
at android.view.Choreographer.doFrame(Choreographer.java:725)
at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:951)
at android.os.Handler.handleCallback(Handler.java:883)
at android.os.Handler.dispatchMessage(Handler.java:100)
at android.os.Looper.loop(Looper.java:214)
at android.app.ActivityThread.main(ActivityThread.java:7356)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:492)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:930)
Caused by: java.lang.IllegalArgumentException: The style on this component requires your app theme to be Theme.MaterialComponents (or a descendant).
at com.google.android.material.internal.ThemeEnforcement.checkTheme(ThemeEnforcement.java:240)
at com.google.android.material.internal.ThemeEnforcement.checkMaterialTheme(ThemeEnforcement.java:215)
at com.google.android.material.internal.ThemeEnforcement.checkCompatibleTheme(ThemeEnforcement.java:143)
at com.google.android.material.internal.ThemeEnforcement.obtainStyledAttributes(ThemeEnforcement.java:78)
at com.google.android.material.card.MaterialCardView.<init>(MaterialCardView.java:128)
at com.google.android.material.card.MaterialCardView.<init>(MaterialCardView.java:118)
Run Code Online (Sandbox Code Playgroud)

这是不言自明的,但问题是布局,它需要继承材料设计风格。确实如此。这只是在隔离环境(测试)中运行它时出现的问题,否则它可以正常工作。我猜测容器中的 Fragment 没有引用样式,但随后我将其添加到 CardView 本身,这让我更加困惑。

@Before
    fun setUp() {
        launchFragmentInContainer<MenuFragment>()
    }
Run Code Online (Sandbox Code Playgroud)
<?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">

    <data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content">

        <com.google.android.material.card.MaterialCardView
            style="@style/Theme.MaterialComponents.DayNight"
            android:id="@+id/materialCardView"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintHorizontal_bias="0.5"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.appcompat.widget.AppCompatTextView
            android:id="@+id/name_textView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@{item.name}"
            android:textSize="30dp"
            app:layout_constraintHorizontal_bias="0.212"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />

        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/options_recycle_view"
            adapter="@{adapter}"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/name_textView"
            app:layout_constraintTop_toTopOf="@+id/materialCardView" />


    </androidx.constraintlayout.widget.ConstraintLayout>

</layout>
Run Code Online (Sandbox Code Playgroud)

应用程序主题在清单中引用。整个应用程序的样式仅在应用程序模块中,菜单模块没有其style.xml文件。该应用程序包含该项目的唯一活动。我试图找到其他人也有同样的问题,但没有运气。

小智 7

FragmentScenarioAPI 允许您为片段容器显式设置所需的主题。

将应用程序主题定义为styles.xmlattribute AppThemeparent=Theme.MaterialComponents可能的MenuFragment启动调用如下所示:

FragmentScenario.launchInContainer(MenuFragment.class, null, R.style.AppTheme, null);

请参阅FragmentScenario文档以获取对此答案范围之外的其余参数的解释。