240 votes

Exemple simple de RecyclerView Android

J'ai dressé une liste d'articles à plusieurs reprises en utilisant la fonction RecyclerView mais c'est un processus assez compliqué. L'utilisation d'un des nombreux tutoriels en ligne fonctionne ( ce , ce y ce sont bons), mais je cherche un exemple simple que je peux copier et coller pour être opérationnel rapidement. Seules les fonctionnalités suivantes sont nécessaires :

  • Disposition verticale
  • Un seul TextView sur chaque ligne
  • Répond aux événements de clics

Parce que je l'ai souhaité plusieurs fois, j'ai finalement décidé de faire la réponse ci-dessous pour ma future référence et la vôtre.

0 votes

Vérifiez ceci tutoriel avec code source

1 votes

Pour les futures personnes, j'ai écrit un article détaillé avec un tutoriel vidéo. handyopinion.com/

0 votes

youtu.be/UZwiKdrm768 : Vous pouvez consulter ce tutoriel vidéo, qui explique en détail le processus de recyclage :

553voto

Suragch Points 197

Voici un exemple minimal qui ressemblera à l'image suivante.

RecyclerView with a list of animal names

Commencez par une activité vide. Vous allez effectuer les tâches suivantes pour ajouter le RecyclerView. Tout ce que vous avez à faire est de copier et coller le code dans chaque section. Vous pourrez ensuite le personnaliser en fonction de vos besoins.

  • Ajouter des dépendances à gradle
  • Ajoutez les fichiers de mise en page xml pour l'activité et pour la ligne RecyclerView.
  • Créer l'adaptateur RecyclerView
  • Initialiser le RecyclerView dans votre activité

Mise à jour des dépendances de Gradle

Assurez-vous que les dépendances suivantes sont présentes dans votre application gradle.build fichier :

implementation 'com.android.support:appcompat-v7:28.0.0'
implementation 'com.android.support:recyclerview-v7:28.0.0'

Vous pouvez mettre à jour les numéros de version en fonction de ce qui est la plus récente . Utilisez compile plutôt que implementation si vous utilisez toujours Android Studio 2.x.

Créer une structure d'activité

Ajouter le RecyclerView à votre mise en page xml.

activité_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/rvAnimals"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</RelativeLayout>

Créer une structure de lignes

Chaque ligne de notre RecyclerView n'aura qu'un seul TextView . Créez un nouveau fichier de ressources de mise en page.

recyclerview_row.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    android:padding="10dp">

    <TextView
        android:id="@+id/tvAnimalName"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="20sp"/>

</LinearLayout>

Créer l'adaptateur

El RecyclerView a besoin d'un adaptateur pour alimenter les vues de chaque ligne avec vos données. Créez un nouveau fichier java.

MonRecyclerViewAdapter.java

public class MyRecyclerViewAdapter extends RecyclerView.Adapter<MyRecyclerViewAdapter.ViewHolder> {

    private List<String> mData;
    private LayoutInflater mInflater;
    private ItemClickListener mClickListener;

    // data is passed into the constructor
    MyRecyclerViewAdapter(Context context, List<String> data) {
        this.mInflater = LayoutInflater.from(context);
        this.mData = data;
    }

    // inflates the row layout from xml when needed
    @Override
    public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.recyclerview_row, parent, false);
        return new ViewHolder(view);
    }

    // binds the data to the TextView in each row
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        String animal = mData.get(position);
        holder.myTextView.setText(animal);
    }

    // total number of rows
    @Override
    public int getItemCount() {
        return mData.size();
    }

    // stores and recycles views as they are scrolled off screen
    public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
        TextView myTextView;

        ViewHolder(View itemView) {
            super(itemView);
            myTextView = itemView.findViewById(R.id.tvAnimalName);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View view) {
            if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
        }
    }

    // convenience method for getting data at click position
    String getItem(int id) {
        return mData.get(id);
    }

    // allows clicks events to be caught
    void setClickListener(ItemClickListener itemClickListener) {
        this.mClickListener = itemClickListener;
    }

    // parent activity will implement this method to respond to click events
    public interface ItemClickListener {
        void onItemClick(View view, int position);
    }
}

Notes

  • Bien que cela ne soit pas strictement nécessaire, j'ai inclus la fonctionnalité d'écoute des événements de clics sur les lignes. Cette fonctionnalité était disponible dans l'ancien ListViews et constitue un besoin commun. Vous pouvez supprimer ce code si vous n'en avez pas besoin.

Initialiser RecyclerView dans l'activité

Ajoutez le code suivant à votre activité principale.

MainActivity.java

public class MainActivity extends AppCompatActivity implements MyRecyclerViewAdapter.ItemClickListener {

    MyRecyclerViewAdapter adapter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        // data to populate the RecyclerView with
        ArrayList<String> animalNames = new ArrayList<>();
        animalNames.add("Horse");
        animalNames.add("Cow");
        animalNames.add("Camel");
        animalNames.add("Sheep");
        animalNames.add("Goat");

        // set up the RecyclerView
        RecyclerView recyclerView = findViewById(R.id.rvAnimals);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        adapter = new MyRecyclerViewAdapter(this, animalNames);
        adapter.setClickListener(this);
        recyclerView.setAdapter(adapter);
    }

    @Override
    public void onItemClick(View view, int position) {
        Toast.makeText(this, "You clicked " + adapter.getItem(position) + " on row number " + position, Toast.LENGTH_SHORT).show();
    }
}

Notes

  • Remarquez que l'activité met en œuvre le ItemClickListener que nous avons défini dans notre adaptateur. Cela nous permet de gérer les événements de clic de ligne dans onItemClick .

Fini

C'est ça. Vous devriez être en mesure d'exécuter votre projet maintenant et obtenir quelque chose de similaire à l'image du haut.

Continuez

Ajout d'un séparateur entre les rangées

Vous pouvez ajouter un simple séparateur comme ceci

DividerItemDecoration dividerItemDecoration = new DividerItemDecoration(recyclerView.getContext(),
    layoutManager.getOrientation());
recyclerView.addItemDecoration(dividerItemDecoration);

Si vous voulez quelque chose d'un peu plus complexe, consultez les réponses suivantes :

Changement de la couleur des lignes au clic

Voir cette réponse pour savoir comment modifier la couleur de l'arrière-plan et ajouter l'effet d'ondulation lorsqu'on clique sur une ligne.

Insert single item

Mise à jour des rangs

Voir cette réponse pour savoir comment ajouter, supprimer et mettre à jour des lignes.

Insert single item

Autres lectures

2 votes

Très bien. Cela fonctionne jusqu'à ce que j'ajoute un bouton et que j'essaie de définir son écouteur de clic. Avez-vous une idée de la raison pour laquelle cela pourrait interférer ?

2 votes

@johnktejik, il est difficile de savoir exactement de quoi vous parlez à partir des informations que vous fournissez ici. Il serait probablement préférable de poser une nouvelle question. Je pense que votre bouton gère l'événement de mouvement de manière à ce que d'autres éléments ne le reçoivent pas.

1 votes

Désolé pour ajouter la description de l'animal ? EX : Camel in new TextView : is animal // Sheep in new TextVire : is animal. J'ai besoin d'aide T_T

4voto

Hem Shrestha Points 396

En me basant sur différentes sources, j'ai créé une implémentation simple de RecyclerView en utilisant une bibliothèque simple.

Ajoutez cette ligne dans build.gradle

implementation 'com.hereshem.lib:awesomelib:2.0.1'

AjouterCréer un RecyclerView en ajoutant MyRecyclerView dans activity_main.xml avec

<com.hereshem.lib.recycler.MyRecyclerView
        android:id="@+id/recycler"
        app:layoutManager="LinearLayoutManager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

Maintenant dans l'activité principale, créez un ViewHolder en passant le nom de la classe qui doit être liée.

public static class EVHolder extends MyViewHolder<Events> {
    TextView date, title, summary;
    public EVHolder(View v) {
        super(v);
        date = v.findViewById(R.id.date);
        title = v.findViewById(R.id.title);
        summary = v.findViewById(R.id.summary);
    }
    @Override
    public void bindView(Events c) {
        date.setText(c.date);
        title.setText(c.title);
        summary.setText(c.summary);
    }
}

Créer une variable de liste d'éléments et des adaptateurs avec très peu de lignes en passant les éléments, la classe et la disposition dans l'adaptateur.

List<Events> items = new ArrayList<>();
MyRecyclerView recycler = findViewById(R.id.recycler);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(this, items, EVHolder.class, R.layout.row_event);
recycler.setAdapter(adapter);

ClickListener peut être ajouté avec les lignes suivantes

recycler.setOnItemClickListener(new MyRecyclerView.OnItemClickListener() {
    @Override
    public void onItemClick(int position) {
        Toast.makeText(MainActivity.this, "Recycler Item Clicked " + position, Toast.LENGTH_SHORT).show();
    }
});

C'est fait.

Plus d'exemples et d'implémentations peuvent être trouvés aquí . J'espère que cela vous aidera ! !!

1 votes

Qu'est-ce que l'activité unique

1 votes

: error : cannot find symbol summary = v.findViewById(R.id.summary) ; ^ symbol : variable summary location : class id EVHolder.java:15 : error : method does not override or implement a method from a supertype @Override ^ EVHolder.java:17 : error : cannot find symbol date.setText(c.date) ;

1 votes

@johnktejik R.id.summary est l'id donné pour le fichier de ressources de mise en page. veuillez voir ce fichier de mise en page github.com/hereshem/Easy-RecyclerView-Library/blob/master/app/

3voto

Khemraj Points 8449

Maintenant, vous avez besoin 1 adaptateur pour tous RecyclerView

  • Un seul adaptateur peut être utilisé pour tous les RecyclerView. Ainsi, NON onBindViewHolder , Non onCreateViewHolder la manipulation.
  • Pas de code pour définir l'adaptateur à partir de la classe Java/Kotlin. Consultez classe d'échantillons .
  • Vous pouvez définir des événements et des données personnalisées pour chaque liste à l'aide de la fonction Adaptateurs de reliure .

screenshot

Je montre ici la mise en place de deux RecyclerView par 1 adaptateur -

activité_home.xml

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto">

    <data>

        <variable
            name="listOne"
            type="java.util.List"/>

        <variable
            name="listTwo"
            type="java.util.List"/>

        <variable
            name="onItemClickListenerOne"
            type="com.ks.nestedrecyclerbindingexample.callbacks.OnItemClickListener"/>

        <variable
            name="onItemClickListenerTwo"
            type="com.ks.nestedrecyclerbindingexample.callbacks.OnItemClickListener"/>

    </data>

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.v7.widget.RecyclerView
            rvItemLayout="@{@layout/row_one}"
            rvList="@{listOne}"
            rvOnItemClick="@{onItemClickListenerOne}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layoutManager="android.support.v7.widget.LinearLayoutManager"
            />

        <android.support.v7.widget.RecyclerView
            rvItemLayout="@{@layout/row_two}"
            rvList="@{listTwo}"
            rvOnItemClick="@{onItemClickListenerTwo}"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layoutManager="android.support.v7.widget.LinearLayoutManager"
            />

    </LinearLayout>

</layout>

Vous pouvez voir que je passe la liste, l'id de la mise en page de l'élément et l'écouteur de clic de la mise en page.

rvItemLayout="@{@layout/row_one}"
rvList="@{listOne}"
rvOnItemClick="@{onItemClickListenerOne}"

Ces attributs personnalisés sont créés par BindingAdapter .

public class BindingAdapters {
    @BindingAdapter(value = {"rvItemLayout", "rvList", "rvOnItemClick"}, requireAll = false)
    public static void setRvAdapter(RecyclerView recyclerView, int rvItemLayout, List rvList, @Nullable OnItemClickListener onItemClickListener) {
        if (rvItemLayout != 0 && rvList != null && rvList.size() > 0)
            recyclerView.setAdapter(new GeneralAdapter(rvItemLayout, rvList, onItemClickListener));
    }
}

Maintenant, à partir de l'activité, vous passez la liste, cliquez sur l'écouteur comme suit

HomeActivity.java

public class HomeActivity extends AppCompatActivity {
    ActivityHomeBinding binding;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        binding = DataBindingUtil.setContentView(this, R.layout.activity_home);
        binding.setListOne(new ArrayList()); // pass your list or set list from response of API
        binding.setListTwo(new ArrayList());
        binding.setOnItemClickListenerOne(new OnItemClickListener() {
            @Override
            public void onItemClick(View view, Object object) {
                if (object instanceof ModelParent) {
                    // TODO: your action here
                }
            }
        });
        binding.setOnItemClickListenerTwo(new OnItemClickListener() {
            @Override
            public void onItemClick(View view, Object object) {
                if (object instanceof ModelChild) {
                    // TODO: your action here  
                }
            }
        });
    }
}

Vous ne voulez pas lire trop, directement cloner/télécharger exemple complet à partir de mon dépôt Github. Et essayez vous-même.

Vous pouvez voir GeneralAdapter.java dans le repo ci-dessus.

Si vous rencontrez des problèmes lors de la mise en place de la liaison des données, veuillez vous reporter à la rubrique cette réponse .

2voto

NuOne T Attygalle Points 847

Puisque je ne peux pas encore commenter, je vais poster comme réponse le lien J'ai trouvé un tutoriel simple et bien organisé sur recyclerview http://www.androiddeft.com/2017/10/01/recyclerview-Android/

En dehors de cela, lorsque vous allez ajouter une vue de recycleur dans votre activité, ce que vous voulez faire est comme ci-dessous et comment vous devriez le faire a été décrit sur le lien

  • ajoutez le composant RecyclerView dans votre fichier de mise en page
  • créer une classe que vous allez afficher comme des lignes de liste
  • créer un fichier de mise en page qui est la mise en page d'une ligne de votre liste
  • Maintenant, nous avons besoin d'un adaptateur personnalisé, donc créez un adaptateur personnalisé en étendant de la classe parente RecyclerView.Adapter
  • ajouter recyclerview dans votre mainActivity oncreate
  • l'ajout de séparateurs
  • ajouter des écouteurs de Touch

2voto

Igor Arny Points 21

Vous pouvez utiliser un adaptateur abstrait avec des utilitaires et des filtres différents.

SimpleAbstractAdapter.kt

abstract class SimpleAbstractAdapter<T>(private var items: ArrayList<T> = arrayListOf()) : RecyclerView.Adapter<SimpleAbstractAdapter.VH>() {
   protected var listener: OnViewHolderListener<T>? = null
   private val filter = ArrayFilter()
   private val lock = Any()
   protected abstract fun getLayout(): Int
   protected abstract fun bindView(item: T, viewHolder: VH)
   protected abstract fun getDiffCallback(): DiffCallback<T>?
   private var onFilterObjectCallback: OnFilterObjectCallback? = null
   private var constraint: CharSequence? = ""

override fun onBindViewHolder(vh: VH, position: Int) {
    getItem(position)?.let { bindView(it, vh) }
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VH {
    return VH(parent, getLayout())
}

override fun getItemCount(): Int = items.size

protected abstract class DiffCallback<T> : DiffUtil.Callback() {
    private val mOldItems = ArrayList<T>()
    private val mNewItems = ArrayList<T>()

    fun setItems(oldItems: List<T>, newItems: List<T>) {
        mOldItems.clear()
        mOldItems.addAll(oldItems)
        mNewItems.clear()
        mNewItems.addAll(newItems)
    }

    override fun getOldListSize(): Int {
        return mOldItems.size
    }

    override fun getNewListSize(): Int {
        return mNewItems.size
    }

    override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return areItemsTheSame(
                mOldItems[oldItemPosition],
                mNewItems[newItemPosition]
        )
    }

    abstract fun areItemsTheSame(oldItem: T, newItem: T): Boolean

    override fun areContentsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
        return areContentsTheSame(
                mOldItems[oldItemPosition],
                mNewItems[newItemPosition]
        )
    }

    abstract fun areContentsTheSame(oldItem: T, newItem: T): Boolean
}

class VH(parent: ViewGroup, @LayoutRes layout: Int) : RecyclerView.ViewHolder(LayoutInflater.from(parent.context).inflate(layout, parent, false))

interface OnViewHolderListener<T> {
    fun onItemClick(position: Int, item: T)
}

fun getItem(position: Int): T? {
    return items.getOrNull(position)
}

fun getItems(): ArrayList<T> {
    return items
}

fun setViewHolderListener(listener: OnViewHolderListener<T>) {
    this.listener = listener
}

fun addAll(list: List<T>) {
    val diffCallback = getDiffCallback()
    when {
        diffCallback != null && !items.isEmpty() -> {
            diffCallback.setItems(items, list)
            val diffResult = DiffUtil.calculateDiff(diffCallback)
            items.clear()
            items.addAll(list)
            diffResult.dispatchUpdatesTo(this)
        }
        diffCallback == null && !items.isEmpty() -> {
            items.clear()
            items.addAll(list)
            notifyDataSetChanged()
        }
        else -> {
            items.addAll(list)
            notifyDataSetChanged()
        }
    }
}

fun add(item: T) {
    items.add(item)
    notifyDataSetChanged()
}

fun add(position:Int, item: T) {
    items.add(position,item)
    notifyItemInserted(position)
}

fun remove(position: Int) {
    items.removeAt(position)
    notifyItemRemoved(position)
}

fun remove(item: T) {
    items.remove(item)
    notifyDataSetChanged()
}

fun clear(notify: Boolean=false) {
    items.clear()
    if (notify) {
        notifyDataSetChanged()
    }
}

fun setFilter(filter: SimpleAdapterFilter<T>): ArrayFilter {
    return this.filter.setFilter(filter)
}

interface SimpleAdapterFilter<T> {
    fun onFilterItem(contains: CharSequence, item: T): Boolean
}

fun convertResultToString(resultValue: Any): CharSequence {
    return filter.convertResultToString(resultValue)
}

fun filter(constraint: CharSequence) {
    this.constraint = constraint
    filter.filter(constraint)
}

fun filter(constraint: CharSequence, listener: Filter.FilterListener) {
    this.constraint = constraint
    filter.filter(constraint, listener)
}

fun getFilter(): Filter {
    return filter
}

interface OnFilterObjectCallback {
    fun handle(countFilterObject: Int)
}

fun setOnFilterObjectCallback(objectCallback: OnFilterObjectCallback) {
    onFilterObjectCallback = objectCallback
}

inner class ArrayFilter : Filter() {
    private var original: ArrayList<T> = arrayListOf()
    private var filter: SimpleAdapterFilter<T> = DefaultFilter()
    private var list: ArrayList<T> = arrayListOf()
    private var values: ArrayList<T> = arrayListOf()

    fun setFilter(filter: SimpleAdapterFilter<T>): ArrayFilter {
        original = items
        this.filter = filter
        return this
    }

    override fun performFiltering(constraint: CharSequence?): Filter.FilterResults {
        val results = Filter.FilterResults()
        if (constraint == null || constraint.isBlank()) {
            synchronized(lock) {
                list = original
            }
            results.values = list
            results.count = list.size
        } else {
            synchronized(lock) {
                values = original
            }
            val result = ArrayList<T>()
            for (value in values) {
                if (constraint!=null && constraint.trim().isNotEmpty() && value != null) {
                    if (filter.onFilterItem(constraint, value)) {
                        result.add(value)
                    }
                } else {
                    value?.let { result.add(it) }
                }
            }
            results.values = result
            results.count = result.size
        }
        return results
    }

    override fun publishResults(constraint: CharSequence, results: Filter.FilterResults) {
        items = results.values as? ArrayList<T> ?: arrayListOf()
        notifyDataSetChanged()
        onFilterObjectCallback?.handle(results.count)
    }

}

class DefaultFilter<T> : SimpleAdapterFilter<T> {
    override fun onFilterItem(contains: CharSequence, item: T): Boolean {
        val valueText = item.toString().toLowerCase()
        if (valueText.startsWith(contains.toString())) {
            return true
        } else {
            val words = valueText.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()
            for (word in words) {
                if (word.contains(contains)) {
                    return true
                }
            }
        }
        return false
    }
  }
}

Et étendez l'adaptateur abstrait avec des méthodes d'implémentation

TasksAdapter.kt

import android.annotation.SuppressLint
  import kotlinx.android.synthetic.main.task_item_layout.view.*

class TasksAdapter(private val listener:TasksListener? = null) : SimpleAbstractAdapter<Task>() {
override fun getLayout(): Int {
    return R.layout.task_item_layout
}

override fun getDiffCallback(): DiffCallback<Task>? {
    return object : DiffCallback<Task>() {
        override fun areItemsTheSame(oldItem: Task, newItem: Task): Boolean {
            return oldItem.id == newItem.id
        }

        override fun areContentsTheSame(oldItem: Task, newItem: Task): Boolean {
            return oldItem.items == newItem.items
        }
    }
}

@SuppressLint("SetTextI18n")
override fun bindView(item: Task, viewHolder: VH) {
    viewHolder.itemView.apply {
        val position = viewHolder.adapterPosition
        val customer = item.customer
        val customerName = if (customer != null) customer.name else ""
        tvTaskCommentTitle.text = customerName + ", #" + item.id
        tvCommentContent.text = item.taskAddress
        ivCall.setOnClickListener {
            listener?.onCallClick(position, item)
        }
        setOnClickListener {
            listener?.onItemClick(position, item)
        }
    }
}

 interface TasksListener : SimpleAbstractAdapter.OnViewHolderListener<Task> {
    fun onCallClick(position: Int, item: Task)
 }
}

Adaptateur Init

mAdapter = TasksAdapter(object : TasksAdapter.TasksListener {
            override fun onCallClick(position: Int, item:Task) {
            }

            override fun onItemClick(position: Int, item:Task) {

            }
        })
rvTasks.adapter = mAdapter

et remplir

mAdapter?.addAll(tasks)

ajouter un filtre personnalisé

mAdapter?.setFilter(object : SimpleAbstractAdapter.SimpleAdapterFilter<MoveTask> {
            override fun onFilterItem(contains: CharSequence, item:Task): Boolean {
                return contains.toString().toLowerCase().contains(item.id?.toLowerCase().toString())
            }
    })

données du filtre

mAdapter?.filter("test")

0 votes

Ajout de quelques changements avec les utilitaires diff, ajout du support des types gist.github.com/arnyigor/568035c6db9bdbeaf609b68a71834349

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X