Android
RecyclerView et LayoutManagers
Recherche…
GridLayoutManager avec comptage dynamique
Lors de la création d'une recyclerview avec un gestionnaire de disposition gridlayout, vous devez spécifier le comptage de port dans le constructeur. Le nombre de plages correspond au nombre de colonnes. Ceci est assez encombrant et ne prend pas en compte les grandes tailles d'écran ou l'orientation de l'écran. Une approche consiste à créer plusieurs dispositions pour les différentes tailles d'écran. Une autre approche plus dynamique peut être vue ci-dessous.
Tout d'abord, nous créons une classe RecyclerView personnalisée comme suit:
public class AutofitRecyclerView extends RecyclerView {
private GridLayoutManager manager;
private int columnWidth = -1;
public AutofitRecyclerView(Context context) {
super(context);
init(context, null);
}
public AutofitRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context, attrs);
}
public AutofitRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context, attrs);
}
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
int[] attrsArray = {
android.R.attr.columnWidth
};
TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
columnWidth = array.getDimensionPixelSize(0, -1);
array.recycle();
}
manager = new GridLayoutManager(getContext(), 1);
setLayoutManager(manager);
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
if (columnWidth > 0) {
int spanCount = Math.max(1, getMeasuredWidth() / columnWidth);
manager.setSpanCount(spanCount);
}
}
}
Cette classe détermine le nombre de colonnes pouvant être intégrées à la recyclerview. Pour l'utiliser, vous devrez le mettre dans votre layout.xml comme suit:
<?xml version="1.0" encoding="utf-8"?>
<com.path.to.your.class.autofitRecyclerView.AutofitRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/auto_fit_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="200dp"
android:clipToPadding="false"
/>
Notez que nous utilisons l'attribut columnWidth. Recyclerview en aura besoin pour déterminer le nombre de colonnes pouvant être insérées dans l'espace disponible.
Dans votre activité / fragment, vous obtenez une référence à la recylerview et définissez-y un adaptateur (ainsi que les décorations ou animations que vous souhaitez ajouter). NE DÉFINISSEZ PAS DE GESTIONNAIRE DE MISE EN PAGE
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.auto_fit_recycler_view);
recyclerView.setAdapter(new MyAdapter());
(où MyAdapter est votre classe d'adaptateur)
Vous avez maintenant une liste de recyclage qui ajustera le compte (c.-à-d. Les colonnes) en fonction de la taille de l'écran. En dernier lieu, vous voudrez peut-être centrer les colonnes dans la recyclerview (par défaut, elles sont alignées sur layout_start). Vous pouvez le faire en modifiant un peu la classe AutofitRecyclerView. Commencez par créer une classe interne dans la recyclerview. Ce sera une classe qui s'étend de GridLayoutManager. Il ajoutera suffisamment de remplissage à gauche et à droite pour centrer les lignes:
public class AutofitRecyclerView extends RecyclerView {
// etc see above
private class CenteredGridLayoutManager extends GridLayoutManager {
public CenteredGridLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public CenteredGridLayoutManager(Context context, int spanCount) {
super(context, spanCount);
}
public CenteredGridLayoutManager(Context context, int spanCount, int orientation, boolean reverseLayout) {
super(context, spanCount, orientation, reverseLayout);
}
@Override
public int getPaddingLeft() {
final int totalItemWidth = columnWidth * getSpanCount();
if (totalItemWidth >= AutofitRecyclerView.this.getMeasuredWidth()) {
return super.getPaddingLeft(); // do nothing
} else {
return Math.round((AutofitRecyclerView.this.getMeasuredWidth() / (1f + getSpanCount())) - (totalItemWidth / (1f + getSpanCount())));
}
}
@Override
public int getPaddingRight() {
return getPaddingLeft();
}
}
}
Ensuite, lorsque vous définissez le LayoutManager dans AutofitRecyclerView, utilisez le CenteredGridLayoutManager comme suit:
private void init(Context context, AttributeSet attrs) {
if (attrs != null) {
int[] attrsArray = {
android.R.attr.columnWidth
};
TypedArray array = context.obtainStyledAttributes(attrs, attrsArray);
columnWidth = array.getDimensionPixelSize(0, -1);
array.recycle();
}
manager = new CenteredGridLayoutManager(getContext(), 1);
setLayoutManager(manager);
}
Et c'est tout! Vous disposez d'un compte dynamique, centré sur la grille, basé sur le centre de grille.
Sources:
Ajout de la vue d'en-tête à recyclerview avec le gestionnaire gridlayout
Pour ajouter un en-tête à une recyclerview avec un gridlayout, il faut d'abord indiquer à l'adaptateur que la vue d'en-tête est la première position, plutôt que la cellule standard utilisée pour le contenu. Ensuite, le gestionnaire de disposition doit être informé que la première position doit avoir une portée égale au nombre * span de la liste entière. *
Prenez une classe RecyclerView.Adapter régulière et configurez-la comme suit:
public class HeaderAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int ITEM_VIEW_TYPE_HEADER = 0;
private static final int ITEM_VIEW_TYPE_ITEM = 1;
private List<YourModel> mModelList;
public HeaderAdapter (List<YourModel> modelList) {
mModelList = modelList;
}
public boolean isHeader(int position) {
return position == 0;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
if (viewType == ITEM_VIEW_TYPE_HEADER) {
View headerView = inflater.inflate(R.layout.header, parent, false);
return new HeaderHolder(headerView);
}
View cellView = inflater.inflate(R.layout.gridcell, parent, false);
return new ModelHolder(cellView);
}
@Override
public int getItemViewType(int position) {
return isHeader(position) ? ITEM_VIEW_TYPE_HEADER : ITEM_VIEW_TYPE_ITEM;
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder h, int position) {
if (isHeader(position)) {
return;
}
final YourModel model = mModelList.get(position -1 ); // Subtract 1 for header
ModelHolder holder = (ModelHolder) h;
// populate your holder with data from your model as usual
}
@Override
public int getItemCount() {
return _categories.size() + 1; // add one for the header
}
}
Puis dans l'activité / fragment:
final HeaderAdapter adapter = new HeaderAdapter (mModelList);
final GridLayoutManager manager = new GridLayoutManager();
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
@Override
public int getSpanSize(int position) {
return adapter.isHeader(position) ? manager.getSpanCount() : 1;
}
});
mRecyclerView.setLayoutManager(manager);
mRecyclerView.setAdapter(adapter);
La même approche peut être utilisée en ajoutant un pied de page en plus ou au lieu d'un en-tête.
Source: blog Square Chiu-Ki Chan
Liste simple avec LinearLayoutManager
Cet exemple ajoute une liste des lieux avec l' image et le nom en utilisant une ArrayList
de la coutume Place
des objets comme jeu de données.
Disposition de l'activité
La disposition de l'activité / du fragment ou de l'emplacement où RecyclerView est utilisé doit uniquement contenir RecyclerView. Il n'y a pas de ScrollView ou une disposition spécifique nécessaire.
<?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/my_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
Définir le modèle de données
Vous pouvez utiliser n'importe quel type de données de classe ou primitive en tant que modèle, comme int
, String
, float[]
ou CustomObject
. RecyclerView fera référence à une List
de ces objets / primitives.
Lorsqu'un élément de liste fait référence à différents types de données tels que du texte, des nombres, des images (comme dans cet exemple avec des espaces), il est souvent utile d'utiliser un objet personnalisé.
public class Place {
// these fields will be shown in a list item
private Bitmap image;
private String name;
// typical constructor
public Place(Bitmap image, String name) {
this.image = image;
this.name = name;
}
// getters
public Bitmap getImage() {
return image;
}
public String getName() {
return name;
}
}
Mise en page des éléments de la liste
Vous devez spécifier un fichier de présentation xml qui sera utilisé pour chaque élément de la liste. Dans cet exemple, un ImageView
est utilisé pour l'image et un TextView
pour le nom. Le LinearLayout
positionne le ImageView
à gauche et le TextView
droit à l'image.
<?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:gravity="center_vertical"
android:orientation="horizontal"
android:padding="8dp">
<ImageView
android:id="@+id/image"
android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
Créez un adaptateur RecyclerView et ViewHolder
Ensuite, vous devez hériter de RecyclerView.Adapter
et du RecyclerView.ViewHolder
. Une structure de classe habituelle serait:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
public class ViewHolder extends RecyclerView.ViewHolder {
// ...
}
}
Tout d'abord, nous implémentons le ViewHolder
. Il hérite uniquement du constructeur par défaut et enregistre les vues nécessaires dans certains champs:
public class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private TextView nameView;
public ViewHolder(View itemView) {
super(itemView);
imageView = (ImageView) itemView.findViewById(R.id.image);
nameView = (TextView) itemView.findViewById(R.id.name);
}
}
Le constructeur de l'adaptateur définit le jeu de données utilisé:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
private List<Place> mPlaces;
public PlaceListAdapter(List<Place> contacts) {
mPlaces = contacts;
}
// ...
}
Pour utiliser notre disposition d'élément de liste personnalisée, nous onCreateViewHolder(...)
la méthode onCreateViewHolder(...)
. Dans cet exemple, le fichier de disposition est appelé place_list_item.xml
.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(
R.layout.place_list_item,
parent,
false
);
return new ViewHolder(view);
}
// ...
}
Dans le onBindViewHolder(...)
, nous définissons réellement le contenu des vues. Nous obtenons le modèle utilisé en le trouvant dans la List
à la position donnée, puis nous définissons l'image et le nom sur les ViewHolder
du ViewHolder
.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public void onBindViewHolder(PlaceListAdapter.ViewHolder viewHolder, int position) {
Place place = mPlaces.get(position);
viewHolder.nameView.setText(place.getName());
viewHolder.imageView.setImageBitmap(place.getImage());
}
// ...
}
Nous devons également mettre en œuvre getItemCount()
, qui renvoie simplement la List
taille de.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public int getItemCount() {
return mPlaces.size();
}
// ...
}
(Générer des données aléatoires)
Pour cet exemple, nous allons générer des endroits aléatoires.
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
List<Place> places = randomPlaces(5);
// ...
}
private List<Place> randomPlaces(int amount) {
List<Place> places = new ArrayList<>();
for (int i = 0; i < amount; i++) {
places.add(new Place(
BitmapFactory.decodeResource(getResources(), Math.random() > 0.5 ?
R.drawable.ic_account_grey600_36dp :
R.drawable.ic_android_grey600_36dp
),
"Place #" + (int) (Math.random() * 1000)
));
}
return places;
}
Connectez le RecyclerView à la PlaceListAdapter et à l'ensemble de données
Connecter un RecyclerView
à un adaptateur est très simple. Vous devez définir LinearLayoutManager
comme gestionnaire de disposition pour obtenir la disposition de liste.
@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
recyclerView.setAdapter(new PlaceListAdapter(places));
recyclerView.setLayoutManager(new LinearLayoutManager(this));
}
Terminé!
StaggeredGridLayoutManager
- Créez votre RecyclerView dans votre fichier XML de mise en page:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Créez votre classe de modèle pour conserver vos données:
public class PintrestItem { String url; public PintrestItem(String url,String name){ this.url=url; this.name=name; } public String getUrl() { return url; } public String getName(){ return name; } String name; }
Créez un fichier de disposition pour contenir les éléments RecyclerView:
<ImageView android:layout_width="match_parent" android:layout_height="wrap_content" android:adjustViewBounds="true" android:scaleType="centerCrop" android:id="@+id/imageView"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:id="@+id/name" android:layout_gravity="center" android:textColor="@android:color/white"/>
Créez la classe d'adaptateur pour RecyclerView:
public class PintrestAdapter extends RecyclerView.Adapter<PintrestAdapter.PintrestViewHolder>{ private ArrayList<PintrestItem>images; Picasso picasso; Context context; public PintrestAdapter(ArrayList<PintrestItem>images,Context context){ this.images=images; picasso=Picasso.with(context); this.context=context; } @Override public PintrestViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view= LayoutInflater.from(parent.getContext()).inflate(R.layout.pintrest_layout_item,parent,false); return new PintrestViewHolder(view); } @Override public void onBindViewHolder(PintrestViewHolder holder, int position) { picasso.load(images.get(position).getUrl()).into(holder.imageView); holder.tv.setText(images.get(position).getName()); } @Override public int getItemCount() { return images.size(); } public class PintrestViewHolder extends RecyclerView.ViewHolder{ ImageView imageView; TextView tv; public PintrestViewHolder(View itemView) { super(itemView); imageView=(ImageView)itemView.findViewById(R.id.imageView); tv=(TextView)itemView.findViewById(R.id.name); } } }
Instanciez le RecyclerView dans votre activité ou fragment:
RecyclerView recyclerView = (RecyclerView)findViewById(R.id.recyclerView); //Create the instance of StaggeredGridLayoutManager with 2 rows i.e the span count and provide the orientation StaggeredGridLayoutManager layoutManager=new new StaggeredGridLayoutManager(2, StaggeredGridLayoutManager.VERTICAL); recyclerView.setLayoutManager(layoutManager); // Create Dummy Data and Add to your List<PintrestItem> List<PintrestItem>items=new ArrayList<PintrestItem> items.add(new PintrestItem("url of image you want to show","imagename")); items.add(new PintrestItem("url of image you want to show","imagename")); items.add(new PintrestItem("url of image you want to show","imagename")); recyclerView.setAdapter(new PintrestAdapter(items,getContext() );
N'oubliez pas d'ajouter la dépendance Picasso dans votre fichier build.gradle:
compile 'com.squareup.picasso:picasso:2.5.2'