Android
RecyclerView e LayoutManagers
Ricerca…
GridLayoutManager con conteggio dinamico dello span
Quando si crea una panoramica di riciclo con un gestore di layout di griglia, è necessario specificare il conteggio di span nel costruttore. Il conteggio dello span si riferisce al numero di colonne. Questo è abbastanza goffo e non tiene conto delle dimensioni dello schermo o dell'orientamento dello schermo. Un approccio consiste nel creare più layout per le varie dimensioni dello schermo. Un altro approccio più dinamico può essere visto sotto.
Per prima cosa creiamo una classe RecyclerView personalizzata come segue:
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);
}
}
}
Questa classe determina il numero di colonne che possono essere inserite nel recyclerview. Per usarlo dovrai metterlo nel tuo layout.xml come segue:
<?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"
/>
Si noti che utilizziamo l'attributo columnWidth. Il recyclerview ne avrà bisogno per determinare quante colonne si inseriranno nello spazio disponibile.
Nella tua attività / frammento ottieni solo un riferimento al ricylerview e ne imposti un adattatore (e tutte le decorazioni o animazioni degli elementi che desideri aggiungere). NON IMPOSTARE UN MANAGER LAYOUT
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.auto_fit_recycler_view);
recyclerView.setAdapter(new MyAdapter());
(dove MyAdapter è la tua classe dell'adattatore)
Ora hai una panoramica sul riciclaggio che regolerà lo spancount (es. Colonne) per adattarsi alle dimensioni dello schermo. Come aggiunta finale, potresti voler centrare le colonne nel recyclerview (di default sono allineate a layout_start). Puoi farlo modificando leggermente la classe AutofitRecyclerView. Inizia creando una classe interiore nel recyclerview. Questa sarà una classe che si estende da GridLayoutManager. Aggiungerà abbastanza padding a sinistra e a destra per centrare le righe:
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();
}
}
}
Quindi, quando si imposta il LayoutManager in AutofitRecyclerView, utilizzare CenteredGridLayoutManager come segue:
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);
}
E questo è tutto! Hai uno spancount dinamico, riciclabile basato sul gridlayoutmanager allineato al centro.
fonti:
Aggiunta della vista intestazione a recyclerview con gridlayout manager
Per aggiungere un'intestazione a un recyclerview con un gridlayout, è innanzitutto necessario comunicare all'adattatore che la vista dell'intestazione è la prima posizione, piuttosto che la cella standard utilizzata per il contenuto. Successivamente, il gestore di layout deve essere informato che la prima posizione deve avere una span uguale al conteggio * span dell'intera lista. *
Prendi una normale classe RecyclerView.Adapter e configurala come segue:
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
}
}
Quindi nell'attività / frammento:
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);
Lo stesso approccio può essere utilizzato aggiungendo un footer in aggiunta o al posto di un'intestazione.
Fonte: blog Chiu-Ki Chan's Square Island
Elenco semplice con LinearLayoutManager
Questo esempio aggiunge un elenco di luoghi con l'immagine e il nome utilizzando un ArrayList
di costume Place
oggetti come set di dati.
Disposizione delle attività
Il layout dell'attività / frammento o in cui viene utilizzato solo RecyclerView deve contenere RecyclerView. Non c'è ScrollView o un layout specifico necessario.
<?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>
Definire il modello di dati
È possibile utilizzare qualsiasi tipo di classe o di dati primitivi come un modello, come int
, String
, float[]
o CustomObject
. RecyclerView farà riferimento a un List
di questi oggetti / primitive.
Quando una voce di elenco si riferisce a diversi tipi di dati come testo, numeri, immagini (come in questo esempio con i luoghi), è spesso una buona idea utilizzare un oggetto personalizzato.
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;
}
}
Elenca il layout dell'articolo
Devi specificare un file di layout xml che verrà utilizzato per ogni elemento dell'elenco. In questo esempio, viene utilizzato un ImageView
per l'immagine e un TextView
per il nome. Il LinearLayout
posiziona l' ImageView
a sinistra e il TextView
direttamente all'immagine.
<?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>
Creare un adattatore RecyclerView e ViewHolder
Successivamente, è necessario ereditare RecyclerView.Adapter
e RecyclerView.ViewHolder
. Una solita struttura di classe sarebbe:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
public class ViewHolder extends RecyclerView.ViewHolder {
// ...
}
}
Innanzitutto, implementiamo ViewHolder
. Assorbe solo il costruttore predefinito e salva le viste necessarie in alcuni campi:
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);
}
}
Il costruttore dell'adattatore imposta il set di dati utilizzato:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
private List<Place> mPlaces;
public PlaceListAdapter(List<Place> contacts) {
mPlaces = contacts;
}
// ...
}
Per usare il nostro layout di elenco personalizzato, sostituiamo il metodo onCreateViewHolder(...)
. In questo esempio, il file di layout si chiama 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);
}
// ...
}
In onBindViewHolder(...)
, effettivamente impostiamo i contenuti delle viste. Otteniamo il modello usato trovandolo nella List
nella posizione data e quindi impostando l'immagine e il nome nelle viste 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());
}
// ...
}
Dobbiamo inoltre implementare getItemCount()
, che restituisce semplicemente la dimensione della List
.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public int getItemCount() {
return mPlaces.size();
}
// ...
}
(Genera dati casuali)
Per questo esempio, genereremo alcuni posti casuali.
@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;
}
Collega RecyclerView con PlaceListAdapter e il set di dati
Connettere un RecyclerView
con un adattatore è molto semplice. È necessario impostare LinearLayoutManager
come gestore di layout per ottenere il layout dell'elenco.
@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));
}
Fatto!
StaggeredGridLayoutManager
- Crea la tua RecyclerView nel tuo file xml di layout:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Crea la tua classe Model per contenere i tuoi dati:
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; }
Creare un file di layout per conservare gli elementi di 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"/>
Creare la classe dell'adattatore per 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); } } }
Crea un'istanza di RecyclerView nella tua attività o frammento:
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() );
Non dimenticare di aggiungere la dipendenza Picasso nel tuo file build.gradle:
compile 'com.squareup.picasso:picasso:2.5.2'