Android
RecyclerView
Recherche…
Introduction
RecyclerView est une version plus avancée de l'affichage en liste avec des performances améliorées et des fonctionnalités supplémentaires.
Paramètres
Paramètre | Détail |
---|---|
Adaptateur | Une sous-classe de RecyclerView.Adapter chargée de fournir des vues représentant des éléments dans un ensemble de données |
Position | La position d'une donnée dans un adaptateur |
Indice | Index d'une vue enfant attachée utilisé dans un appel à getChildAt (int). Contraste avec la position |
Contraignant | Processus de préparation d'une vue enfant pour afficher des données correspondant à une position dans l'adaptateur |
Recycler (voir) | Une vue précédemment utilisée pour afficher des données pour une position d'adaptateur spécifique peut être placée dans un cache pour une réutilisation ultérieure afin d'afficher à nouveau le même type de données ultérieurement. Cela peut considérablement améliorer les performances en sautant l'inflation ou la construction initiale de la mise en page |
Scrap (voir) | Une vue enfant entrée dans un état temporairement détaché lors de la mise en page. Les vues Scrap peuvent être réutilisées sans se détacher complètement du parent RecyclerView, que ce soit sans modification si aucune liaison n'est requise ou modifiée par l'adaptateur si la vue a été considérée comme sale |
Sale (voir) | Une vue enfant qui doit être rebondie par l'adaptateur avant d'être affichée |
Remarques
RecyclerView
est une vue flexible pour fournir une fenêtre limitée dans un grand ensemble de données.
Avant d'utiliser RecyclerView
vous devez ajouter la dépendance de la bibliothèque de support dans le fichier build.gradle
:
dependencies {
// Match the version of your support library dependency
compile 'com.android.support:recyclerview-v7:25.3.1'
}
Vous pouvez trouver le dernier numéro de version de recyclerview sur le site officiel.
Autres sujets connexes:
Il y a d'autres sujets qui décrivent les composants RecyclerView
:
Documentation officielle
http://developer.android.com/reference/android/support/v7/widget/RecyclerView.html
Versions plus anciennes:
//it requires compileSdkVersion 25
compile 'com.android.support:recyclerview-v7:25.2.0'
compile 'com.android.support:recyclerview-v7:25.1.0'
compile 'com.android.support:recyclerview-v7:25.0.0'
//it requires compileSdkVersion 24
compile 'com.android.support:recyclerview-v7:24.2.1'
compile 'com.android.support:recyclerview-v7:24.2.0'
compile 'com.android.support:recyclerview-v7:24.1.1'
compile 'com.android.support:recyclerview-v7:24.1.0'
//it requires compileSdkVersion 23
compile 'com.android.support:recyclerview-v7:23.4.0'
compile 'com.android.support:recyclerview-v7:23.3.0'
compile 'com.android.support:recyclerview-v7:23.2.1'
compile 'com.android.support:recyclerview-v7:23.2.0'
compile 'com.android.support:recyclerview-v7:23.1.1'
compile 'com.android.support:recyclerview-v7:23.1.0'
compile 'com.android.support:recyclerview-v7:23.0.1'
compile 'com.android.support:recyclerview-v7:23.0.0'
//it requires compileSdkVersion 22
compile 'com.android.support:recyclerview-v7:22.2.1'
compile 'com.android.support:recyclerview-v7:22.2.0'
compile 'com.android.support:recyclerview-v7:22.1.1'
compile 'com.android.support:recyclerview-v7:22.1.0'
compile 'com.android.support:recyclerview-v7:22.0.0'
//it requires compileSdkVersion 21
compile 'com.android.support:recyclerview-v7:21.0.3'
compile 'com.android.support:recyclerview-v7:21.0.2'
compile 'com.android.support:recyclerview-v7:21.0.0'
Ajouter un RecyclerView
Ajoutez la dépendance décrite dans la section Remarque, puis ajoutez RecyclerView
à votre mise en page:
<android.support.v7.widget.RecyclerView
android:id="@+id/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
Une fois que vous avez ajouté un widget RecyclerView
à votre mise en page, obtenez un handle pour l'objet, connectez-le à un gestionnaire de disposition et connectez un adaptateur pour que les données soient affichées:
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
// set a layout manager (LinearLayoutManager in this example)
mLayoutManager = new LinearLayoutManager(getApplicationContext());
mRecyclerView.setLayoutManager(mLayoutManager);
// specify an adapter
mAdapter = new MyAdapter(myDataset);
mRecyclerView.setAdapter(mAdapter);
Ou simplement configurer le gestionnaire de disposition à partir de XML en ajoutant ces lignes:
xmlns:app="http://schemas.android.com/apk/res-auto"
app:layoutManager="android.support.v7.widget.LinearLayoutManager"
Si vous savez que les modifications apportées au contenu de RecyclerView
ne modifieront pas la taille de mise en page de RecyclerView
, utilisez le code suivant pour améliorer les performances du composant. Si RecyclerView a une taille fixe, il sait que RecyclerView lui-même ne sera pas redimensionné en raison de ses enfants. Il ne fait que gérer le changement lui-même. Si vous invalidez le parent, le coordinateur, la mise en page, etc. (vous pouvez utiliser cette méthode avant même de définir LayoutManager
et Adapter
):
mRecyclerView.setHasFixedSize(true);
RecyclerView
fournit ces gestionnaires de disposition intégrés à utiliser. Vous pouvez donc créer une liste, une grille et une grille échelonnée en utilisant RecyclerView
:
- LinearLayoutManager affiche les éléments dans une liste de défilement verticale ou horizontale.
- GridLayoutManager affiche les éléments dans une grille.
- StaggeredGridLayoutManager affiche les éléments dans une grille échelonnée.
Chargement plus fluide des articles
Si les éléments de votre RecyclerView
chargent des données du réseau (généralement des images) ou effectuent d'autres traitements, cela peut prendre un temps considérable et vous pouvez vous retrouver avec des éléments à l'écran mais pas complètement chargés. Pour éviter cela, vous pouvez étendre le LinearLayoutManager
existant pour précharger un certain nombre d'éléments avant qu'ils ne soient visibles à l'écran:
package com.example;
import android.content.Context;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.OrientationHelper;
import android.support.v7.widget.RecyclerView;
/**
* A LinearLayoutManager that preloads items off-screen.
* <p>
* Preloading is useful in situations where items might take some time to load
* fully, commonly because they have maps, images or other items that require
* network requests to complete before they can be displayed.
* <p>
* By default, this layout will load a single additional page's worth of items,
* a page being a pixel measure equivalent to the on-screen size of the
* recycler view. This can be altered using the relevant constructor, or
* through the {@link #setPages(int)} method.
*/
public class PreLoadingLinearLayoutManager extends LinearLayoutManager {
private int mPages = 1;
private OrientationHelper mOrientationHelper;
public PreLoadingLinearLayoutManager(final Context context) {
super(context);
}
public PreLoadingLinearLayoutManager(final Context context, final int pages) {
super(context);
this.mPages = pages;
}
public PreLoadingLinearLayoutManager(final Context context, final int orientation, final boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
@Override
public void setOrientation(final int orientation) {
super.setOrientation(orientation);
mOrientationHelper = null;
}
/**
* Set the number of pages of layout that will be preloaded off-screen,
* a page being a pixel measure equivalent to the on-screen size of the
* recycler view.
* @param pages the number of pages; can be {@code 0} to disable preloading
*/
public void setPages(final int pages) {
this.mPages = pages;
}
@Override
protected int getExtraLayoutSpace(final RecyclerView.State state) {
if (mOrientationHelper == null) {
mOrientationHelper = OrientationHelper.createOrientationHelper(this, getOrientation());
}
return mOrientationHelper.getTotalSpace() * mPages;
}
}
Glisser-Déposer et Glisser avec RecyclerView
Vous pouvez implémenter les fonctionnalités de glisser-déplacer et de glisser-déposer avec RecyclerView sans utiliser de bibliothèques tierces.
Utilisez simplement la classe ItemTouchHelper
incluse dans la bibliothèque de support RecyclerView.
Instanciez ItemTouchHelper
avec le rappel SimpleCallback
et, selon la fonctionnalité que vous prenez en charge, vous devez remplacer onMove(RecyclerView, ViewHolder, ViewHolder)
et / ou onSwiped(ViewHolder, int)
et attacher enfin à votre RecyclerView
.
ItemTouchHelper.SimpleCallback simpleItemTouchCallback = new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int swipeDir) {
// remove item from adapter
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
final int fromPos = viewHolder.getAdapterPosition();
final int toPos = target.getAdapterPosition();
// move item in `fromPos` to `toPos` in adapter.
return true;// true if moved, false otherwise
}
};
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(simpleItemTouchCallback);
itemTouchHelper.attachToRecyclerView(recyclerView);
Il convient de mentionner que le constructeur de SimpleCallback
applique la même stratégie de balayage à tous les éléments de RecyclerView
. Dans tous les cas, il est possible de mettre à jour la direction de balayage par défaut pour des éléments spécifiques en getSwipeDirs(RecyclerView, ViewHolder)
simplement la méthode getSwipeDirs(RecyclerView, ViewHolder)
.
Supposons par exemple que notre RecyclerView
inclue un HeaderViewHolder
et que nous ne souhaitons évidemment pas lui appliquer de glissement. Il suffira de remplacer getSwipeDirs
comme suit:
@Override
public int getSwipeDirs(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if (viewHolder instanceof HeaderViewHolder) {
// no swipe for header
return 0;
}
// default swipe for all other items
return super.getSwipeDirs(recyclerView, viewHolder);
}
Ajouter en-tête / pied de page à un RecyclerView
Ceci est un exemple de code d'adaptateur.
public class SampleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int FOOTER_VIEW = 1;
// Define a view holder for Footer view
public class FooterViewHolder extends ViewHolder {
public FooterViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do whatever you want on clicking the item
}
});
}
}
// Now define the viewholder for Normal list item
public class NormalViewHolder extends ViewHolder {
public NormalViewHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// Do whatever you want on clicking the normal items
}
});
}
}
// And now in onCreateViewHolder you have to pass the correct view
// while populating the list item.
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
if (viewType == FOOTER_VIEW) {
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_footer, parent, false);
FooterViewHolder vh = new FooterViewHolder(v);
return vh;
}
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_normal, parent, false);
NormalViewHolder vh = new NormalViewHolder(v);
return vh;
}
// Now bind the viewholders in onBindViewHolder
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
try {
if (holder instanceof NormalViewHolder) {
NormalViewHolder vh = (NormalViewHolder) holder;
vh.bindView(position);
} else if (holder instanceof FooterViewHolder) {
FooterViewHolder vh = (FooterViewHolder) holder;
}
} catch (Exception e) {
e.printStackTrace();
}
}
// Now the critical part. You have return the exact item count of your list
// I've only one footer. So I returned data.size() + 1
// If you've multiple headers and footers, you've to return total count
// like, headers.size() + data.size() + footers.size()
@Override
public int getItemCount() {
if (data == null) {
return 0;
}
if (data.size() == 0) {
//Return 1 here to show nothing
return 1;
}
// Add extra view to show the footer view
return data.size() + 1;
}
// Now define getItemViewType of your own.
@Override
public int getItemViewType(int position) {
if (position == data.size()) {
// This is where we'll add footer.
return FOOTER_VIEW;
}
return super.getItemViewType(position);
}
// So you're done with adding a footer and its action on onClick.
// Now set the default ViewHolder for NormalViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {
// Define elements of a row here
public ViewHolder(View itemView) {
super(itemView);
// Find view by ID and initialize here
}
public void bindView(int position) {
// bindView() method to implement actions
}
}
}
Voici une bonne lecture de l'implémentation de RecyclerView
avec en-tête et pied de page.
Méthode alternative:
Bien que la réponse ci-dessus fonctionne, vous pouvez également utiliser cette approche en utilisant une vue de recycleur utilisant un NestedScrollView
Vous pouvez ajouter une mise en page pour l'en-tête en utilisant l'approche suivante:
<android.support.v4.widget.NestedScrollView
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
layout="@layout/drawer_view_header"
android:id="@+id/navigation_header"/>
<android.support.v7.widget.RecyclerView
android:layout_below="@id/navigation_header"
android:id="@+id/followers_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
Ou vous pouvez également utiliser un LinearLayout
avec un alignement vertical dans votre NestedScrollView
.
Note: Cela ne fonctionnera qu'avec RecyclerView
au-dessus de 23.2.0
compile 'com.android.support:recyclerview-v7:23.2.0'
Utiliser plusieurs ViewHolders avec ItemViewType
Parfois, un RecyclerView doit utiliser plusieurs types de vues pour être affiché dans la liste affichée dans l'interface utilisateur, et chaque vue nécessite un fichier XML de mise en page différent.
Pour ce problème, vous pouvez utiliser différents ViewHolders dans un seul adaptateur, en utilisant une méthode spéciale dans RecyclerView - getItemViewType(int position)
.
Voici un exemple d'utilisation de deux ViewHolders:
Un ViewHolder pour afficher les entrées de la liste
Un ViewHolder pour afficher plusieurs vues d'en-tête
@Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View itemView = LayoutInflater.from(context).inflate(viewType, parent, false); return ViewHolder.create(itemView, viewType); } @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) { final Item model = this.items.get(position); ((ViewHolder) holder).bind(model); } @Override public int getItemViewType(int position) { return inSearchState ? R.layout.item_header : R.layout.item_entry; } abstract class ViewHolder { abstract void bind(Item model); public static ViewHolder create(View v, int viewType) { return viewType == R.layout.item_header ? new HeaderViewHolder(v) :new EntryViewHolder(v); } } static class EntryViewHolder extends ViewHolder { private View v; public EntryViewHolder(View v) { this.v = v; } @Override public void bind(Item model) { // Bind item data to entry view. } } static class HeaderViewHolder extends ViewHolder { private View v; public HeaderViewHolder(View v) { this.v = v; } @Override public void bind(Item model) { // Bind item data to header view. } }
Filtrer les éléments dans RecyclerView avec un SearchView
Ajouter une méthode de filter
dans RecyclerView.Adapter
:
public void filter(String text) {
if(text.isEmpty()){
items.clear();
items.addAll(itemsCopy);
} else{
ArrayList<PhoneBookItem> result = new ArrayList<>();
text = text.toLowerCase();
for(PhoneBookItem item: itemsCopy){
//match by name or phone
if(item.name.toLowerCase().contains(text) || item.phone.toLowerCase().contains(text)){
result.add(item);
}
}
items.clear();
items.addAll(result);
}
notifyDataSetChanged();
}
itemsCopy
est initialisé dans le constructeur de l'adaptateur comme itemsCopy.addAll(items)
.
Si vous le faites, appelez simplement le filter
de OnQueryTextListener
partir de SearchView
:
searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
adapter.filter(query);
return true;
}
@Override
public boolean onQueryTextChange(String newText) {
adapter.filter(newText);
return true;
}
});
Menu contextuel avec recyclerView
mettre ce code dans votre ViewHolder
note: Dans ce code, j'utilise btnExpand
click-event, pour tout événement click de recyclerview
, vous pouvez définir l'écouteur sur l'objet itemView.
public class MyViewHolder extends RecyclerView.ViewHolder{
CardView cv;
TextView recordName, visibleFile, date, time;
Button btnIn, btnExpand;
public MyViewHolder(final View itemView) {
super(itemView);
cv = (CardView)itemView.findViewById(R.id.cardview);
recordName = (TextView)itemView.findViewById(R.id.tv_record);
visibleFile = (TextView)itemView.findViewById(R.id.visible_file);
date = (TextView)itemView.findViewById(R.id.date);
time = (TextView)itemView.findViewById(R.id.time);
btnIn = (Button)itemView.findViewById(R.id.btn_in_out);
btnExpand = (Button) itemView.findViewById(R.id.btn_expand);
btnExpand.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
PopupMenu popup = new PopupMenu(btnExpand.getContext(), itemView);
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
@Override
public boolean onMenuItemClick(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_delete:
moveFile(recordName.getText().toString(), getAdapterPosition());
return true;
case R.id.action_play:
String valueOfPath = recordName.getText().toString();
Intent intent = new Intent();
intent.setAction(android.content.Intent.ACTION_VIEW);
File file = new File(valueOfPath);
intent.setDataAndType(Uri.fromFile(file), "audio/*");
context.startActivity(intent);
return true;
case R.id.action_share:
String valueOfPath = recordName.getText().toString();
File filee = new File(valueOfPath);
try {
Intent sendIntent = new Intent();
sendIntent.setAction(Intent.ACTION_SEND);
sendIntent.setType("audio/*");
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(filee));
context.startActivity(sendIntent);
} catch (NoSuchMethodError | IllegalArgumentException | NullPointerException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
return true;
default:
return false;
}
}
});
// here you can inflate your menu
popup.inflate(R.menu.my_menu_item);
popup.setGravity(Gravity.RIGHT);
// if you want icon with menu items then write this try-catch block.
try {
Field mFieldPopup=popup.getClass().getDeclaredField("mPopup");
mFieldPopup.setAccessible(true);
MenuPopupHelper mPopup = (MenuPopupHelper) mFieldPopup.get(popup);
mPopup.setForceShowIcon(true);
} catch (Exception e) {
}
popup.show();
}
});
}
}
autre manière de montrer des icônes dans le menu
try {
Field[] fields = popup.getClass().getDeclaredFields();
for (Field field : fields) {
if ("mPopup".equals(field.getName())) {
field.setAccessible(true);
Object menuPopupHelper = field.get(popup);
Class<?> classPopupHelper = Class.forName(menuPopupHelper
.getClass().getName());
Method setForceIcons = classPopupHelper.getMethod(
"setForceShowIcon", boolean.class);
setForceIcons.invoke(menuPopupHelper, true);
break;
}
}
} catch (Exception e) {
}
Voici la sortie:
Animer le changement de données
RecyclerView
effectuera une animation pertinente si l'une des méthodes "notify" est utilisée à l'exception de notifyDataSetChanged
; cela inclut notifyItemChanged
, notifyItemInserted
, notifyItemMoved
, notifyItemRemoved
, etc.
L'adaptateur doit étendre cette classe à la place de RecyclerView.Adapter
.
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import java.util.List;
public abstract class AnimatedRecyclerAdapter<T, VH extends RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
protected List<T> models;
protected AnimatedRecyclerAdapter(@NonNull List<T> models) {
this.models = models;
}
//Set new models.
public void setModels(@NonNull final List<T> models) {
applyAndAnimateRemovals(models);
applyAndAnimateAdditions(models);
applyAndAnimateMovedItems(models);
}
//Remove an item at position and notify changes.
private T removeItem(int position) {
final T model = models.remove(position);
notifyItemRemoved(position);
return model;
}
//Add an item at position and notify changes.
private void addItem(int position, T model) {
models.add(position, model);
notifyItemInserted(position);
}
//Move an item at fromPosition to toPosition and notify changes.
private void moveItem(int fromPosition, int toPosition) {
final T model = models.remove(fromPosition);
models.add(toPosition, model);
notifyItemMoved(fromPosition, toPosition);
}
//Remove items that no longer exist in the new models.
private void applyAndAnimateRemovals(@NonNull final List<T> newTs) {
for (int i = models.size() - 1; i >= 0; i--) {
final T model = models.get(i);
if (!newTs.contains(model)) {
removeItem(i);
}
}
}
//Add items that do not exist in the old models.
private void applyAndAnimateAdditions(@NonNull final List<T> newTs) {
for (int i = 0, count = newTs.size(); i < count; i++) {
final T model = newTs.get(i);
if (!models.contains(model)) {
addItem(i, model);
}
}
}
//Move items that have changed their position.
private void applyAndAnimateMovedItems(@NonNull final List<T> newTs) {
for (int toPosition = newTs.size() - 1; toPosition >= 0; toPosition--) {
final T model = newTs.get(toPosition);
final int fromPosition = models.indexOf(model);
if (fromPosition >= 0 && fromPosition != toPosition) {
moveItem(fromPosition, toPosition);
}
}
}
}
Vous ne devez PAS utiliser la même
List
poursetModels
etList
dans l'adaptateur.
Vous déclarez des models
tant que variables globales. DataModel
est une classe factice uniquement.
private List<DataModel> models;
private YourAdapter adapter;
Initialisez les models
avant de les transmettre à l'adaptateur. YourAdapter
est l'implémentation de AnimatedRecyclerAdapter
.
models = new ArrayList<>();
//Add models
models.add(new DataModel());
//Do NOT pass the models directly. Otherwise, when you modify global models,
//you will also modify models in adapter.
//adapter = new YourAdapter(models); <- This is wrong.
adapter = new YourAdapter(new ArrayList(models));
Appelez ceci après avoir mis à jour vos models
globaux.
adapter.setModels(new ArrayList(models));
Si vous ne remplacez pas la valeur
equals
, toute la comparaison est comparée par référence.
Exemple utilisant SortedList
Android a introduit la classe SortedList
peu après l'introduction de RecyclerView
. Cette classe gère tous les appels de méthode 'notify' au RecyclerView.Adapter
pour garantir une animation correcte, et permet même le traitement par lots de plusieurs modifications, de sorte que les animations ne changent pas.
import android.support.v7.util.SortedList;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.util.SortedListAdapterCallback;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import java.util.List;
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private SortedList<DataModel> mSortedList;
class ViewHolder extends RecyclerView.ViewHolder {
TextView text;
CheckBox checkBox;
ViewHolder(View itemView){
super(itemView);
//Initiate your code here...
}
void setDataModel(DataModel model) {
//Update your UI with the data model passed here...
text.setText(modle.getText());
checkBox.setChecked(model.isChecked());
}
}
public MyAdapter() {
mSortedList = new SortedList<>(DataModel.class, new SortedListAdapterCallback<DataModel>(this) {
@Override
public int compare(DataModel o1, DataModel o2) {
//This gets called to find the ordering between objects in the array.
if (o1.someValue() < o2.someValue()) {
return -1;
} else if (o1.someValue() > o2.someValue()) {
return 1;
} else {
return 0;
}
}
@Override
public boolean areContentsTheSame(DataModel oldItem, DataModel newItem) {
//This is to see of the content of this object has changed. These items are only considered equal if areItemsTheSame() returned true.
//If this returns false, onBindViewHolder() is called with the holder containing the item, and the item's position.
return oldItem.getText().equals(newItem.getText()) && oldItem.isChecked() == newItem.isChecked();
}
@Override
public boolean areItemsTheSame(DataModel item1, DataModel item2) {
//Checks to see if these two items are the same. If not, it is added to the list, otherwise, check if content has changed.
return item1.equals(item2);
}
});
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View itemView = //Initiate your item view here.
return new ViewHolder(itemView);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
//Just update the holder with the object in the sorted list from the given position
DataModel model = mSortedList.get(position);
if (model != null) {
holder.setDataModel(model);
}
}
@Override
public int getItemCount() {
return mSortedList.size();
}
public void resetList(List<DataModel> models) {
//If you are performing multiple changes, use the batching methods to ensure proper animation.
mSortedList.beginBatchedUpdates();
mSortedList.clear();
mSortedList.addAll(models);
mSortedList.endBatchedUpdates();
}
//The following methods each modify the data set and automatically handles calling the appropriate 'notify' method on the adapter.
public void addModel(DataModel model) {
mSortedList.add(model);
}
public void addModels(List<DataModel> models) {
mSortedList.addAll(models);
}
public void clear() {
mSortedList.clear();
}
public void removeModel(DataModel model) {
mSortedList.remove(model);
}
public void removeModelAt(int i) {
mSortedList.removeItemAt(i);
}
}
RecyclerView avec DataBinding
Voici une classe ViewHolder générique que vous pouvez utiliser avec n'importe quelle disposition de DataBinding. Ici, une instance de classe ViewDataBinding
particulière est créée à l'aide de l'objet View
gonflé et de la classe d'utilitaire DataBindingUtil
.
import android.databinding.DataBindingUtil;
import android.support.v7.widget.RecyclerView;
import android.view.View;
public class BindingViewHolder<T> extends RecyclerView.ViewHolder{
private final T binding;
public BindingViewHolder(View itemView) {
super(itemView);
binding = (T)DataBindingUtil.bind(itemView);
}
public T getBinding() {
return binding;
}
}
Après avoir créé cette classe, vous pouvez utiliser <layout>
dans votre fichier de mise en page pour activer la liaison de données pour cette mise en page, comme ceci:
file name: my_item.xml
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="item"
type="ItemModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:text="@{item.itemLabel}" />
</LinearLayout>
</layout>
et voici votre exemple de dataModel:
public class ItemModel {
public String itemLabel;
}
Par défaut, la bibliothèque Android Data Binding génère une classe ViewDataBinding
basée sur le nom du fichier de mise en page, en la convertissant en cas Pascal et en y ajoutant le suffixe "Binding". Pour cet exemple, ce serait MyItemBinding
pour le fichier de présentation my_item.xml
. Cette classe Binding aurait également une méthode setter pour définir l'objet défini en tant que données dans le fichier de disposition ( ItemModel
pour cet exemple).
Maintenant que nous avons toutes les pièces, nous pouvons implémenter notre adaptateur comme ceci:
class MyAdapter extends RecyclerView.Adapter<BindingViewHolder<MyItemBinding>>{
ArrayList<ItemModel> items = new ArrayList<>();
public MyAdapter(ArrayList<ItemModel> items) {
this.items = items;
}
@Override public BindingViewHolder<MyItemBinding> onCreateViewHolder(ViewGroup parent, int viewType) {
return new BindingViewHolder<>(LayoutInflater.from(parent.getContext()).inflate(R.layout.my_item, parent, false));
}
@Override public void onBindViewHolder(BindingViewHolder<ItemModel> holder, int position) {
holder.getBinding().setItemModel(items.get(position));
holder.getBinding().executePendingBindings();
}
@Override public int getItemCount() {
return items.size();
}
}
Défilement sans fin dans Recycleview.
Ici, j'ai partagé un extrait de code pour implémenter le défilement sans fin dans la vue de recyclage.
Étape 1: Créez d'abord une méthode abstraite dans l'adaptateur Recycleview, comme ci-dessous.
public abstract class ViewAllCategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public abstract void load();
}
Étape 2: Remplacez maintenant la méthode onBindViewHolder et getItemCount()
de la classe ViewAllCategoryAdapter et appelez la méthode Load()
comme ci-dessous.
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, final int position) {
if ((position >= getItemCount() - 1)) {
load();
}
}
@Override
public int getItemCount() {
return YOURLIST.size();
}
Etape 3: Maintenant que chaque logique de backend est terminée, il est temps d'exécuter cette logique. C'est simple, vous pouvez remplacer la méthode de chargement où vous créez l'objet de votre adaptateur.
adapter = new ViewAllCategoryAdapter(CONTEXT, YOURLIST) {
@Override
public void load() {
/* do your stuff here */
/* This method is automatically call while user reach at end of your list. */
}
};
recycleCategory.setAdapter(adapter);
La méthode load()
appelle maintenant automatiquement lorsque l'utilisateur fait défiler à la fin de la liste.
Meilleure chance
Afficher la vue par défaut jusqu'à ce que les éléments se chargent ou quand les données ne sont pas disponibles
Classe d'adaptateur
private class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final int EMPTY_VIEW = 77777;
List<CustomData> datalist = new ArrayList<>();
MyAdapter() {
super();
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(parent.getContext());
if (viewType == EMPTY_VIEW) {
return new EmptyView(layoutInflater.inflate(R.layout.nothing_yet, parent, false));
} else {
return new ItemView(layoutInflater.inflate(R.layout.my_item, parent, false));
}
}
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
if (getItemViewType(position) == EMPTY_VIEW) {
EmptyView emptyView = (EmptyView) holder;
emptyView.primaryText.setText("No data yet");
emptyView.secondaryText.setText("You're doing good !");
emptyView.primaryText.setCompoundDrawablesWithIntrinsicBounds(null, new IconicsDrawable(getActivity()).icon(FontAwesome.Icon.faw_ticket).sizeDp(48).color(Color.DKGRAY), null, null);
} else {
ItemView itemView = (ItemView) holder;
// Bind data to itemView
}
}
@Override
public int getItemCount() {
return datalist.size() > 0 ? datalist.size() : 1;
}
@Override
public int getItemViewType(int position) {
if datalist.size() == 0) {
return EMPTY_VIEW;
}
return super.getItemViewType(position);
}
}
nothing_yet.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical"
android:paddingBottom="100dp"
android:paddingTop="100dp">
<TextView
android:id="@+id/nothingPrimary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:drawableTint="@android:color/secondary_text_light"
android:drawableTop="@drawable/ic_folder_open_black_24dp"
android:enabled="false"
android:fontFamily="sans-serif-light"
android:text="No Item's Yet"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="@android:color/secondary_text_light"
android:textSize="40sp"
tools:targetApi="m" />
<TextView
android:id="@+id/nothingSecondary"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:enabled="false"
android:fontFamily="sans-serif-condensed"
android:text="You're doing good !"
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="@android:color/tertiary_text_light" />
</LinearLayout>
J'utilise FontAwesome avec Iconics Library pour les images. Ajoutez ceci à votre fichier build.gradle au niveau de l'application.
compile 'com.mikepenz:fontawesome-typeface:4.6.0.3@aar'
compile 'com.mikepenz:iconics-core:2.8.1@aar'
Ajouter des lignes de séparation aux éléments RecyclerView
Ajoutez simplement ces lignes à l'initialisation
RecyclerView mRecyclerView = (RecyclerView) view.findViewById(recyclerView);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL));
Ajoutez un adapter
et appelez .notifyDataSetChanged();
comme d'habitude !
Ce n'est pas une fonctionnalité intégrée de Recyclerview, mais ajoutée aux bibliothèques de support. Donc, n'oubliez pas d'inclure ceci dans votre fichier build.gradle au niveau de l'application
compile "com.android.support:appcompat-v7:25.3.1"
compile "com.android.support:recyclerview-v7:25.3.1"
Plusieurs ItemDecorations peuvent être ajoutés à un seul RecyclerView.
Changer la couleur du séparateur :
Il est assez facile de définir une couleur pour un objetDecoration.
- l'étape est la suivante: créer un fichier
divider.xml
qui se trouve dansdrawable
dossierdrawable
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="line">
<size
android:width="1px"
android:height="1px"/>
<solid android:color="@color/divider_color"/>
</shape>
- l'étape est la suivante: mise en dessin
// Get drawable object
Drawable mDivider = ContextCompat.getDrawable(m_jContext, R.drawable.divider);
// Create a DividerItemDecoration whose orientation is Horizontal
DividerItemDecoration hItemDecoration = new DividerItemDecoration(m_jContext,
DividerItemDecoration.HORIZONTAL);
// Set the drawable on it
hItemDecoration.setDrawable(mDivider);
// Create a DividerItemDecoration whose orientation is vertical
DividerItemDecoration vItemDecoration = new DividerItemDecoration(m_jContext,
DividerItemDecoration.VERTICAL);
// Set the drawable on it
vItemDecoration.setDrawable(mDivider);