Если Вы начали изучение программирование под платформу Android, очень скоро от «Hello World!» Вы приступите к созданию более разумных приложений. Почти что в каждом приложении под Android Вы будете встречаться с использованием списков, их реализация не такая уж и тривиальная, как это можно себе представить на первых порах, особенно для новичков! Очень важно сразу разобраться в концепции и набить руку, иначе после придётся часто возвращаться к старым наработкам для выдёргивания кусков рабочего кода.
Я расскажу как реализован список с помощью виджета RecyclerView, любезно предоставленного нам корпорацией Google. Виджет развивается и есть смысл хорошенько познакомится с его реализацией и предоставляемым API. На скришоте пример того, как должен выглядеть в итоге наш список.
Наше приложение будет состоять из фабрики клонов, которая будет генерировать 100 объектов, и одного активити, внутри которого опишем необходимые классы, для работы с RecyclerView.
Также нам будет необходимо создать 2 файла-макета. Один activity_main.xml для RecycleView, в нём будет описан только сам виджет. Второй list_item_person.xml для представления отдельного элемента списка.
activity_main.xml
<?xml version="1.0" encoding="utf-8"?> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@color/colorPrimary" android:scrollbars="vertical" />
list_item_person.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_marginBottom="2dp" android:orientation="vertical" android:padding="4dp" android:background="#FFFFFF" > <TextView android:id="@+id/personNameView" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFFFFF"/> <TextView android:id="@+id/personAdressView" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#FFFFFF"/> <LinearLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:orientation="horizontal" android:background="#FFFFFF" > <TextView android:id="@+id/personSexView" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginRight="10dp" android:layout_weight="1" android:background="#FFFFFF"/> <TextView android:id="@+id/personAgeView" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:background="#FFFFFF"/> </LinearLayout> </LinearLayout>
Для начала необходимо выполнить разрешение зависимостей. Нажимаем в AndroidStudio «Ctrl+Alt+Shift+S«, жмём (1) добавить зависимость, набираем recyclerview (2), жмём поиск (3), выбираем нужную версию (4), ок (5).
Наш будущий лист необходимо будет чем то заполнить. Я создал «фабрику клонов», которая создаёт 100 объектов с некими данными и с помощью метода getCloneList любезно возвращает лист, полный объектов.
import java.util.ArrayList; import java.util.List; public class CloneFactory { private static CloneFactory sCloneFactory; private static List<Person> mPersonList; public class Person { private String name; private int age; private String adress; private boolean sex; public Person() { } public Person(String name, int age, String adress, boolean sex) { this.name = name; this.age = age; this.adress = adress; this.sex = sex; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getAdress() { return adress; } public void setAdress(String adress) { this.adress = adress; } public boolean isSex() { return sex; } public void setSex(boolean sex) { this.sex = sex; } } private CloneFactory() { mPersonList = new ArrayList<>(100); for (int i = 0; i < 100; i++) { if(i % 2 == 0){ mPersonList.add(new Person("Иванов Иван клон#"+i, 25, "Москва", true)); }else{ mPersonList.add(new Person("Петрова Мария клон#"+i, 33, "Санкт-Петербург", false)); } } } public static List<Person> getCloneList() { if(sCloneFactory == null){ sCloneFactory = new CloneFactory(); } return mPersonList; } }
Всё остальное происходит в MainActivity. Для того что бы RecycleView завёлся, необходимо запастись некоторыми классами. Ему необходим адаптер — класс, наследующийся от абстрактного класса RecyclerView.Adapter.
private class PersonAdapter extends RecyclerView.Adapter<PersonHolder>{ ... }
Этот адаптер выполняет связывание объектов нашей модели (В нашем случае персон-клонов) с представлениями, которые будут отображаться в списке(во вьюхе RecyclerView). Связывает он их не напрямую, а через второй класс. А второй чудо-класс — это наследник от RecyclerView.ViewHolder, реализующий паттерн холдер.
private class PersonHolder extends RecyclerView.ViewHolder{ ... }
По нему параметризуется адаптер, его роль сперва, при первом создании, запомнить все ссылки на виджеты той вьюшки, которую ему передадут в аргументе конструктора, а после, просто, по просьбе адаптера наполнять эти виджеты данными из объекта модели, которого передаёт ему в метод public void bindCrime(Person person) адаптер. Перечитайте ещё разок предыдущее предложение, оно является одним из ключевых в статье. Если плохо понятно, я постараюсь в приведённом ниже коде донести мысль в комментах. С помощью того, что он не ищет каждый раз ссылки на виджеты, а постоянно хранит их на готове, достигается скорость отработки UI RecyclerView.
import android.app.Activity; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.TextView; import java.util.List; import info.javaway.maks.testapp.CloneFactory.*; /*Класс-активити, в котором происходит вся кухня. Нет смысла распыляться на фрагменты в данном случае, наша задача максимально понять как работает RecyclerView*/ public class MainActivity extends Activity { //ссылка на адаптер, класс который знает всё о модели и дёргает методы холдера private PersonAdapter mAdapter; //ссылка на вьюшку из представления private RecyclerView mRecyclerView; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); //Заполняем лайаут Activity контейнером с единственным виджетом RecyclerView setContentView(R.layout.activity_main); //Находим ссылку на контейнер - виджет mRecyclerView = (RecyclerView) findViewById(R.id.recyclerView); //LinearLayoutManager занимается размещением объектов на экране и прокруткой mRecyclerView.setLayoutManager(new LinearLayoutManager(this)); //Подготавливаем армию клонов List<CloneFactory.Person> personList = CloneFactory.getCloneList(); //Создаём экземпляр адаптера и передаём ему под командование наших клонов. Далее руководит ими он mAdapter = new PersonAdapter(personList); //Назначаем вьюхе адаптером наш экземпляр PersonAdapter mRecyclerView.setAdapter(mAdapter); } /*Класс PersonHolder занят тем, что держит на готове ссылки на элементы виджетов, которые он с радостью наполнит данными из объекта модели в методе bindCrimе. Этот класс используется только адаптером в коде ниже, адаптер дёргает его и поручает грязную работу по заполнению виджетов*/ private class PersonHolder extends RecyclerView.ViewHolder{ private TextView mPersonNameTextView; private TextView mPersonAdressTextView; private TextView mPersonSexTextView; private TextView mPersonAgeTextView; private CloneFactory.Person mPerson; public PersonHolder(View itemView) { super(itemView); mPersonNameTextView = (TextView) itemView.findViewById(R.id.personNameView); mPersonAdressTextView = (TextView) itemView.findViewById(R.id.personAdressView); mPersonSexTextView = (TextView) itemView.findViewById(R.id.personSexView); mPersonAgeTextView = (TextView) itemView.findViewById(R.id.personAgeView); } //Метод, связывающий ранее добытые в конструкторе ссылки с данными модели public void bindCrime(Person person) { mPerson = person; mPersonNameTextView.setText(mPerson.getName()); mPersonAdressTextView.setText(mPerson.getAdress()); mPersonAgeTextView.setText(""+mPerson.getAge()); if(mPerson.isSex()){ mPersonSexTextView.setText("Мужчина"); }else { mPersonSexTextView.setText("Женщина"); } } } //Наш адаптер, мост между фабрикой клонов и выводом их на экран. //Его методы будет дёргать LinearLayoutManager, назныченный вьюшке //RecyclerView в методе onCreate нашей активити private class PersonAdapter extends RecyclerView.Adapter<PersonHolder> { private List<CloneFactory.Person> mPersons; public PersonAdapter(List<CloneFactory.Person> persons) { mPersons = persons; } //Создаёт пустую вьюшку,оборачивает её в PersonHolder. //Дальше забота по наполнению этой вьюшки ложиться именно на объект PersonHolder'а @Override public PersonHolder onCreateViewHolder(ViewGroup parent, int viewType) { LayoutInflater li = getLayoutInflater(); View view = li.inflate(R.layout.list_item_person, parent, false); return new PersonHolder(view); } //Дёргает метод холдера при выводе нового элемента списка на экран, //передавая ему актуальный объект модели для разбора и представления @Override public void onBindViewHolder(PersonHolder holder, int position) { CloneFactory.Person person = mPersons.get(position); holder.bindCrime(person); } //Возвращает размер хранилища моделей @Override public int getItemCount() { return mPersons.size(); } } }
Вы должны были заметить, что в методе onCreate, как только мы создали виджет RecyclerView, сразу же присваеваем ему другой объект LinearLayouManager. Дело в том что виджет RecyclerView очень ленивый и любит всю работу поручать другим. В том числе размещение элементов на экране и прокрутку списка он поручает именно LinearLayouManager‘у. Если не выполнить это присвоение, получим ошибку. Также на плечи бедного LinearLayouManager‘а ложиться работа по дёрганию методов onCreateViewHolder(), onBindViewHolder() и getItemCount() нашего адаптера.
В принципе это всё, что необходимо для понимания как работать со списком. Для более глубокого понимания есть описание API на сайте разработчиков, а также статья на Хабре. Надеюсь что Вы смогли понять как использовать список в приложении на андроид!
Булевое поле sex — это шовинизм : ))))
Да и метод isSex() выглядит странновато.
Sex по английски сначит не только секс но и пол человека. Вогт так вот.
Спасибо за урок! Подскажите пожалуйста как теперь сделать избранное для пользователя и возможность добавлять в избранное из этого списка, нажав на сердечко или звездочку…