带有RecyclerView的可滚动CardView

11

我想要实现一个屏幕,其中包含一个卡片视图(Card view),该视图内部包含一个RecyclerView。

CardView的高度应与RecyclerView的内容高度相同,这意味着如果RecyclerView中只有几个项目,我应该能够看到卡的底部角和卡片的底部阴影;但是,如果RecyclerView中有很多项,CardView应“滚动”以使卡片的底部角和阴影位于RecylerView的底部。

当RecyclerView在顶部时,它应该如下所示:List at top

当用户开始滚动时,顶部角随着RecyclerView的滚动而消失:List during scroll

最后,当用户滚动到RecyclerView的底部时,CardView的底部角和阴影出现:List at end

目前,我通过将RecyclerView放入CardView中,并将CardView放入NestedScrollView中,实现了可行的实现,但这破坏了fling手势...

<android.support.design.widget.CoordinatorLayout
    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"
    android:clipChildren="false"
    android:id="@+id/containerLayout"
    android:layout_height="match_parent"
    android:layout_width="match_parent"
    android:orientation="vertical"
    tools:ignore="MissingPrefix">

    <android.support.v4.widget.NestedScrollView
        android:clipToPadding="false"
        android:layout_height="match_parent"
        android:layout_width="match_parent"
        android:paddingBottom="16dp"
        android:paddingLeft="85dp"
        android:paddingRight="85dp"
        android:paddingTop="16dp">

        <android.support.v7.widget.CardView
            android:layout_height="wrap_content"
            android:layout_width="match_parent"
            app:cardBackgroundColor="?android:attr/windowBackground">

            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerView"
                android:layout_height="wrap_content"
                android:layout_width="match_parent"/>
        </android.support.v7.widget.CardView>
    </android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>

你对如何实现这样的设计有任何提示或想法吗?我猜CoordinatorLayout可能会对我有帮助,但是我找不到任何资料...

谢谢


1
我遇到了同样的问题。android:nestedScrollingEnabled="false" 可以解决 fling 手势的问题,但是如果你的 RecyclerView 足够大,界面会非常卡顿。你在这方面有什么进展吗? - vyndor
@pdegand59 我在考虑一种不使用NestedScrollView的解决方案,而是在RecyclerView中添加一个滚动监听器。当你从下往上滚动时,CardView将会离开你的屏幕;当你滚动到列表末尾时,底部将会显示出来。可以通过使用cardView.animate().y(theNewPosition).setDuration(0).start()来实现这一点。这只是一个想法,我还没有在代码中进行测试。 - JJ86
1
请注意,在NestedScrollView内使用RecyclerView就像在ScrollView内使用LinearLayout一样。这将没有回收,RecyclerView的高度将为itemHeight * itemCount。如果您不担心这个问题,请用ScrollView替换NestedScrollView,这样您就可以使用fling手势了。 - Oknesif
1
另一个不太优雅的想法是以这样一种方式重写回收器适配器,使得第一个和最后一个项目具有圆角和透明背景的额外空间。 - Oknesif
3个回答

5

借鉴Oknesif的操纵适配器思路,我创建了一个包含三个布局(topitem、middleitem、bottomitem)和两个XML可绘制形状用于topitem和bottomitem的适配器。因此,我成功地完全摆脱了NestedScrollView和CardView。

效果如下:

enter image description here

这里是代码。首先,MainActivity

public class MainActivity extends AppCompatActivity {
    final static int LIST_SIZE = 100;

    final static int TOP = 0;
    final static int BOTTOM = LIST_SIZE;
    final static int MIDDLE = 1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity);

        final ArrayList<Integer> list = new ArrayList<>();
        for (int i = 0; i < LIST_SIZE; i++) {
            list.add(i);
        }

        class Viewholder extends RecyclerView.ViewHolder {
            TextView textView;

            Viewholder(View itemView) {
                super(itemView);
                textView = itemView.findViewById(R.id.textView);
            }
        }

        RecyclerView recyclerView = findViewById(R.id.recyclerView);
        final RecyclerView.Adapter<Viewholder> adapter = new RecyclerView.Adapter<Viewholder>() {
            LayoutInflater inflater = LayoutInflater.from(MainActivity.this);

            @Override
            public Viewholder onCreateViewHolder(ViewGroup parent, int viewType) {
                switch (viewType) {
                    case TOP:
                        return new Viewholder(inflater.inflate(R.layout.topitem, parent, false));
                    case BOTTOM:
                        return new Viewholder(inflater.inflate(R.layout.bottomitem, parent, false));
                    case MIDDLE:
                    default:
                        return new Viewholder(inflater.inflate(R.layout.middleitem, parent, false));
                }
            }

            @Override
            public void onBindViewHolder(Viewholder holder, int position) {
                holder.textView.setText(String.valueOf(list.get(position)));
                if (position != 0 && position != LIST_SIZE - 1) {
                    int color = position % 2 == 0 ? android.R.color.holo_orange_dark : android.R.color.holo_orange_light;
                    holder.itemView.setBackgroundColor(getResources().getColor(color));
                }
            }

            @Override
            public int getItemCount() {
                return LIST_SIZE;
            }

            @Override
            public int getItemViewType(int position) {
                int itemViewType;
                switch (position) {
                    case 0:
                        itemViewType = TOP;
                        break;
                    case LIST_SIZE - 1:
                        itemViewType = BOTTOM;
                        break;
                    default:
                        itemViewType = MIDDLE;
                }
                return itemViewType;
            }
        };
        recyclerView.setAdapter(adapter);
        recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
    }
}

res/layout/activity.xml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/containerLayout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clipToPadding="false"
        android:paddingLeft="25dp"
        android:paddingRight="25dp" />
</android.support.design.widget.CoordinatorLayout>

res/layout/topitem.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/topbackground"
    android:layout_marginTop="50dp"
    android:textAlignment="center"
    android:textColor="@android:color/white"
    android:textSize="24sp"
    android:textStyle="bold" />

res/layout/middleitem.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:textAlignment="center"
    android:textColor="@android:color/white"
    android:textSize="24sp"
    android:textStyle="bold" />

res/layout/bottomitem.xml:

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/textView"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:background="@drawable/bottombackground"
    android:layout_marginBottom="50dp"
    android:textAlignment="center"
    android:textColor="@android:color/white"
    android:textSize="24sp"
    android:textStyle="bold" />

res/drawable/topbackground.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners
        android:topLeftRadius="5dp"
        android:topRightRadius="5dp" />
    <solid android:color="@android:color/holo_orange_dark" />
</shape>

res/drawable/bottombackground.xml:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">
    <corners
        android:bottomLeftRadius="5dp"
        android:bottomRightRadius="5dp" />
    <solid android:color="@android:color/holo_orange_light" />
</shape>

编辑:

在底部XML项目布局中添加此行:

android:elevation="12dp"

将背景改为白色,得到以下结果:

enter image description here


你能否在单元格上尝试白色背景和仰角?我确定在每个单元格的侧面分隔处会有一些阴影重叠。这正是为什么这个目标是只使用一个cardview来完成整个列表的主要原因。 - pdegand59
你的阴影被裁剪了。在RecyclerView中添加“clipToPadding = false”。 - pdegand59
是的,你说得对。当填充没有被剪切并且三个布局都有高度时,会出现从单元格分离处“蔓延”的“阴影”。然而,只在底部布局上使用高度可以给你我刚刚发布的结果。这就是你想要的吗? - kalabalik

1

这只是一行简单的代码

recycler.setNestedScrollingEnabled(false);

不要忘记将cardview的高度设置为wrap_content。

0

我有一个建议,基于我之前使用过的Constraintlayout。您可以创建两个Guideline来设置CardView在滚动过程中的起始和结束位置。让我举个例子,这是用于视图起始位置的XML:

<android.support.constraint.ConstraintLayout
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"
android:clipChildren="false"
android:id="@+id/containerLayout"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical"
tools:ignore="MissingPrefix">

<android.support.constraint.Guideline
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/guideline"
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.1"/>

<android.support.constraint.Guideline
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/guideline2"
    android:orientation="horizontal"
    app:layout_constraintGuide_percent="0.9"/>

<android.support.v7.widget.CardView
    android:layout_height="wrap_content"
    android:layout_width="match_parent"
    app:cardBackgroundColor="?android:attr/windowBackground"

    app:layout_constraintTop_toTopOf="@+id/guideline">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_height="wrap_content"
        android:layout_width="match_parent" />
</android.support.v7.widget.CardView>

在这里,我假设您想在屏幕顶部留下大约10%的空白。如果您想要更少或更多,请进行调整。

一旦用户开始滚动,您可以将Cardview的顶部约束调整到父级的顶部,一旦他到达列表底部,您可以将Cardview的底部约束调整到guideline2,这将在屏幕下方留下10%的空间。

这样就可以实现所需的效果,而且不会有太多性能问题,因为您不需要使用Scrollview。

如果您需要我详细解释答案的任何部分,请告诉我。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接