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 te 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ę.
RecyclerView
toViewGroup
, który 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.RecyclerView
Każdy element na liście jest zdefiniowany przez obiekt uchwytu widoku. Po utworzeniu obiektu widoku nie są z nim powiązane żadne dane. Po utworzeniu obiektu widoku
RecyclerView
wiąż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 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 wersjaViewHolder
zapewnia 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:
LinearLayoutManager
rozmieszcza elementy na liście jednowymiarowej.GridLayoutManager
układa elementy w dwuwymiarowej siatce:- Jeśli siatka jest ułożona pionowo,
GridLayoutManager
stara się, aby wszystkie elementy w każdym wierszu miały taką samą szerokość i wysokość, ale poszczególne wiersze mogą mieć różną wysokość. - Jeśli siatka jest ułożona poziomo,
GridLayoutManager
próbuje ustawić wszystkie elementy w każdej kolumnie tak, aby miały tę samą szerokość i wysokość, ale różne kolumny mogą mieć różną szerokość.
- Jeśli siatka jest ułożona pionowo,
StaggeredGridLayoutManager
jest podobne doGridLayoutManager
, 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 współpracują ze sobą, aby określić 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()
:RecyclerView
wywołuje tę metodę za każdym razem, gdy musi utworzyć nowyViewHolder
. Metoda tworzy i inicjuje obiektViewHolder
oraz powiązany z nim obiektView
, ale nie wypełnia zawartości widoku – obiektViewHolder
nie został jeszcze powiązany z konkretnymi danymi.onBindViewHolder()
:RecyclerView
wywołuje tę metodę, aby powiązaćViewHolder
z danymi. Metoda pobiera odpowiednie dane i używa ich do wypełnienia układu obiektu View Holder. Jeśli np.RecyclerView
wyświetla listę nazw, metoda może znaleźć odpowiednią nazwę na liście i wypełnić widżetTextView
w uchwycie widoku.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ż 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
:
- 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:fitsSystemWindows
natrue
lub używającViewCompat.setOnApplyWindowInsetsListener
. - Aby umożliwić rysowanie elementów listy pod paskami systemowymi podczas przewijania, ustaw wartość
android:clipToPadding
nafalse
wRecyclerView
.
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.