Tworzenie list dynamicznych za pomocą RecyclerView, części Androida Jetpack.

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

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.

  • RecyclerView to ViewGroup zawierający widoki odpowiadające Twoim danym. Jest to widok, więc dodajesz RecyclerView do 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 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 definiuje się przez rozszerzenie klasy 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 LayoutManager biblioteki.

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.

  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 zachowanie każdego elementu na liście. Na podstawie tego projektu rozszerz klasę ViewHolder. Twoja wersja ViewHolder zapewnia wszystkie funkcje dla elementów listy. Obiekt ViewHolder jest kodem otaczającym obiekt View, a widokiem zarządza RecyclerView.

  3. Zdefiniuj Adapter, który powiąże Twoje dane z widokami ViewHolder.

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:

  • LinearLayoutManager porządkuje elementy na jednowymiarowej liście.
  • GridLayoutManager rozmieszcza elementy w dwuwymiarowej siatce:
    • Jeśli siatka jest ułożona pionowo, GridLayoutManager pró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, GridLayoutManager pró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.
  • StaggeredGridLayoutManager jest podobny do GridLayoutManager, 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(): RecyclerView wywołuje tę metodę, gdy musi utworzyć nowy obiekt 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 ViewHolder. Jeśli na przykład RecyclerView wyświetla listę nazw, metoda może znaleźć odpowiednią nazwę na liście i wypełnić widżet TextView obiektu ViewHolder.

  • 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 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:

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.

Przykładowe aplikacje