【问题标题】:Scrollable CardView with RecyclerView inside内部带有 RecyclerView 的可滚动 CardView
【发布时间】:2016-12-30 09:19:25
【问题描述】:

我想实现一个屏幕,其中有一个包含 RecyclerView 的卡片视图。

CardView 应该与 recycler view 的内容高度相同,这意味着如果 RecyclerView 有很少的项目,我应该看到卡片的底角和底部阴影,但如果 R​​ecyclerView 有很多项目,则卡片视图应与 RecyclerView 一起“滚动”,以使卡片视图的底角和阴影位于 RecylerView 的底部。

当 RecyclerView 位于顶部时,它应该是这样的:

当用户开始滚动时,顶角会随着 RecyclerView 的滚动而消失:

最后,当用户到达 RecyclerView 的底部时,会出现 CardView 的底角和阴影:

从现在开始,我设法通过将 RecyclerView 放在 CardView 中并将 CardView 放在 NestedScrollView 中来实现有效的实现,但这会破坏投掷手势......

<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 可以帮助我,但我找不到任何东西......

谢谢

【问题讨论】:

  • 我面临同样的问题。 android:nestedScrollingEnabled="false" 修复了投掷手势,但是如果您的 RecyclerView 足够大,界面就会超级滞后。你在这方面有什么进展吗?
  • @pdegand59 我正在考虑一个没有 NestedScrollView 的解决方案,但在 RecyclerView 中添加一个滚动侦听器。当您从下向上滚动时,CardView离开您的屏幕;当您到达项目的末尾时,将显示底部。这可以通过使用cardView.animate().y(theNewPosition).setDuration(0).start() 来实现。这只是一个想法,我没有在代码上测试过。
  • 请注意,在NestedScrollView 中使用RecyclerView 就像在ScrollView 中使用LinearLayout。不会有回收,RecyclerView 会有 itemHeight*itemCount 的高度。如果您不担心,请将NestedScrollView 替换为ScrollView,您将拥有一掷千金的手势。
  • 另一个不太优雅的想法是重写回收适配器,使第一个和最后一个项目具有圆角,并且这个额外的空间具有透明背景。

标签: android android-recyclerview scrollview cardview


【解决方案1】:

我有一个基于我之前使用过的 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。

如果您需要我更详细地阐述我的答案的任何部分,请告诉我。

【讨论】:

    【解决方案2】:

    借鉴 Oknesif 的可操作适配器的想法,我制作了一个具有三种布局(topitem、middleitem、bottomitem)的适配器,并为 topitem 和 bottomitem 提供了两个 XML 可绘制形状。因此,我能够完全摆脱NestedScrollViewCardView

    这就是它的样子:

    这是代码。一、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"
    

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

    【讨论】:

    • 您可以尝试在单元格上标高的白色背景上吗?我确信在每个单元格的侧面分离处会有一些阴影重叠。这就是为什么这样做的目标是在整个列表中只使用 1 个卡片视图的主要原因
    • 你的影子被剪裁在一边。将clipToPadding="false" 添加到回收站视图
    • 是的,你是对的。当填充没有被剪裁并且所有三个布局都存在高程时,单元格分隔中会出现“阴影”。但是,在底部布局上使用高程只会给您我刚刚发布的结果。这是你想要的吗?
    【解决方案3】:

    这只是一行简单的代码

    recycler.setNestedScrollingEnabled(false);
    

    并且不要忘记将卡片视图高度设置为 wrap_content

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-11-04
      • 2020-08-22
      • 2020-07-05
      • 1970-01-01
      • 2016-11-06
      • 1970-01-01
      • 2016-03-20
      • 1970-01-01
      相关资源
      最近更新 更多