Эта статья была обновлена 1 февраля 2017 г. для Android 7.1 (уровень API 25)
Чтобы узнать больше об Android, просмотрите наш учебник « Вывод данных в пользовательский интерфейс приложения Android» .
RecyclerView
был представлен с дизайном материалов Google в Android 5.0 Lollipop.
Если вы только начинаете разработку под Android или уже являетесь опытным разработчиком, то RecyclerView
стоит изучить.
Идея утилизации представления была в Android с версии 1 в форме ListView
. Идея проста: представить большой набор данных с использованием небольшого набора представлений, переработав и связав эти представления.
RecyclerView
— это более гибкий шаблон RecyclerView
использования представлений, чем ListView
и GridView
. Что отличает RecyclerView
от его предшественников, так это то, что он фокусируется только на представлениях RecyclerView
использования. Все остальные действия, необходимые для создания представления, такие как представление набора данных или раздувание представлений, делегируются подключаемым классам, и это делает его таким гибким. Сложная часть заключается в настройке этих классов для создания полнофункционального RecyclerView
и об этом я расскажу в этой статье.
Чтобы использовать RecyclerView
вам необходимо выполнить следующие шаги:
- Добавить библиотеку поддержки
- Добавьте
RecyclerView
в XML-файл макета - Создать пользовательский макет строки
- Создайте
RecyclerView.Adapter
для заполнения данных вRecyclerView
- Создайте
ViewHolder
чтобы предоставить ссылку на представления для каждого элемента данных - Свяжите
Adapter
сRecyclerView
вActivity
Давайте RecyclerView
с RecyclerView
, вы можете найти код для окончательного проекта на GitHub .
Добавить зависимости
Откройте build.gradle (приложение) и добавьте необходимые зависимости.
dependencies { ... compile 'com.android.support:cardview-v7:25.1.0' compile 'com.android.support:recyclerview-v7:25.1.0' }
Синхронизируйте Gradle и все готово.
Добавьте RecyclerView в файл макета
< RelativeLayout ... < android.support.v7.widget.RecyclerView xmlns:android = "http://schemas.android.com/apk/res/android" android:id = "@+id/recyclerview" android:layout_width = "match_parent" android:layout_height = "match_parent" /> </ RelativeLayout >
Создать пользовательский макет строки
Строка Layout представляет макет каждого отдельного элемента, отображаемого в RecyclerView.
Создайте файл с именем row_layout.xml и добавьте в него следующее:
< android.support.v7.widget.CardView xmlns:android = "http://schemas.android.com/apk/res/android" xmlns:app = "http://schemas.android.com/apk/res-auto" android:id = "@+id/cardView" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:layout_marginBottom = "@dimen/activity_vertical_margin" android:clickable = "true" android:focusable = "true" android:foreground = "?android:attr/selectableItemBackground" app:cardCornerRadius = "@dimen/activity_vertical_margin" app:cardElevation = "@dimen/activity_vertical_margin" > < RelativeLayout android:layout_width = "match_parent" android:layout_height = "wrap_content" android:padding = "16dp" > < ImageView android:id = "@+id/imageView" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_alignParentLeft = "true" android:layout_alignParentTop = "true" android:layout_marginRight = "16dp" /> < TextView android:id = "@+id/title" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_alignParentTop = "true" android:layout_toRightOf = "@+id/imageView" android:text = "Title" android:textSize = "30sp" /> < TextView android:id = "@+id/description" android:layout_width = "wrap_content" android:layout_height = "wrap_content" android:layout_below = "@+id/title" android:layout_toRightOf = "@+id/imageView" android:text = "Description" /> </ RelativeLayout > </ android.support.v7.widget.CardView >
Просто обратите внимание: при создании макета элемента RecyclerView не забудьте добавить следующие строки в контейнер ViewGroup макета. Эти строки кода добавят волновой эффект к элементам RecyclerView.
android:clickable="true" android:focusable="true" android:foreground="?android:attr/selectableItemBackground"
RecyclerView
будет список случайно выбранных фильмов и описание. Нам нужен класс, представляющий данные одного элемента RecyclerView
, создайте файл с именем Data.java и добавьте следующее:
public class Data { public String title; public String description; public int imageId; Data(String title, String description, int imageId) { this .title = title; this .description = description; this .imageId = imageId; } }
Адаптер RecyclerView
RecyclerView.Adapter
аналогичен адаптерам, используемым в ListView
но с ViewHolder
необходимым для повышения производительности. ListView
имеет адаптеры для различных источников, такие как ArrayAdapter
для массивов и CursorAdapter
для результатов базы данных. RecyclerView.Adapter
требует пользовательской реализации для передачи данных в адаптер.
Адаптер имеет три метода.
-
onCreateViewHolder()
раздувает макет строки и инициализируетonCreateViewHolder()
. После инициализации держателя представления он управляет методамиfindViewById()
, находя представления один раз и перерабатывая их, чтобы избежать повторных вызовов. -
onBindViewHolder()
использует держатель представления,onCreateViewHolder()
методеonCreateViewHolder()
для заполнения текущей строкиRecyclerView
данными.
Создайте новый файл с именем Recycler_View_Adapter.java со следующим классом:
public class Recycler_View_Adapter extends RecyclerView . Adapter < View_Holder > { List<Data> list = Collections.emptyList(); Context context; public Recycler_View_Adapter (List<Data> list, Context context) { this .list = list; this .context = context; } @Override public View_Holder onCreateViewHolder (ViewGroup parent, int viewType) { //Inflate the layout, initialize the View Holder View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.row_layout, parent, false ); View_Holder holder = new View_Holder(v); return holder; } @Override public void onBindViewHolder (View_Holder holder, int position) { //Use the provided View Holder on the onCreateViewHolder method to populate the current row on the RecyclerView holder.title.setText(list.get(position).title); holder.description.setText(list.get(position).description); holder.imageView.setImageResource(list.get(position).imageId); //animate(holder); } @Override public int getItemCount () { //returns the number of elements the RecyclerView will display return list.size(); } @Override public void onAttachedToRecyclerView (RecyclerView recyclerView) { super .onAttachedToRecyclerView(recyclerView); } // Insert a new item to the RecyclerView on a predefined position public void insert ( int position, Data data) { list.add(position, data); notifyItemInserted(position); } // Remove a RecyclerView item containing a specified Data object public void remove (Data data) { int position = list.indexOf(data); list.remove(position); notifyItemRemoved(position); } }
Создание ViewHolder
RecyclerView
использует ViewHolder
для хранения ссылок на соответствующие представления для одной записи в RecyclerView
. Это решение позволяет избежать всех вызовов метода findViewById()
в адаптере, чтобы найти представления, которые будут заполнены данными.
Создайте файл с именем View_Holder.java со следующим классом:
public class View_Holder extends RecyclerView . ViewHolder { CardView cv; TextView title; TextView description; ImageView imageView; View_Holder(View itemView) { super (itemView); cv = (CardView) itemView.findViewById(R.id.cardView); title = (TextView) itemView.findViewById(R.id.title); description = (TextView) itemView.findViewById(R.id.description); imageView = (ImageView) itemView.findViewById(R.id.imageView); } }
LayoutManager
Менеджер макетов присоединяет, измеряет и размещает все дочерние представления RecyclerView
в режиме реального времени. По мере того как пользователь прокручивает представление, менеджер компоновки определяет, когда будут добавлены новые дочерние представления и когда старые дочерние представления будут отсоединены и удалены.
Эти реализации по умолчанию доступны:
-
LinearLayoutManager
— отображает элементы в вертикальной или горизонтальной прокрутке списка. -
GridLayoutManager
— отображает элементы в сетке. -
StaggeredGridLayoutManager
— отображает элементы в шахматном порядке.
Вы можете создать собственный LayoutManager
, расширив RecyclerView.LayoutManager
или одну из приведенных выше реализаций и переопределив необходимые методы.
Теперь, когда RecyclerView
завершен, следующий шаг — заполнить его некоторыми данными. В onCreate()
класса MainActivity
создайте экземпляр Recycler_View_Adapter
и предоставьте этому адаптеру список данных и контекст. Метод getApplication()
предоставит контекст приложения.
List<Data> data = fill_with_data(); RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview); Recycler_View_Adapter adapter = new Recycler_View_Adapter(data, getApplication()); recyclerView.setAdapter(adapter); recyclerView.setLayoutManager( new LinearLayoutManager( this ));
Нам также понадобятся примеры данных, для этого примера я создал функцию по умолчанию, в реальном приложении это может происходить из источника данных.
public List<Data> fill_with_data () { List<Data> data = new ArrayList<>(); data.add( new Data( "Batman vs Superman" , "Following the destruction of Metropolis, Batman embarks on a personal vendetta against Superman " , R.drawable.ic_action_movie)); data.add( new Data( "X-Men: Apocalypse" , "X-Men: Apocalypse is an upcoming American superhero film based on the X-Men characters that appear in Marvel Comics " , R.drawable.ic_action_movie)); data.add( new Data( "Captain America: Civil War" , "A feud between Captain America and Iron Man leaves the Avengers in turmoil. " , R.drawable.ic_action_movie)); data.add( new Data( "Kung Fu Panda 3" , "After reuniting with his long-lost father, Po must train a village of pandas" , R.drawable.ic_action_movie)); data.add( new Data( "Warcraft" , "Fleeing their dying home to colonize another, fearsome orc warriors invade the peaceful realm of Azeroth. " , R.drawable.ic_action_movie)); data.add( new Data( "Alice in Wonderland" , "Alice in Wonderland: Through the Looking Glass " , R.drawable.ic_action_movie)); return data; }
Вы можете найти файлы значков для кнопки ic_action_movie
, упомянутой выше, здесь .
Это завершает все шаги по настройке RecyclerView.
Анимация повторяется с ItemAnimator
Пока что я объяснил все, что нужно для создания собственного RecyclerView
и объяснил, что делает каждая часть структуры RecyclerView
. Теперь давайте сделаем вещи более интересными с помощью анимации предметов.
RecyclerView.ItemAnimator
— это класс, который определяет анимацию, выполняемую над элементами, и будет анимировать изменения ViewGroup
такие как добавление / удаление / выбор, уведомленные адаптеру. DefaultItemAnimator
— это базовая анимация, доступная по умолчанию с помощью RecyclerView
.
Чтобы настроить DefaultItemAnimator
добавьте аниматор элементов в RecyclerView
. Этот код замедляет процесс добавления и удаления элементов из RecyclerView
.
Добавьте это после нашего последнего кода в методе onCreate
:
RecyclerView.ItemAnimator itemAnimator = new DefaultItemAnimator(); itemAnimator.setAddDuration( 1000 ); itemAnimator.setRemoveDuration( 1000 ); recyclerView.setItemAnimator(itemAnimator);
Еще один подход к анимации элементов RecyclerView
— использование Android-интерполяторов. Согласно developer.android.com , интерполятор определяет скорость изменения анимации.
Следующие примеры представляют две анимации, которые я реализовал, используя эти интерполяторы. Сохраните файлы в / anim / в папке ресурсов проекта.
anticipate_overshoot_interpolator.xml
<?xml version="1.0" encoding="utf-8"?> < set xmlns:android = "http://schemas.android.com/apk/res/android" android:interpolator = "@android:anim/anticipate_overshoot_interpolator" > < translate android:fromYDelta = "-50%p" android:toYDelta = "0" android:duration = "2000" /> </ set >
bounce_interpolator.xml
<?xml version="1.0" encoding="utf-8"?> < set xmlns:android = "http://schemas.android.com/apk/res/android" android:interpolator = "@android:anim/bounce_interpolator" > < translate android:duration = "1500" android:fromYDelta = "-150%p" android:toYDelta = "0" /> </ set >
Теперь вернемся к RecyclerView
. Внутри класса RecyclerView
Adapter добавьте следующую функцию.
public void animate (RecyclerView.ViewHolder viewHolder) { final Animation animAnticipateOvershoot = AnimationUtils.loadAnimation(context, R.anim.bounce_interpolator); viewHolder.itemView.setAnimation(animAnticipateOvershoot); }
Если вы присмотритесь к loadAnimation()
, ему потребуется параметр контекста, и поэтому конструктор адаптера был изменен в первую очередь. Теперь мы можем анимировать RecyclerView
. Внутри onBindViewHolder()
вызовите функцию onBindViewHolder()
и передайте параметр View_Holder
класса View_Holder
.
Запустите приложение и проверьте анимацию. Вот как bounce_interpolator
анимация bounce_interpolator
.
Далее давайте протестируем анимацию StaggeredGrid
, но сначала я установлю для менеджера компоновки RecyclerView
значение StaggeredGrid
в StaggeredGrid
Main Activity onCreate()
с двумя столбцами и вертикальной прокруткой.
recyclerView.setLayoutManager( new StaggeredGridLayoutManager( 2 ,StaggeredGridLayoutManager.VERTICAL));
Использовать этот метод анимации в RecyclerView
очень просто. Если вам нужны новые анимации, просто измените тип интерполяции в приведенных выше примерах, установите параметры по своему усмотрению, и у вас будет новый стиль анимации.
onItemClickListener
проблемы с настройкой onItemClickListener
для элементов RecyclerView
?
Хотя отображение элементов в RecyclerView
лучше с точки зрения производительности, чем его предшественники, ListView
и GridView
. Эти представления имеют возможность добавлять прослушиватели щелчков элементов, чтобы перехватить, какой элемент списка был нажат.
Вот как это выглядит на ListView
:
ListView listView = ...; listView.setOnItemClickListener( new AdapterView.OnItemClickListener() { @Override public void onItemClick (AdapterView<?> parent, View view, int position, long id) { } });
К сожалению, для RecyclerView
нет такой вещи, но следующее решение может обеспечить решение этой проблемы.
Создайте interface
Java, как показано ниже.
public interface RecyclerViewItemClickListener { public void onClick (View view, int position); public void onLongClick (View view, int position); }
Как видно из этого interface
идея состоит в том, чтобы предоставить решение, аналогичное решению ListView
для onLongClick
и onLongClick
элементов RecyclerView
.
Чтобы обнаружить элемент RecyclerView
которому щелкают, нам нужен вспомогательный класс.
public class CustomRVItemTouchListener implements RecyclerView . OnItemTouchListener { //GestureDetector to intercept touch events GestureDetector gestureDetector; private RecyclerViewItemClickListener clickListener; public CustomRVItemTouchListener (Context context, final RecyclerView recyclerView, final RecyclerViewItemClickListener clickListener) { this .clickListener = clickListener; gestureDetector = new GestureDetector(context, new GestureDetector.SimpleOnGestureListener() { @Override public boolean onSingleTapUp (MotionEvent e) { return true ; } @Override public void onLongPress (MotionEvent e) { //find the long pressed view View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null ) { clickListener.onLongClick(child, recyclerView.getChildLayoutPosition(child)); } } }); } @Override public boolean onInterceptTouchEvent (RecyclerView recyclerView, MotionEvent e) { View child = recyclerView.findChildViewUnder(e.getX(), e.getY()); if (child != null && clickListener != null && gestureDetector.onTouchEvent(e)) { clickListener.onClick(child, recyclerView.getChildLayoutPosition(child)); } return false ; } @Override public void onTouchEvent (RecyclerView rv, MotionEvent e) { } @Override public void onRequestDisallowInterceptTouchEvent ( boolean disallowIntercept) { } }
По сути, этот класс обнаруживает элемент RecyclerView
в позиции (X, Y), где был нажат экран. Этот класс полезен для обоих типов кликов, созданных интерфейсом.
Наконец, вот как этот новый приемник щелчков реализован для RecyclerView
.
recyclerView.addOnItemTouchListener( new CustomRVItemTouchListener( this , recyclerView, new RecyclerViewItemClickListener() { @Override public void onClick (View view, int position) { } @Override public void onLongClick (View view, int position) { } }));
Как вы можете видеть, пользовательский сенсорный слушатель предоставляет ссылку на элемент RecyclerView
который был нажат, и его положение в списке.
Вывод
RecyclerView
— это гибкий шаблон для повторного использования представлений, исключающий имитации ListView
и GridView
благодаря чему анимации элементов можно легко устанавливать и использовать повторно. Привыкание к разработке с помощью RecyclerView
может сначала потребовать некоторой работы, но оно того стоит.
Если у вас есть какие-либо вопросы или комментарии, пожалуйста, дайте мне знать ниже .
Чтобы узнать больше об Android, просмотрите наш учебник « Вывод данных в пользовательский интерфейс приложения Android» .