Tworzenie list dynamicznych za pomocą RecyclerView Część Android Jetpack.
RecyclerView ułatwia wyświetlanie dużych zbiorów danych. Ty dostarczasz dane i określasz wygląd każdego elementu, a biblioteka RecyclerView dynamicznie tworzy elementy, gdy są potrzebne.
Jak sama nazwa wskazuje, RecyclerView przetwarza te poszczególne elementy. Gdy element zniknie z ekranu, RecyclerView nie zniszczy jego widoku. Zamiast tego RecyclerView ponownie używa widoku dla nowych elementów, które zostały przewinięte na ekranie. RecyclerView zwiększa wydajność i szybkość reakcji aplikacji oraz zmniejsza zużycie energii.
Kluczowe klasy
Lista dynamiczna jest tworzona przez kilka klas.
RecyclerView
to kolumnaViewGroup
zawierająca widoki odpowiadające Twoim danym. Jest to widok, więc możesz dodaćRecyclerView
do układu w taki sam sposób, w jaki dodajesz inne elementy interfejsu.Każdy element na liście jest zdefiniowany przez obiekt holder widoku. Gdy tworzysz widok, nie jest on powiązany z żadnymi danymi. Po utworzeniu
RecyclerView
wiąże go z danymi. Widok uchwytu definiujesz, rozszerzając elementRecyclerView.ViewHolder
.RecyclerView
wysyła żądania widoków i wiąże je z ich danymi, wywołując metody w adapterze. Definiujesz go, rozszerzającRecyclerView.Adapter
.Menedżer układu rozmieszcza poszczególne elementy na liście. Możesz użyć jednego z menedżerów układu udostępnianych przez bibliotekę RecyclerView lub zdefiniować własnego. Menedżerowie układów są oparte na abstrakcyjnej klasie
LayoutManager
biblioteki.
Sposób działania wszystkich elementów możesz sprawdzić w próbnej aplikacji RecyclerView (Kotlin) lub próbnej aplikacji RecyclerView (Java).
Wdrażanie RecyclerView
Jeśli chcesz użyć RecyclerView, musisz wykonać kilka czynności. Zostały one szczegółowo opisane w następnych sekcjach.
Określ wygląd listy lub siatki. Zazwyczaj możesz użyć jednego ze standardowych menedżerów układu biblioteki RecyclerView.
Zaprojektuj wygląd i działanie każdego elementu na liście. Na podstawie tego projektu rozszerz klasę
ViewHolder
. Twoja wersjaViewHolder
oferuje wszystkie funkcje dla elementów listy. Twój widok uchwytu to opakowanieView
, a tym widokiem zarządzaRecyclerView
.Zdefiniuj
Adapter
, który łączy Twoje dane z widokamiViewHolder
.
Dostępne są też zaawansowane opcje dostosowywania, które umożliwiają dostosowanie RecyclerView do Twoich potrzeb.
Planowanie układu
Elementy w RecyclerView są uporządkowane według klasy LayoutManager
. Biblioteka RecyclerView udostępnia 3 menedżerów układu, które obsługują większość typowych sytuacji związanych z układem:
LinearLayoutManager
umieszcza elementy na liście jednowymiarowej.GridLayoutManager
umieszcza elementy w dwudwumiarowej siatce:- Jeśli siatka jest ułożona w poziomie,
GridLayoutManager
stara się, aby wszystkie elementy w każdym wierszu miały taką samą szerokość i wysokość, ale różne wiersze mogą mieć różne wysokości. - Jeśli siatka jest ułożona poziomo,
GridLayoutManager
stara się, aby wszystkie elementy w każdej kolumnie miały taką samą szerokość i wysokość, ale różne kolumny mogą mieć różne szerokości.
- Jeśli siatka jest ułożona w poziomie,
StaggeredGridLayoutManager
jest podobne doGridLayoutManager
, ale nie wymaga, aby elementy w wierszu miały tę samą wysokość (w przypadku siatek pionowych) lub aby elementy w tej samej kolumnie miały tę samą szerokość (w przypadku siatek poziomych). W efekcie elementy w wierszu lub kolumnie mogą być przesunięte względem siebie.
Musisz też zaprojektować układ poszczególnych elementów. Ten układ jest potrzebny podczas projektowania widoku zasobnika, jak opisano w następnej sekcji.
Wdrażanie adaptera i uchwytu
Po ustaleniu układu musisz zaimplementować Adapter
i ViewHolder
. Te 2 klasy współpracują ze sobą, aby określić sposób wyświetlania danych. ViewHolder
to element opakowujący View
, który zawiera układ pojedynczego elementu na liście. W razie potrzeby usługa Adapter
tworzy obiekty ViewHolder
, a także ustawia dane dla tych widoków. Proces kojarzenia widoków z ich danymi nazywa się wiązaniem.
Podczas definiowania adaptera zastępujesz 3 kluczowe metody:
onCreateViewHolder()
:RecyclerView
wywołuje tę metodę, gdy musi utworzyć nowy obiektViewHolder
. Metoda tworzy i inicjalizuje obiektViewHolder
oraz powiązany z nim obiektView
, ale nie wypełnia zawartości widoku – obiektViewHolder
nie został jeszcze powiązany z określonymi danymi.onBindViewHolder()
:RecyclerView
wywołuje tę metodę, aby powiązaćViewHolder
z danymi. Metoda pobiera odpowiednie dane i wypełnia nimi układ widoku. Jeśli na przykładRecyclerView
wyświetla listę nazw, metoda może znaleźć odpowiednią nazwę na liście i wypełnić widgetTextView
widoczny dla właściciela.getItemCount()
:RecyclerView
wywołuje tę metodę, aby uzyskać rozmiar zbioru danych. Na przykład w aplikacji książki adresowej może to być łączna liczba adresów. RecyclerView używa tego, aby określić, kiedy nie ma już żadnych elementów do wyświetlenia.
Oto typowy przykład prostego adaptera z zagnieżdżonym elementem ViewHolder
, który wyświetla listę danych. W tym przypadku RecyclerView wyświetla prostą listę elementów tekstowych. Do adaptera przekazywana jest tablica ciągów znaków zawierająca tekst elementów ViewHolder
.
Kotlin
class CustomAdapter(private val dataSet: Array<String>) : RecyclerView.Adapter<CustomAdapter.ViewHolder>() { /** * Provide a reference to the type of views that you are using * (custom ViewHolder) */ class ViewHolder(view: View) : RecyclerView.ViewHolder(view) { val textView: TextView init { // Define click listener for the ViewHolder's View textView = view.findViewById(R.id.textView) } } // Create new views (invoked by the layout manager) override fun onCreateViewHolder(viewGroup: ViewGroup, viewType: Int): ViewHolder { // Create a new view, which defines the UI of the list item val view = LayoutInflater.from(viewGroup.context) .inflate(R.layout.text_row_item, viewGroup, false) return ViewHolder(view) } // Replace the contents of a view (invoked by the layout manager) override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.textView.text = dataSet[position] } // Return the size of your dataset (invoked by the layout manager) override fun getItemCount() = dataSet.size }
Java
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.ViewHolder> { private String[] localDataSet; /** * Provide a reference to the type of views that you are using * (custom ViewHolder) */ public static class ViewHolder extends RecyclerView.ViewHolder { private final TextView textView; public ViewHolder(View view) { super(view); // Define click listener for the ViewHolder's View textView = (TextView) view.findViewById(R.id.textView); } public TextView getTextView() { return textView; } } /** * Initialize the dataset of the Adapter * * @param dataSet String[] containing the data to populate views to be used * by RecyclerView */ public CustomAdapter(String[] dataSet) { localDataSet = dataSet; } // Create new views (invoked by the layout manager) @Override public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) { // Create a new view, which defines the UI of the list item View view = LayoutInflater.from(viewGroup.getContext()) .inflate(R.layout.text_row_item, viewGroup, false); return new ViewHolder(view); } // Replace the contents of a view (invoked by the layout manager) @Override public void onBindViewHolder(ViewHolder viewHolder, final int position) { // Get element from your dataset at this position and replace the // contents of the view with that element viewHolder.getTextView().setText(localDataSet[position]); } // Return the size of your dataset (invoked by the layout manager) @Override public int getItemCount() { return localDataSet.length; } }
Układ każdego elementu widoku jest definiowany w pliku układu XML, jak zwykle.
W tym przypadku aplikacja ma plik text_row_item.xml
o takiej treści:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="@dimen/list_item_height"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:gravity="center_vertical">
<TextView
android:id="@+id/textView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/element_text"/>
</FrameLayout>
Dalsze kroki
Poniższy fragment kodu pokazuje, jak używać funkcji RecyclerView
.
Kotlin
class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) val dataset = arrayOf("January", "February", "March") val customAdapter = CustomAdapter(dataset) val recyclerView: RecyclerView = findViewById(R.id.recycler_view) recyclerView.layoutManager = LinearLayoutManager(this) recyclerView.adapter = customAdapter } }
Java
RecyclerView recyclerView = findViewById(R.id.recycler_view); recyclerView.layoutManager = new LinearLayoutManager(this) recyclerView.setAdapter(customAdapter);
Biblioteka oferuje też wiele sposobów dostosowywania implementacji. Więcej informacji znajdziesz w artykule Zaawansowana personalizacja RecyclerView.
Włącz wyświetlanie bez ramki
Aby włączyć wyświetlanie bez ramki w przypadku RecyclerView
:
- Aby skonfigurować wyświetlacz bez ramki z obsługą zgodności wstecznej, wywołaj funkcję
enableEdgeToEdge()
. - Jeśli elementy listy początkowo zachodzą na paski systemu, zastosuj wgłębienia w
RecyclerView
. Możesz to zrobić, ustawiając wartośćandroid:fitsSystemWindows
natrue
lub używającViewCompat.setOnApplyWindowInsetsListener
. - Aby elementy listy były wyświetlane pod paskami systemu podczas przewijania, ustaw parametr
android:clipToPadding
nafalse
w elementachRecyclerView
.
Ten film pokazuje RecyclerView
z wyświetlaczem od krawędzi do krawędzi wyłączonym (po lewej) i włączonym (po prawej):
Przykładowy kod wstawki:
Kotlin
ViewCompat.setOnApplyWindowInsetsListener( findViewById(R.id.my_recycler_view) ) { v, insets -> val innerPadding = insets.getInsets( WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() // If using EditText, also add // "or WindowInsetsCompat.Type.ime()" to // maintain focus when opening the IME ) v.setPadding( innerPadding.left, innerPadding.top, innerPadding.right, innerPadding.bottom) insets }
Java
ViewCompat.setOnApplyWindowInsetsListener( activity.findViewById(R.id.my_recycler_view), (v, insets) -> { Insets innerPadding = insets.getInsets( WindowInsetsCompat.Type.systemBars() | WindowInsetsCompat.Type.displayCutout() // If using EditText, also add // "| WindowInsetsCompat.Type.ime()" to // maintain focus when opening the IME ); v.setPadding( innerPadding.left, innerPadding.top, innerPadding.right, innerPadding.bottom ); return insets; } );
Plik XML RecyclerView
:
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:clipToPadding="false"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Dodatkowe materiały
Więcej informacji o testowaniu na Androidzie znajdziesz w tych materiałach.