Tworzenie dynamicznych list za pomocą RecyclerView Część Androida Jetpack.
RecyclerView ułatwia wydajne wyświetlanie dużych zbiorów danych. Dostarczasz dane i określasz wygląd każdego elementu, a biblioteka RecyclerView dynamicznie tworzy elementy, gdy są potrzebne.
Zgodnie z nazwą RecyclerView ponownie wykorzystuje poszczególne elementy. Gdy element zniknie z ekranu, RecyclerView nie niszczy jego widoku. Zamiast tego widok RecyclerView ponownie wykorzystuje widok dla nowych elementów, które zostały przewinięte na ekran. RecyclerView zwiększa wydajność i szybkość reakcji aplikacji oraz zmniejsza zużycie energii.
Kluczowe klasy
Kilka klas współpracuje ze sobą, aby utworzyć dynamiczną listę.
RecyclerViewtoViewGroup, która zawiera widoki odpowiadające Twoim danym. Jest to widok, więc dodajesz go do układu w taki sam sposób jak inne elementy interfejsu.RecyclerViewKażdy element na liście jest zdefiniowany przez obiekt uchwytu widoku. Gdy tworzony jest uchwyt widoku, nie są z nim powiązane żadne dane. Po utworzeniu obiektu widoku
RecyclerViewwiąże go z danymi. Uchwyt widoku definiujesz, rozszerzając klasęRecyclerView.ViewHolder.RecyclerViewżąda widoków i powiązuje je z danymi, wywołując metody w adapterze. Adapter definiujesz, rozszerzając klasęRecyclerView.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 biblioteki
LayoutManager.
Wszystkie elementy możesz zobaczyć w przykładowej aplikacji RecyclerView (Kotlin) lub przykładowej aplikacji RecyclerView (Java).
Kroki wdrażania elementu RecyclerView
Jeśli zamierzasz używać elementu RecyclerView, musisz wykonać kilka czynności. Szczegółowo opisujemy je w kolejnych sekcjach.
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 działanie każdego elementu na liście. Na podstawie tego projektu rozszerz klasę
ViewHolder. Twoja wersjaViewHolderzapewnia wszystkie funkcje elementów listy. Element View Holder jest otoczką elementuView, a tym widokiem zarządzaRecyclerView.Określ parametr
Adapter, który powiąże Twoje dane z widokamiViewHolder.
Dostępne są też zaawansowane opcje dostosowywania, które pozwalają dopasować element RecyclerView do Twoich konkretnych potrzeb.
Planowanie układu
Elementy w obiekcie RecyclerView są rozmieszczone według klasy
LayoutManager. Biblioteka RecyclerView udostępnia 3 menedżerów układu, którzy obsługują najczęstsze sytuacje związane z układem:
LinearLayoutManagerrozmieszcza elementy na liście jednowymiarowej.GridLayoutManagerukłada elementy w dwuwymiarowej siatce:- Jeśli siatka jest ułożona pionowo,
GridLayoutManagerstara się, aby wszystkie elementy w każdym wierszu miały taką samą szerokość i wysokość, ale różne wiersze mogą mieć różną wysokość. - Jeśli siatka jest ułożona poziomo,
GridLayoutManagerstara się, aby wszystkie elementy w każdej kolumnie miały tę samą szerokość i wysokość, ale różne kolumny mogą mieć różną szerokość.
- Jeśli siatka jest ułożona pionowo,
StaggeredGridLayoutManagerdziała podobnie jakGridLayoutManager, ale nie wymaga, aby elementy w wierszu miały tę samą wysokość (w przypadku siatek pionowych) ani aby elementy w tej samej kolumnie miały tę samą szerokość (w przypadku siatek poziomych). W rezultacie 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, zgodnie z opisem w następnej sekcji.
Wdrażanie adaptera i wyświetlanie modułu
Po określeniu układu musisz wdrożyć Adapter i ViewHolder. Te 2 klasy określają sposób wyświetlania danych. Element ViewHolder jest otoczką elementu View, który zawiera układ pojedynczego elementu na liście. Usługa Adapter tworzy w razie potrzeby obiekty ViewHolder
i ustawia dla nich dane. Proces przypisywania widoków do danych nazywa się wiązaniem.
Definiując adapter, zastępujesz 3 kluczowe metody:
onCreateViewHolder():RecyclerViewwywołuje tę metodę za każdym razem, gdy musi utworzyć nowyViewHolder. Metoda tworzy i inicjuje obiektViewHolderoraz powiązany z nim obiektView, ale nie wypełnia treś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 View Holder. Jeśli np.RecyclerViewwyświetla listę nazw, metoda może znaleźć odpowiednią nazwę na liście i wypełnić widżetTextVieww uchwycie widoku.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 tego, aby określić, kiedy nie ma już więcej elementów, które można wyświetlić.
Oto typowy przykład prostego adaptera z zagnieżdżonym elementem ViewHolder, który wyświetla listę danych. W tym przypadku element RecyclerView wyświetla prostą listę elementów tekstowych. Do adaptera przekazywana jest tablica ciągów tekstowych zawierających 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 zdefiniowany w pliku układu XML, tak jak zwykle.
W tym przypadku aplikacja ma plik text_row_item.xml o takiej zawartoś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ć znaku 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 elementu RecyclerView.
Włącz wyświetlanie bez ramki
Aby włączyć wyświetlanie bez ramki na urządzeniu RecyclerView, wykonaj te czynności:
- Aby skonfigurować wyświetlanie bez ramki zgodne wstecznie, wywołaj funkcję
enableEdgeToEdge(). - Jeśli elementy listy początkowo nakładają się na paski systemowe, zastosuj wcięcia w
RecyclerView. Możesz to zrobić, ustawiającandroid:fitsSystemWindowsnatruelub używającViewCompat.setOnApplyWindowInsetsListener. - Aby umożliwić rysowanie elementów listy pod paskami systemowymi podczas przewijania, ustaw wartość
falseparametruandroid:clipToPaddingw plikuRecyclerView.
Poniższy film pokazuje RecyclerView z wyłączonym (po lewej) i włączonym (po prawej) wyświetlaczem 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; } );
RecyclerView XML:
<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.