Прокручиваемый CardView с RecyclerView внутри

Я хотел бы реализовать экран, на котором у меня есть представление карты, содержащее RecyclerView.

CardView должен иметь ту же высоту, что и содержимое представления ресайклера, это означает, что если в RecyclerView мало элементов, я должен видеть нижние углы и нижнюю тень карты, но если в RecyclerView много элементов, представление карты должно «прокрутите» с помощью 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 мог бы мне помочь, но я ничего не нашел...

Спасибо


person pdegand59    schedule 23.08.2016    source источник
comment
Я сталкиваюсь с той же проблемой. android:nestedScrollingEnabled="false" исправляет жест броска, но интерфейс сильно тормозит, если ваш RecyclerView достаточно велик. Есть ли у вас прогресс в этом?   -  person vyndor    schedule 02.12.2016
comment
@ pdegand59 Я думал о решении без NestedScrollView, но с прослушивателем прокрутки в RecyclerView. При прокрутке снизу вверх значок CardView уходит с экрана; когда вы дойдете до конца ваших предметов, будет показано дно. Этого можно добиться, используя cardView.animate().y(theNewPosition).setDuration(0).start(). Это просто идея, я не проверял ее на коде.   -  person JJ86    schedule 30.11.2017
comment
Имейте в виду, что использование RecyclerView внутри NestedScrollView похоже на использование LinearLayout внутри ScrollView. Переработки не будет, RecyclerView будет иметь высоту itemHeight*itemCount. Если вас это не беспокоит, замените NestedScrollView на ScrollView, и вы получите жест броска.   -  person Oknesif    schedule 01.12.2017
comment
Еще одна не очень элегантная идея — переписать адаптер ресайклера таким образом, чтобы первый и последний элементы имели закругленные углы и дополнительное пространство с прозрачным фоном.   -  person Oknesif    schedule 01.12.2017


Ответы (3)


Подхватив идею Окнесифа об управляемом адаптере, я сделал адаптер с тремя макетами (topitem, middleitem, bottomitem) с двумя формами XML для рисования для topitem и bottomitem. Таким образом, я смог полностью избавиться от NestedScrollView и CardView.

Вот как это выглядит:

введите здесь описание изображения

И вот код. Во-первых, 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"

и изменение фона на белый дает следующий результат:

введите здесь описание изображения

person kalabalik    schedule 04.12.2017
comment
Можете ли вы попробовать на белом фоне с высотой ячеек? Я уверен, что на боковом разделении каждой ячейки будут некоторые перекрытия теней. Это основная причина, по которой цель состоит в том, чтобы использовать только 1 карточку для всего списка. - person pdegand59; 05.12.2017
comment
Твоя тень подрезана сбоку. Добавьте clipToPadding="false" в recyclerview - person pdegand59; 05.12.2017
comment
Да, ты был прав. Тени выползают из-за разделения ячеек, когда отступы не обрезаны и когда на всех трех макетах есть возвышение. Однако использование высоты на нижнем макете дает вам только тот результат, который я только что опубликовал. Это то, что вы хотите? - person kalabalik; 05.12.2017

это просто строка кода

recycler.setNestedScrollingEnabled(false);

и не забудьте сделать высоту карты в wrap_content

person Chandan Bhandari    schedule 06.12.2017

У меня есть предложение, основанное на 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>

here I am assuming that you want to leave roughly 10% of screen space empty on top. If you want less or more, please adjust.

Как только пользователь начнет прокручивать, вы можете настроить верхнее ограничение Cardview на верхнюю часть родителя, и как только он достигнет нижней части списка, вы можете настроить нижнее ограничение Cardview на guideline2, что оставит 10% пространства на экране внизу. .

Это должно достичь желаемого эффекта без особых проблем с производительностью, поскольку вы избавляетесь от Scrollview.

Пожалуйста, дайте мне знать, если вам нужно, чтобы я более подробно изложил какую-либо часть моего ответа.

person Abhishek    schedule 03.12.2017