Другое

Разделители и отступы в RecyclerView: Полное руководство

Узнайте, как добавлять разделители и отступы между элементами RecyclerView с помощью классов ItemDecoration. Полное руководство с примерами кода для LinearLayoutManager и GridLayoutManager.

Как добавить разделители и отступы между элементами в RecyclerView

В разработке под Android я пытаюсь добавить разделители и отступы между элементами в RecyclerView, аналогично тому, как это делалось с ListView с помощью параметров divider и dividerHeight:

xml
<ListView
    android:id="@+id/activity_home_list_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@android:color/transparent"
    android:dividerHeight="8dp"/>

Однако я не вижу эквивалентных свойств в классе RecyclerView:

xml
<android.support.v7.widget.RecyclerView
    android:id="@+id/activity_home_recycler_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="vertical"/>

Какой рекомендуемый подход для добавления разделителей и отступов между элементами в RecyclerView? Следует ли определять отступы и/или добавлять пользовательский вид разделителя прямо в макет элемента списка, или есть более лучший способ добиться этого?

Чтобы добавлять разделители и отступы между элементами в RecyclerView, необходимо использовать классы ItemDecoration, а не XML-атрибуты, как в ListView. Рекомендуемые подходы включают использование встроенного DividerItemDecoration из библиотеки Android Support Library, создание пользовательских классов ItemDecoration для большего контроля или реализацию отступов через поля в макетах элементов.

Содержание

Использование DividerItemDecoration

Простой подход - использование встроенного класса DividerItemDecoration из библиотеки Android Support Library. Это предоставляет стандартный разделитель между элементами для LinearLayoutManager.

Реализация:

java
// В вашем Activity или Fragment
RecyclerView recyclerView = findViewById(R.id.activity_home_recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

// Добавление декорации разделителя
DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(
    recyclerView.getContext(), 
    DividerItemDecoration.VERTICAL
);
recyclerView.addItemDecoration(dividerItemDecoration);

Настройка внешнего вида разделителя:

Вы можете создать пользовательский drawable разделителя в папке res/drawable:

xml
<!-- res/drawable/custom_divider.xml -->
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="line">
    <size android:height="1dp" android:width="1dp"/>
    <solid android:color="#CCCCCC"/>
</shape>

Затем используйте его в вашем пользовательском разделителе:

java
public class CustomDividerItemDecoration extends RecyclerView.ItemDecoration {
    private Drawable divider;
    
    public CustomDividerItemDecoration(Context context, int resId) {
        divider = ContextCompat.getDrawable(context, resId);
    }
    
    @Override
    public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();
        
        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();
            
            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + divider.getIntrinsicHeight();
            
            divider.setBounds(left, top, right, bottom);
            divider.draw(c);
        }
    }
}

Создание пользовательских ItemDecorations

Для большего контроля над разделителями и отступами вы можете создавать пользовательские классы ItemDecoration, расширяя RecyclerView.ItemDecoration.

Базовый пользовательский ItemDecoration:

java
public class SimpleItemDecorator extends RecyclerView.ItemDecoration {
    private int space;
    private boolean isHorizontalLayout;

    public SimpleItemDecorator(int space) {
        this.space = space;
    }

    public SimpleItemDecorator(int space, boolean isHorizontalLayout) {
        this.space = space;
        this.isHorizontalLayout = isHorizontalLayout;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        outRect.bottom = space;
        outRect.right = space;
        outRect.left = space;
        outRect.top = space;
    }
}

Условный разделитель для последнего элемента:

java
public class ConditionalDividerDecoration extends RecyclerView.ItemDecoration {
    private Drawable divider;
    private int verticalSpaceHeight;

    public ConditionalDividerDecoration(Context context, int verticalSpaceHeight) {
        this.verticalSpaceHeight = verticalSpaceHeight;
        // Получение разделителя по умолчанию
        TypedArray styledAttributes = context.obtainStyledAttributes(
            new int[]{android.R.attr.listDivider});
        divider = styledAttributes.getDrawable(0);
        styledAttributes.recycle();
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
            outRect.bottom = verticalSpaceHeight;
        }
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        int left = parent.getPaddingLeft();
        int right = parent.getWidth() - parent.getPaddingRight();

        int childCount = parent.getChildCount();
        for (int i = 0; i < childCount - 1; i++) {
            View child = parent.getChildAt(i);
            RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child.getLayoutParams();

            int top = child.getBottom() + params.bottomMargin;
            int bottom = top + divider.getIntrinsicHeight();

            divider.setBounds(left, top, right, bottom);
            divider.draw(c);
        }
    }
}

Добавление отступов между элементами

Для добавления отступов между элементами без разделителей можно использовать более простой ItemDecoration:

java
public class VerticalSpaceItemDecoration extends RecyclerView.ItemDecoration {
    private final int verticalSpaceHeight;

    public VerticalSpaceItemDecoration(int verticalSpaceHeight) {
        this.verticalSpaceHeight = verticalSpaceHeight;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (parent.getChildAdapterPosition(view) != parent.getAdapter().getItemCount() - 1) {
            outRect.bottom = verticalSpaceHeight;
        }
    }
}

Использование:

java
recyclerView.addItemDecoration(new VerticalSpaceItemDecoration(48)); // 48dp отступ

Особенности GridLayoutManager

При использовании GridLayoutManager требуется специальная обработка для разделителей и отступов:

java
public class GridSpacesItemDecoration extends RecyclerView.ItemDecoration {
    private int space;

    public GridSpacesItemDecoration(int space) {
        this.space = space;
    }

    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        int position = parent.getChildAdapterPosition(view);
        int spanCount = ((GridLayoutManager) parent.getLayoutManager()).getSpanCount();
        int column = position % spanCount;

        // Добавление отступов слева и справа
        outRect.left = column * space / spanCount;
        outRect.right = space - (column + 1) * space / spanCount;

        // Добавление верхнего отступа для первой строки
        if (position < spanCount) {
            outRect.top = space;
        }
        outRect.bottom = space;
    }
}

Варианты разделителей Material Design

Для более новых реализаций Material Design рассмотрите использование MaterialDividerItemDecoration:

java
// Для Material Components
MaterialDividerItemDecoration dividerDecoration = new MaterialDividerItemDecoration(
    requireContext(), 
    DividerItemDecoration.VERTICAL
);
dividerDecoration.setLastItemDecorated(false); // Не рисовать после последнего элемента
dividerDecoration.setDividerColor(Color.parseColor("#CCCCCC"));
dividerDecoration.setDividerInsetStart(16);
dividerDecoration.setDividerInsetEnd(16);

recyclerView.addItemDecoration(dividerDecoration);

Лучшие практики и рекомендации

  1. Используйте ItemDecoration для последовательных отступов: ItemDecorations рисуются RecyclerView и остаются последовательными при прокрутке и изменении элементов.

  2. Обрабатывайте крайние случаи: Всегда учитывайте первый и последний элементы при реализации разделителей.

  3. Вопросы производительности: Сложные операции onDraw могут повлиять на производительность. Делайте рисование разделителей простым.

  4. Разные подходы для разных нужд:

    • Для простых разделителей: Используйте DividerItemDecoration
    • Для пользовательских отступов: Создавайте пользовательские ItemDecoration
    • Для Material Design: Используйте MaterialDividerItemDecoration
    • Для сеток: Используйте специализированные декораторы сеток
  5. Избегайте добавления разделителей в макеты элементов: Добавление представлений разделителей в индивидуальные макеты создает проблемы с производительностью и непоследовательное поведение.

  6. Учитывайте доступность: Убедитесь, что разделители не мешают функциям доступности, предоставляя правильное группирование контента.

Пример реализации, сочетающий отступы и разделители:

java
// В настройке вашего Activity/Fragment
RecyclerView recyclerView = findViewById(R.id.recyclerView);
recyclerView.setLayoutManager(new LinearLayoutManager(this));

// Добавление отступов
recyclerView.addItemDecoration(new VerticalSpaceItemDecoration(16));

// Добавление разделителей
recyclerView.addItemDecoration(new DividerItemDecoration(
    this, 
    DividerItemDecoration.VERTICAL
));

Ключевое отличие от ListView заключается в том, что RecyclerView использует более гибкую и мощную систему ItemDecoration, которая позволяет лучше настраивать и обеспечивает лучшую производительность, но требует программной настройки вместо простых XML-атрибутов.

Источники

  1. Как добавить разделители и отступы между элементами в RecyclerView - Stack Overflow
  2. DividerItemDecoration | Android Developers
  3. Как добавить разделители в Android RecyclerView - GeeksforGeeks
  4. RecyclerView ItemDecoration: Максимизация возможностей - Medium
  5. Добавление разделителей и отступов между элементами в RecyclerView - Ready Android
  6. Реализация DividerItemDecoration - GitHub

Заключение

Чтобы добавлять разделители и отступы между элементами RecyclerView:

  • Начните с DividerItemDecoration для простых разделителей
  • Создавайте пользовательские классы ItemDecoration для расширенного контроля
  • Используйте getItemOffsets() для отступов и onDraw() для пользовательских разделителей
  • Рассмотрите MaterialDividerItemDecoration для приложений Material Design
  • Обрабатывайте крайние случаи, такие как первый/последний элементы и сценарии GridLayoutManager
  • Избегайте добавления разделителей непосредственно в макеты элементов для лучшей производительности

Система ItemDecoration обеспечивает большую гибкость, чем встроенные свойства разделителей ListView, позволяя лучше настраивать и обеспечивая последовательное поведение в разных менеджерах компоновки.

Авторы
Проверено модерацией
Модерация