Android
RecyclerView y LayoutManagers
Buscar..
GridLayoutManager con recuento dinámico de span
Al crear una recyclerview con un administrador de diseño de gridlayout, debe especificar el recuento de intervalo en el constructor. El recuento de span se refiere al número de columnas. Esto es bastante torpe y no tiene en cuenta los tamaños de pantalla más grandes o la orientación de la pantalla. Un enfoque es crear diseños múltiples para los distintos tamaños de pantalla. Otro enfoque más dinámico se puede ver a continuación.
Primero creamos una clase personalizada RecyclerView de la siguiente manera:
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);
}
}
}
Esta clase determina cuántas columnas pueden caber en el recyclerview. Para usarlo, deberá colocarlo en su layout.xml de la siguiente manera:
<?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"
/>
Observe que usamos el atributo columnWidth. La recyclerview lo necesitará para determinar cuántas columnas cabrán en el espacio disponible.
En su actividad / fragmento, solo obtiene una referencia a recylerview y le asigna un adaptador (y las decoraciones o animaciones de elementos que desee agregar). NO CONFIGURAR UN GERENTE DE DISEÑO
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.auto_fit_recycler_view);
recyclerView.setAdapter(new MyAdapter());
(donde MyAdapter es su clase de adaptador)
Ahora tiene una vista de reciclaje que ajustará el número de usuarios (es decir, columnas) para que se ajuste al tamaño de la pantalla. Como adición final, es posible que desee centrar las columnas en recyclerview (de forma predeterminada, están alineadas con layout_start). Puedes hacerlo modificando un poco la clase AutofitRecyclerView. Comience creando una clase interna en el recyclerview. Esta será una clase que se extiende desde GridLayoutManager. Agregará suficiente relleno a la izquierda y la derecha para centrar las filas:
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();
}
}
}
Luego, cuando configura LayoutManager en AutofitRecyclerView, use CenteredGridLayoutManager de la siguiente manera:
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);
}
¡Y eso es! Usted tiene un recuento basado en el centro de gestión de cuadrantes alineado dinámico basado en administrador de recortes.
Fuentes:
Agregar vista de encabezado a recyclerview con el administrador de gridlayout
Para agregar un encabezado a una recyclerview con un gridlayout, primero se debe informar al adaptador que la vista del encabezado es la primera posición, en lugar de la celda estándar utilizada para el contenido. A continuación, se debe informar al administrador de diseño que la primera posición debe tener un intervalo igual al * recuento de intervalos de toda la lista. *
Tome una clase regular RecyclerView.Adapter y configúrela de la siguiente manera:
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
}
}
Luego en la actividad / fragmento:
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);
Se puede utilizar el mismo método para agregar un pie de página además de o en lugar de un encabezado.
Fuente: blog Square Island de Chiu-Ki Chan.
Lista simple con LinearLayoutManager
Este ejemplo agrega una lista de lugares con imagen y nombre usando una ArrayList
de objetos personalizados de Place
como conjunto de datos.
Diseño de la actividad
El diseño de la actividad / fragmento o donde se utiliza RecyclerView solo tiene que contener RecyclerView. No hay ScrollView o un diseño específico necesario.
<?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>
Definir el modelo de datos.
Puede usar cualquier clase o tipo de datos primitivos como modelo, como int
, String
, float[]
o CustomObject
. RecyclerView se referirá a una List
de estos objetos / primitivos.
Cuando un elemento de la lista hace referencia a diferentes tipos de datos como texto, números, imágenes (como en este ejemplo con lugares), a menudo es una buena idea usar un objeto personalizado.
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;
}
}
Lista de elementos de diseño
Debe especificar un archivo de diseño xml que se utilizará para cada elemento de la lista. En este ejemplo, se utiliza un ImageView
para la imagen y un TextView
para el nombre. LinearLayout
coloca el ImageView
a la izquierda y el TextView
a la derecha de la imagen.
<?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>
Crear un adaptador RecyclerView y ViewHolder
A continuación, debe heredar RecyclerView.Adapter
y RecyclerView.ViewHolder
. Una estructura de clase habitual sería:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
public class ViewHolder extends RecyclerView.ViewHolder {
// ...
}
}
Primero, implementamos el ViewHolder
. Solo hereda el constructor predeterminado y guarda las vistas necesarias en algunos campos:
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);
}
}
El constructor del adaptador establece el conjunto de datos utilizado:
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
private List<Place> mPlaces;
public PlaceListAdapter(List<Place> contacts) {
mPlaces = contacts;
}
// ...
}
Para usar el diseño de nuestro elemento de lista personalizado, reemplazamos el método onCreateViewHolder(...)
. En este ejemplo, el archivo de diseño se llama 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);
}
// ...
}
En onBindViewHolder(...)
, configuramos el contenido de las vistas. Obtenemos el modelo usado al encontrarlo en la List
en la posición dada y luego configuramos la imagen y el nombre en las vistas de 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());
}
// ...
}
También necesitamos implementar getItemCount()
, que simplemente devuelve el tamaño de la List
.
public class PlaceListAdapter extends RecyclerView.Adapter<PlaceListAdapter.ViewHolder> {
// ...
@Override
public int getItemCount() {
return mPlaces.size();
}
// ...
}
(Generar datos aleatorios)
Para este ejemplo, generaremos algunos lugares al azar.
@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;
}
Conecte el RecyclerView con el PlaceListAdapter y el conjunto de datos
Conectar un RecyclerView
con un adaptador es muy fácil. LinearLayoutManager
configurar LinearLayoutManager
como administrador de diseño para lograr el diseño de la lista.
@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));
}
¡Hecho!
StaggeredGridLayoutManager
- Crea tu RecyclerView en tu archivo xml de diseño:
<android.support.v7.widget.RecyclerView
android:id="@+id/recycleView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Crea tu clase de modelo para mantener tus datos:
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; }
Cree un archivo de diseño para contener los elementos 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"/>
Cree la clase de adaptador para 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); } } }
Cree una instancia de RecyclerView en su actividad o fragmento:
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() );
No olvide agregar la dependencia de Picasso en su archivo build.gradle:
compile 'com.squareup.picasso:picasso:2.5.2'