Tworzenie list dynamicznych za pomocą RecyclerView, części Androida Jetpack.
RecyclerView ułatwia efektywne wyświetlanie dużych zbiorów danych. Ty dostarczasz dane i określasz, jak ma wyglądać każdy element, a biblioteka RecyclerView dynamicznie tworzy elementy, gdy są potrzebne.
Jak sama nazwa wskazuje, RecyclerView poddaje recyklingowi te poszczególne elementy. Gdy element znika z ekranu, RecyclerView nie niszczy jego widoku. Zamiast tego RecyclerView ponownie wykorzystuje widok dla nowych elementów, które zostały przewinięte na ekranie. RecyclerView zwiększa wydajność i szybkość działania aplikacji oraz zmniejsza zużycie energii.
Najważniejsze zajęcia
Do utworzenia listy dynamicznej służy kilka klas.
RecyclerViewtoViewGroupzawierający widoki odpowiadające Twoim danym. Jest to widok, więc dodajeszRecyclerViewdo układu tak samo jak każdy inny element interfejsu.Każdy element na liście jest definiowany przez obiekt view holder. Po utworzeniu obiektu ViewHolder nie są z nim powiązane żadne dane. Po utworzeniu obiektu ViewHolder
RecyclerViewwiąże go z danymi. Uchwyt widoku definiujesz, rozszerzając klasęRecyclerView.ViewHolder.RecyclerViewżąda widoków i wiąże je z danymi, wywołując metody w adapterze. Adapter definiuje się przez rozszerzenie klasyRecyclerView.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łasny. Wszystkie menedżery układu są oparte na klasie abstrakcyjnej
LayoutManagerbiblioteki.
Możesz zobaczyć, jak wszystkie elementy pasują do siebie, w przykładowej aplikacji RecyclerView (Kotlin) lub przykładowej aplikacji RecyclerView (Java).
Etapy wdrażania RecyclerView
Jeśli chcesz używać RecyclerView, musisz wykonać kilka czynności. Zostały one szczegółowo opisane w sekcjach poniżej.
Zdecyduj, jak ma wyglądać lista lub siatka. Zwykle możesz użyć jednego ze standardowych menedżerów układu biblioteki RecyclerView.
Zaprojektuj wygląd i zachowanie każdego elementu na liście. Na podstawie tego projektu rozszerz klasę
ViewHolder. Twoja wersjaViewHolderzapewnia wszystkie funkcje dla elementów listy. Obiekt ViewHolder jest kodem otaczającym obiektView, a widokiem zarządzaRecyclerView.Zdefiniuj
Adapter, który powiąże Twoje dane z widokamiViewHolder.
Dostępne są też zaawansowane opcje dostosowywania, które pozwalają dostosować RecyclerView do konkretnych potrzeb.
Planowanie układu
Elementy w RecyclerView są rozmieszczane przez klasę LayoutManager. Biblioteka RecyclerView udostępnia 3 menedżery układu, które obsługują najczęstsze sytuacje związane z układem:
LinearLayoutManagerporządkuje elementy na jednowymiarowej liście.GridLayoutManagerrozmieszcza elementy w dwuwymiarowej siatce:- Jeśli siatka jest ułożona pionowo,
GridLayoutManagerpróbuje ustawić wszystkie elementy w każdym wierszu tak, aby miały tę samą szerokość i wysokość, ale różne wiersze mogą mieć różne wysokości. - Jeśli siatka jest ułożona poziomo,
GridLayoutManagerpróbuje sprawić, aby wszystkie elementy w każdej kolumnie miały tę samą szerokość i wysokość, ale różne kolumny mogą mieć różne szerokości.
- Jeśli siatka jest ułożona pionowo,
StaggeredGridLayoutManagerjest podobny doGridLayoutManager, ale nie wymaga, aby elementy w wierszu miały tę samą wysokość (w przypadku siatek pionowych) lub 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 będzie potrzebny podczas projektowania obiektu ViewHolder, jak opisano w następnej sekcji.
Implementowanie adaptera i obiektu ViewHolder
Po określeniu układu musisz wdrożyć Adapter i ViewHolder. Te 2 klasy współpracują ze sobą, aby określić sposób wyświetlania danych. ViewHolder to kod wokół View, który zawiera układ pojedynczego elementu na liście. Adapter tworzy obiekty ViewHolder w razie potrzeby i ustawia dane dla tych widoków. Proces powiązania widoków z danymi nazywa się wiązaniem.
Podczas definiowania adaptera zastępujesz 3 kluczowe metody:
onCreateViewHolder():RecyclerViewwywołuje tę metodę, gdy musi utworzyć nowy obiektViewHolder. Metoda tworzy i inicjuje obiektViewHolderoraz powiązany z nim obiektView, ale nie wypełnia zawartości widoku – obiektViewHoldernie został jeszcze powiązany z konkretnymi danymi.onBindViewHolder():RecyclerViewwywołuje tę metodę, aby powiązaćViewHolderz danymi. Metoda pobiera odpowiednie dane i używa ich do wypełnienia układu obiektu ViewHolder. Jeśli na przykładRecyclerViewwyświetla listę nazw, metoda może znaleźć odpowiednią nazwę na liście i wypełnić widżetTextViewobiektu ViewHolder.getItemCount():RecyclerViewwywoł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 tej metody, aby określić, kiedy nie ma już 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, tak jak zwykle.
W tym przypadku aplikacja ma taki plik text_row_item.xml:
<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ć 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 Zaawansowane dostosowywanie RecyclerView.
Włącz wyświetlanie bez ramki
Aby włączyć wyświetlanie bez ramki w przypadku RecyclerView, wykonaj te czynności:
- Skonfiguruj zgodne wstecznie wyświetlanie bez ramki, wywołując funkcję
enableEdgeToEdge(). - Jeśli elementy listy początkowo nakładają się na paski systemowe, zastosuj wstawki w
RecyclerView. Możesz to zrobić, ustawiającandroid:fitsSystemWindowsnatruelub używającViewCompat.setOnApplyWindowInsetsListener. - Zezwól na rysowanie elementów listy pod paskami systemowymi podczas przewijania, ustawiając wartość
android:clipToPaddingnafalsewRecyclerView.
Poniższy film przedstawia aplikację RecyclerView z wyłączonym (po lewej) i włączonym (po prawej) wyświetlaniem od krawędzi do krawędzi:
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.