Домой Android разработкаJava, Kotlin и Android SDK Listview и RecyclerView — бесконечные списки на android

Listview и RecyclerView — бесконечные списки на android

by dilix
Анимированный RecyclerView

Показать длинный или условно бесконечный постраничный список — частая задача для разработчика. В android для этого раньше использовался listView, а теперь более продвинутая версия recyclerView.

ScrollView — прокрутка экрана

Когда View не умещаются в рамках одного экрана их можно разместить в прокручивающемся контейнере ScrollView

Listview и RecyclerView - бесконечные списки на android

Проблема в том, что все элементы, в том числе которые не видны в данный момент, существуют в памяти. Если человек листает уже пятнадцатую страницу ленты в Facebook, ему не нужно чтобы первые записи тормозили прокрутку списка.

ListView как оптимизация длинных списков

Часто верстка View для отображения данных одинаковая от элемента к элементу (или же существует ограниченное число разных типов данных).

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

Listview и RecyclerView - бесконечные списки на android

Для использования ListView в приложении необходимы

  • ListView. View, контейнер для отображения списка,
  • Adapter. Менеджер данных и View — отображения данных в списке. Он отвечает за то, какой именно элемент интерфейса необходимо показать на определенной позиции.

Можно было бы подробнее остановиться на реализации ListView на Android, но Google давно представил переосмысленную версию данного компонента — RecyclerView.

Основные недостатки ListView

  • Сложно добавить анимации и декорации элементам списка,
  • Компонент ListView отвечает не только за переиспользование View, но и за порядок и структуру списка. Это значит, что реализовать горизонтальный список, используя ListView и не затрагивая большой части кодовой базы — проблематично,
  • Базовая реализация адаптера подразумевает поиск элементов View (findViewById) для отображения данных при каждом новом биндинге данных. И хотя шаблон ViewHolder широко известен, но применялся далеко не всегда.

RecyclerView — замена ListView

Основные отличия RecyclerView от ListView

  • RecyclerView отвечает только за переиспользование View, способ отображения данных задается с помощью отдельного менеджера — LayoutManager , существует ряд базовых реализаций
    • LinearLayoutManager — отображение горизонатльных и вертикальных последовательных и инвертированных списков,
    • GridLayoutManager — отображение табличных списков,
    • StaggeredGridLayoutManager — отображение табличных списков с контейнерами динамического размера
  • За анимации отдельных элементов при изменении, добавлении, удалении отвечает отдельный компонент ItemAnimator
  • Принудительно используется шаблон ViewHolder. Это позволяет оптимизировать производительность за счет сохранения ссылок на View в контейнере.

Компонентный подход в реализации RecyclerView позволяет гибко настраивать по отдельности каждый аспект списка, дописывая и переопределяя только необходимые части реализации.


Подключение RecyclerView

RecyclerView находится в support library, подключаемом в build.gradle

dependencies {
    implementation 'com.android.support:recyclerview-v7:27.1.1'
}

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

Выбираем какой LayoutManager хотим использовать

recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));

Adapter для RecyclerView

Adapter отвечает за биндинг данных в список, каркас для адаптера выглядит следующим образом

public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ItemViewHolder> {

    class ItemViewHolder extends RecyclerView.ViewHolder {
        private TextView contentTextView;

        public ItemViewHolder(View itemView) {
            super(itemView);
            contentTextView = itemView.findViewById(R.id.tweet_content_text_view);
        }
    }
}

Разные типы данных в Adapter

Механизм Adapter позволяет из коробки показывать разные типы данных в рамках одного RecyclerView.

public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    class ViewHolder0 extends RecyclerView.ViewHolder {
        ...
        public ViewHolder0(View itemView){
        ...
        }
    }

    class ViewHolder2 extends RecyclerView.ViewHolder {
        ...
        public ViewHolder2(View itemView){
        ...
    }

    @Override
    public int getItemViewType(int position) {
        // В зависимости от позии вернуть определенный тип
        return calculatedPosition;
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
         switch (viewType) {
             case 0: return new ViewHolder0(...);
             case 2: return new ViewHolder2(...);
             ...
         }
    }

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, final int position) {
        switch (holder.getItemViewType()) {
            case 0:
                ViewHolder0 viewHolder0 = (ViewHolder0)holder;
                ...
                break;

            case 2:
                ViewHolder2 viewHolder2 = (ViewHolder2)holder;
                ...
                break;
        }
    }
}

Вы просто для каждой позиции в списке возвращаете свой признак типа. Далее по нему возвращаете правильный ViewHolder. И потом биндите данные в полученный ViewHolder.

SnapHelper для gallery mode в RecyclerView

SnapHelper позволяет максимально просто влиять на то, как происходит fling в рамках RecyclerView. Например, можно сделать «постраничную прокрутку».

SnapHelper snapHelper = new PagerSnapHelper();
snapHelper.attachToRecyclerView(recyclerView);

Декорирование и отступы элементов в RecyclerView

Частая задача — добавить отступы или как-нибудь выделить определенные элементы в RecyclerView. Для этого существуют декораторы.

public class RecyclerViewMargin extends RecyclerView.ItemDecoration {
	private final int columns;
	private int margin;

	public RecyclerViewMargin(@IntRange(from=0)int margin ,@IntRange(from=0) int columns ) {
	    this.margin = margin;
	    this.columns=columns;

	}

	/**
	 * Установка разных отступов для элементов.
	 */
	@Override
	public void getItemOffsets(Rect outRect, View view,
	                           RecyclerView parent, RecyclerView.State state) {
	    int position = parent.getChildLayoutPosition(view);
	    outRect.right = margin;
	    outRect.left = margin;
	    outRect.bottom = margin;
	    
	    if(position % columns == 0) {
	        outRect.top = margin;
	    }
	}
}
RecyclerViewMargin decoration = new RecyclerViewMargin(itemMargin, numColumns);
recyclerView.addItemDecoration(decoration);

Анимации ячеек в RecyclerView

Если с ListView мы использовали notifyDataSetChange(), то в RecyclerView представили целый ряд методов, которые позволяет уведомить адаптер о смене данных. Методы имеют префикс notify* и позволяют сказать, что изменилась только часть данных. В данном случае RecyclerView проиграет корректные анимации работы со списками из коробки.

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

Хочешь обсудить Android разработку?
Заходи к нам Вконтакте, на Facebook и в Телеграм!

Добавить комментарий

Может быть интересно

Этот сайт использует Cookie файлы для улучшения вашего пользовательского взаимодействия. Используя данный сайт вы соглашается с этим. Принять Читать

Политика конфиденциальности и Cookies