Tworzenie dynamicznych list za pomocą RecyclerView   Część Androida Jetpack.

Wypróbuj Compose
Jetpack Compose to zalecany zestaw narzędzi interfejsu na Androida. Dowiedz się, jak pracować z układami w Compose.

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 to ViewGroup, 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.

  1. Zdecyduj, jak ma wyglądać lista lub siatka. Zwykle możesz użyć jednego ze standardowych menedżerów układu biblioteki RecyclerView.

  2. Zaprojektuj wygląd i działanie każdego elementu na liście. Na podstawie tego projektu rozszerz klasę ViewHolder. Twoja wersja ViewHolder zapewnia wszystkie funkcje elementów listy. Element View Holder jest otoczką elementu View, a tym widokiem zarządza RecyclerView.

  3. Określ parametr Adapter, który powiąże Twoje dane z widokami ViewHolder.

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ść.
  • StaggeredGridLayoutManager jest podobne do GridLayoutManager, 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ć AdapterViewHolder. 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ć nowy ViewHolder. Metoda tworzy i inicjuje obiekt ViewHolder oraz powiązany z nim obiekt View, ale nie wypełnia zawartości widoku – obiekt ViewHolder 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żet TextView 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:

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.

Przykładowe aplikacje