ViewPager2 内项目的阴影被剪裁

Ric*_*ard 1 android android-layout android-view android-recyclerview android-viewpager2

我有一个活动,其中有NavHostFragment. 这NavHostFragment将承载三个片段,其中两个是FragmentAFragmentB。在 里面FragmentB,我有一个ViewPager2有两页的页面:PageAPageB,两者实际上都是由一个片段 构建的FragmentC。在每个PageA和里面PageB,我都有一个RecyclerView

这是 的布局 XML FragmentB

<?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.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:padding="@dimen/keyline_4"
        android:clipChildren="false"
        android:clipToPadding="false"
        tools:context=".ui.NavigationActivity">

        ...

        <androidx.viewpager2.widget.ViewPager2
            android:id="@+id/frag_course_view_pager"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="@dimen/keyline_4"
            android:clipChildren="false"
            ... />

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/frag_course_tablayout"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:layout_marginTop="@dimen/keyline_4"
            ... />


    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Run Code Online (Sandbox Code Playgroud)

如您所见,我已将片段的根布局设置clipChildrenfalse. 我也将ViewPager2's设置clipChildrenfalsePageA这是和PageB(即)的布局 XML FragmentC

<?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>
        <variable
            name="viewmodel"
            type="com.mobile.tugasakhir.viewmodels.course.CourseTabViewModel" />
    </data>

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/frag_course_tab_rv"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:paddingBottom="@dimen/offset_bottom_nav_bar_padding"
        android:clipChildren="false"
        android:clipToPadding="false"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"/>

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

正如您所看到的,它只有RecyclerView并且我已设置clipChildrenfalse. RV 的 ViewHolder 的扩展 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">

    <data>
        ...
    </data>

    <androidx.constraintlayout.widget.ConstraintLayout
        android:id="@+id/component_course_container"
        android:layout_width="match_parent"
        android:layout_height="@dimen/course_card_height"
        android:background="@drawable/drawable_rounded_rect"
        android:backgroundTint="?attr/colorSurface"
        android:elevation="@dimen/elevation_0">
        ...

        ...

    </androidx.constraintlayout.widget.ConstraintLayout>
</layout>
Run Code Online (Sandbox Code Playgroud)

elevation被剪掉的部分是上面ViewHolderXML 布局的阴影。由于我已将clipChildren所有父级的所有属性设置为false,因此阴影不应该被剪切,但它仍然如此。为什么会发生这种情况?如何在不更改填充/边距的情况下防止它被剪裁?

注意:我也有一个RecyclerViewinside FragmentA,但不同的是RecyclerViewinsideFragmentA没有嵌套在 a 内ViewPager2。遵循方法(将所有父级设置clipChildrenfalseFragmentA允许RecyclerView的项目显示其阴影。

这是问题的图像。

问题


更新

使用布局检查器,看起来内部ViewPager2有更多的 ViewGroup(用红色矩形标记)。我的RecyclerView项目被剪掉后用绿色矩形标记。这是布局检查器显示的内容。

布局检查器

可以看到,里面ViewPager2有一个ViewPager2$RecyclerViewImpl,里面有一个FrameLayout(我没有创建这些ViewGroups)。事实证明,当s设置为 false 时,这两个clipChildren设置true为Even 。我可以像这样瞄准内部。ViewPager2clipChildrenViewPager2$RecyclerViewImplFragmentB

(viewPager.getChildAt(0) as ViewGroup).clipChildren = false
Run Code Online (Sandbox Code Playgroud)

然后我尝试FrameLayout使用类似的方法来瞄准。

((viewPager.getChildAt(0) as ViewGroup).getChildAt(0) as ViewGroup).clipChildren = false
Run Code Online (Sandbox Code Playgroud)

但是,我收到一条错误消息,指出第二个getChildAt(0)返回null. 在我的布局检查器中,它清楚地显示在我的 RecyclerView 之前有一个 FrameLayout。这FrameLayoutclipChildren设置为 true。我很确定我必须将FrameLayout'设置clipChildrenfalse才能使阴影不被剪切,但我可​​能是错的。

以下是分别显示我成功设置 ofclipChildren和失败设置ofRecyclerViewImpl的屏幕截图。falseclipChildrenFrameLayoutfalse

成功

失败

或者有没有更好的方法来消除阴影?

我出于私人原因遮盖了布局预览;这会导致布局预览成为一个白框。


更新2

对于那些想直接查看问题的人,只需运行我在此Github 链接中提供的应用程序即可。使用布局检查器来查看我所看到的内容。

Mar*_*ini 5

更详细地查看您的问题后,我认为问题是 ViewHolder XML 使用 Drawable 作为背景试图模拟投影。

事实证明,Android 上的阴影不是“真正的阴影”,而是框架伪造的绘制元素。在某些情况下,这些阴影使用框架添加的额外填充;再次。

CardViews 有一个内部机制来处理这个问题(特别是在没有海拔概念的平台上)。

因此,我建议您更改 ViewHOlder 以包含 CardView。在这种情况下,它看起来像:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/component_course_container"
    android:layout_width="match_parent"
    android:layout_height="200dp"
    android:backgroundTint="#efefef">

    <androidx.cardview.widget.CardView
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:cardCornerRadius="8dp"
        app:cardUseCompatPadding="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

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

            <TextView
                android:layout_width="0dp"
                android:layout_height="0dp"
                android:text="Your Content Goes Here"
                app:layout_constraintBottom_toBottomOf="parent"
                app:layout_constraintEnd_toEndOf="parent"
                app:layout_constraintStart_toStartOf="parent"
                app:layout_constraintTop_toTopOf="parent" />

        </androidx.constraintlayout.widget.ConstraintLayout>

    </androidx.cardview.widget.CardView>

</androidx.constraintlayout.widget.ConstraintLayout>
Run Code Online (Sandbox Code Playgroud)

然后...删除所有的clipChildren/CliptoPadding等。这些会给你带来比解决方案更令人头痛的问题,并且会造成性能损失(框架现在需要绘制额外的东西,否则这些东西不会被渲染,因为它被覆盖了)。

我希望这可以帮助你。

关于 FrameLayout,这是一个 VP“东西”,我希望 Google 在最新的 AppCompat 实现中用新的“FragmentContainer”替换它。

祝你好运。

现在的样子是这样的:

在此输入图像描述