Mamy zdefiniowane rodzaje elementów, obiekty typu view holder dla nich, abstrakcyjny adapter oparty o typy generyczne, oraz renderujący listę RecyclerView. Pozostało nam już tylko podłączyć do elementów listy metody obsługujące kliknięcia. W głównej aktywności dodamy też metody pozwalające na zareagowanie w różny sposób dla każdego typu elementu.
9. Listenery kliknięc w elementy listy
Najpierw zdefiniujmy interfejs listenera:
ItemClickListener.java
public interface ItemClickListener {
void onItemClick(View view, int position);
}
Metoda onItemClick będzie przyjmowała widok z którego przyszło kliknięcie oraz pozycję na liście klikniętego elementu.
Poprawmy definicję adaptera abstrakcyjnego tak, aby implementował ten interfejs:
AbstractAdapter.java
public abstract class AbstractAdapter<I, VH extends AbstractRecyclerViewHolder>
extends RecyclerView.Adapter<VH> implements ItemClickListener {
// ...
}
10. Implementacja metody onClick w AbstractRecyclerViewHolder
Aby nasze elementy obsługiwały kliknięcia musimy w holderze abstrakcyjnym zaimplementować OnClickListener. Dzięki temu będziemy mogli nadpisać metodę onClick. Będzie ona pobierać pozycję elementu z adeptera i wywoływać metodę przekazanego listenera onItemClick. Potrzebny jest jeszcze tylko setter dla listenera i pole typu ItemClickListener. Całość powinna wyglądać następująco:
AbstractRecyclerViewHolder.java
public abstract class AbstractRecyclerViewHolder<T> extends RecyclerView.ViewHolder
implements View.OnClickListener {
protected ItemClickListener mItemClickListener;
public AbstractRecyclerViewHolder(View itemView) {
super(itemView);
super.itemView.setOnClickListener(this);
}
public void setItemClickListener(ItemClickListener itemClickListener) {
mItemClickListener = itemClickListener;
}
@Override
public void onClick(View view) {
if (mItemClickListener != null) {
int adapterPosition = getAdapterPosition();
if (adapterPosition != RecyclerView.NO_POSITION) {
mItemClickListener.onItemClick(view, adapterPosition);
}
}
}
// ...
}
11. Implementacja metody onItemClick w adapterze
Teraz wszystkie konkretne adaptery dziedziczące po klasie AbstractAdapter będą musiały zaimplementować metodę onItemClick. Dodajmy więc ją do RecycledListAdapter’a, wypełnieniem jej zajmiemy się później:
RecycledListAdapter.java
@Override
public void onItemClick(View view, int position) {
// TODO: do something on item click
}
Pozostało podłączyć listener z view holderem. W tym celu w metodzie onCreateViewHolder adapterów konkretnych dodajemy na końcu linijkę kodu:
RecycledListAdapter.java
@Override
public AbstractRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// ...
viewHolder.setItemClickListener(this);
return viewHolder;
}
Kliknięcie w dowolny element recyclera spowoduje wywołanie metody onItemClick w naszej klasie RecycledListAdapter. Należy mieć na uwadze, że obsługujemy również kliknięcie w nagłówek sekcji, tak więc w naszym przypadku, kiedy pierwszym elementem recyclera jest nagłówek, otrzymamy dla niego wartość argumentu position = 0. Możemy zróżnicować obsługę kliknięcia w zależności od typu elementu, np. ignorować kliknięcia w nagłówek a dla pozostałych typów wywołać odpowiednią metodę (callback) w głównej aktywności. Użyjemy do tego metody getItemViewType():
RecycledListAdapter.java
@Override
public void onItemClick(View view, int position) {
Log.d(TAG, "onItemClick: " + Integer.toString(position));
int itemViewType = getItemViewType(position);
switch (RecycledListItem.ItemType.values()[itemViewType]) {
case BOOK:
listener.onBookItemClickListener((Book) items.get(position).getValue());
break;
case DVD:
listener.onDVDItemClickListener((Dvd) items.get(position).getValue());
break;
case HEADER:
default:
break;
}
No dobrze, ale przecież nie mamy takich metod w MainActivity, jak również nie ma też obiektu listener w klasie RecycledListAdapter.
12. Metody w MainActivity reagujące na kliknięcie w element listy
Najpierw zdefiniujmy interfejs:
RecycledListItemClickListener.java
public interface RecycledListItemClickListener {
void onBookItemClickListener(Book model);
void onDVDItemClickListener(Dvd model);
}
Będziemy mieć dwie metody które w wyniku kliknięcia w element listy otrzymają model odpowiadający temu elementowi.
Zaimplementujmy je w głównej aktywności, oraz przekażmy do adaptera listener, którym od teraz jest nasza aktywność.
MainActivity.java
public class MainActivity extends AppCompatActivity
implements RecycledListItemClickListener {
// ...
protected void initViews() {
listRecyclerView = (RecyclerView) findViewById(R.id.list_recycler);
adapter = new RecycledListAdapter();
listRecyclerView.setLayoutManager(new LinearLayoutManager(this));
listRecyclerView.setAdapter(adapter);
items = MockAPI.retrieveData();
adapter.setItems(items);
adapter.setItemClickListener(this)
}
@Override
public void onBookItemClickListener(Book book) {
Log.d(TAG, "Book clicked: " + book.toString());
}
@Override
public void onDVDItemClickListener(Dvd dvd) {
Log.d(TAG, "DVD clicked: " + dvd.toString());
}
}
Brakuje jeszcze tylko metody ustawiające w adapterze listener:
RecycledListAdapter.java
public class RecycledListAdapter extends AbstractAdapter<RecycledListItem, AbstractRecyclerViewHolder> {
private static final String TAG = RecycledListAdapter.class.getSimpleName();
private RecycledListItemClickListener listener;
// ...
public void setItemClickListener(RecycledListItemClickListener listener) {
this.listener = listener;
}
}
W ten sposób możemy sobie obsłużyć w różny sposób kliknięcia w różne elementy listy. Z głównej aktywności możemy sobie otwierać np. nową aktywność z widokami szczegółów np. książki czy płyty DVD – mającymi przecież różne layouty, logikę i funkcjonalności.
To wszystko jeśli chodzi o tworzenie recyclera z różnymi typami elementów. Teraz w prosty sposób można rozszerzać listę o kolejne typy i ich zróżnicowaną obsługę. Cały layout ekranu funkcjonalnego w naszej aplikacji można zbudować w oparciu o przewijalny recycler i poszczególne klocki interfejsu użytkownika będącymi jego elementami. Oczywiście pozostało jeszcze zasilić naszą listę konkretnymi danymi pochodzącymi z realnego API lub bazy danych, ale to już temat na osobny tutorial.
Cały kod dostępny jest na GitHubie.