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

Wypróbuj tworzenie wiadomości
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. 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 używa widoku do 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 ViewGroup, który zawiera widoki odpowiadające Twoim danym. Jest to widok, więc możesz dodać RecyclerViewdo 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 z nim powiązanych żadnych danych. Po utworzeniu RecyclerView wiąże go z danymi. Widok uchwytu definiujesz, rozszerzając element RecyclerView.ViewHolder.

  • RecyclerView wysyła żądania widoków i wiąże je z ich danymi, wywołując metody w adapterze. Definiujesz go, rozszerzając 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. Menedżerowie układów są oparte na abstrakcyjnej klasie LayoutManager biblioteki.

Sposób, w jaki wszystkie elementy pasują do siebie, możesz zobaczyć w próbnej aplikacji RecyclerView (Kotlin) lub próbnej aplikacji RecyclerView (Java).

Wdrażanie RecyclerView

Jeśli chcesz używać RecyclerView, musisz wykonać kilka czynności. Zostały one szczegółowo opisane w następnych sekcjach.

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

  2. Zaprojektuj wygląd i zachowanie każdego elementu na liście. Na podstawie tej architektury rozszerz klasę ViewHolder. Twoja wersja ViewHolder zawiera wszystkie funkcje dotyczące elementów listy. Twój widok uchwytu to opakowanie View, którym zarządza RecyclerView.

  3. Zdefiniuj Adapter, który łączy Twoje dane z widokami ViewHolder.

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.
  • StaggeredGridLayoutManagerjest podobne do GridLayoutManager, ale nie wymaga, aby elementy w wierszu miały tę samą wysokość (w przypadku siatek pionowych) lub aby elementy w 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. Potrzebujesz tego układu podczas projektowania widoku uchwytu, jak opisano w następnej sekcji.

Wdrażanie adaptera i uchwytu

Po ustaleniu układu musisz zaimplementować AdapterViewHolder. 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 obiekt ViewHolder. Metoda tworzy i inicjalizuje obiekt ViewHolder oraz powiązany z nim obiekt View, ale nie wypełnia zawartości widoku – obiekt ViewHolder 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 wykorzystuje je do wypełnienia układu widoku. Jeśli na przykład RecyclerView wyświetla listę nazw, metoda może znaleźć odpowiednią nazwę na liście i wypełnić widget TextView 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:

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.

Przykładowe aplikacje