Lollipop new features: RecyclerView

jueves, 7 de mayo de 2015


Con la llegada de Android 5 Lollipop llegaron una serie de novedades a la plataforma que voy a intentar repasar en este y próximos artículos. En vamos a empezar viendo el nuevo widget ReciclerView. El widget ReciclerView viene incluido dentro de la nueva versión actualizada de la libreria de soporte para versiones antiguas appcompat v7. Viene para sustituir a los widget ListView y GridView.

Introducción a ReciclerView

Es un contenedor de colección elementos en forma de lista pero es más flexible y oferce mejor rendimiento que ListView. Permite configurar una serie de animaciones para la eliminación, desplazamiento y creación de nuevos elementos en tiempo real.

La creación y administración de un RecyclerView es similar a la de un ListView. Se necesita un origen datos y un adaptador que los lea, interprete e infle, pero también es necesario un nuevo elemento llamado LayoutManager.

El LayoutManager es el encargado de reutilizar los views y pedir el nuevo contenido al adapter, se encarga de reciclar los elementos que dejan de ser visibles para el usuario al hacer scroll por el contenido de otros que son visibles.

En Lollipop vienen 3 LayoutManager :
  • LinearLayoutManager muestra los items en forma de lista horizontal o vertical
  • GridLayoutManager muestra los items en forma de grid
  • StaggeredGridLayoutManager muestra los items en forma de grid con celdas no uniformes.
El adapter tiene que extender de la clase RecyclerView.Adapter y viene integrado con el patrón ViewHolder, vamos a ver un ejemplo de cómo se implementa.

Ejemplo

Vamos a ver un ejemplo de un ReciclerView que va a ser un grid de imagenes.

Dependencias

En el fichero build.gradle del modulo de la aplicación tenemos que añadir las dependencias que vamos a necesitar.
  dependencies {
      ...
      compile 'com.android.support:appcompat-v7:22.0.0'
      compile 'com.android.support:recyclerview-v7:22.0.+'
      compile 'com.squareup.picasso:picasso:2.5.2'
  }


Picasso es una libería para descargar imágenes pero se queda fuera del alcance de este artículo, asi que no entraré en detalles.

Layouts

Vamos a tener 3 layouts:

activity_items.xml:

Nuestro ejemplo es sencillo a si que solo consta de un Framelayout donde visualizaremos despues el fragment con el contenido. Es una buena práctica en Android utilizar fragments para reutilización de vistas entre diferentes resoluciones.

<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:id="@+id/container"
    android:layout_width="match_parent" android:layout_height="match_parent"
    tools:context=".ItemsActivity" tools:ignore="MergeRootFrame" />


fragment_items.xml:

El layout del fragment va a ser donde vamos a tener realmente el RecyclerView.

&lt;RelativeLayout xmlns:android=&quot;http://schemas.android.com/apk/res/android&quot;
    xmlns:tools=&quot;http://schemas.android.com/tools&quot;
    android:layout_width=&quot;match_parent&quot;
    android:layout_height=&quot;match_parent&quot;
    tools:context=&quot;.ItemsActivity$ItemsFragment&quot;&gt;
    &lt;android.support.v7.widget.RecyclerView
        android:id=&quot;@+id/items_recycler_view&quot;
        android:scrollbars=&quot;vertical&quot;
        android:layout_width=&quot;match_parent&quot;
        android:layout_height=&quot;match_parent&quot;/&gt;
&lt;/RelativeLayout&gt;


items_item.xml:

El layout para cada elemento del RecyclerView, que consta de una imagen por el momento.
<?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="match_parent">
         <ImageView
         android:id="@+id/itemImage"
         android:layout_width="@dimen/image_width"
         android:layout_height="@dimen/image_height" />
</LinearLayout>

Modelo

El modelo es bastante sencillo y de momento para este artículo solo ultilizaremos el getter image.

public class Item {
    String title;
    String description;
    String imageUrl;

    public Item(String title, String description, String imageUrl) {
        this.title = title;
        this.description = description;
        this.imageUrl = imageUrl;
    }
}

Activity

El activity es bastante sencillo también solo visualiza el fragment.

public class ItemsActivity extends ActionBarActivity {

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

        if (savedInstanceState == null) {
            getSupportFragmentManager().beginTransaction()
                    .add(R.id.container, new ItemsFragment())
                    .commit();
        }
    }
}

Fragment

El fragment, es donde esta realmente esta parte del trabajo, crea los datos a representar, configura el RecyclerView y el adapter.

public class ItemsFragment extends Fragment {
    private RecyclerView recyclerView;
    private RecyclerView.Adapter adapter;
    private RecyclerView.LayoutManager layoutManager;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_items, container, false);

        recyclerView = (RecyclerView) rootView.findViewById(R.id.items_recycler_view);

        //poner a true si cambios en el contenido  no supone
        //cambios en el tamaño del control, esto mejora rendimiento
        recyclerView.setHasFixedSize(true);

        //usamos un GridLayoutManager con 2 columnas
        layoutManager = new GridLayoutManager(rootView.getContext(),2);

        recyclerView.setLayoutManager(layoutManager);

        adapter = new ItemsAdapter(createItems());

        recyclerView.setAdapter(adapter);
        return rootView;
    }


    private Item[] createItems(){

        String commonPath = "http://lorempixel.com/400/400/people/";

        Item[] items = new Item[]
                { new Item(null,null,commonPath + "1"),
                        new Item(null,null,commonPath + "2"),
                        new Item(null,null,commonPath + "3"),
                        new Item(null,null,commonPath + "4"),
                        new Item(null,null,commonPath + "5"),
                        new Item(null,null,commonPath + "6"),
                        new Item(null,null,commonPath + "7"),
                        new Item(null,null,commonPath + "8"),
                        new Item(null,null,commonPath + "9"),
                        new Item(null,null,commonPath + "10")};

        return items;
    }
}

Adapter

El Adapter es el encargado de representar los datos, fijaros la integración que tiene ahora un adapter con el ViewHolder, en versiones anteriores a Lollipop era bastante manual la aplicación de este patrón.

public class ItemsAdapter extends RecyclerView.Adapter {
    private Item[] items;

    public static class ViewHolder extends RecyclerView.ViewHolder {
        public ImageView imageView;

        public ViewHolder(View v) {
            super(v);
            imageView = (ImageView) v.findViewById(R.id.itemImage);;
        }
    }

    public ItemsAdapter(Item[] items) {
        this.items = items;
    }

    // crea una nueva vista(invocado por el layout manager)
    @Override
    public ItemsAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
                                                   int viewType) {
        //creamos la nueva imagen
        View v = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.item_items, parent, false);

        ViewHolder vh = new ViewHolder(v);
        return vh;
    }

    // Reemplaza el contenido de una vista (invocado por el layout manager)
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
        Item item = items[position];

        //cargamos la imagen con picasso
        Picasso.with(holder.imageView.getContext())
                .load(item.imageUrl)
                .into(holder.imageView);
    }

    @Override
    public int getItemCount() {
        return items.length;
    }
}

Concusiones

A partir de Lollipop existe un nuevo widget llamado RecyclerView que debe ser el que utilicemos para representar datos en forma de lista. Esta más optimizado que el antiguo ListView. Para configurarlo es parecido a ListView la diferencia es que viene integrado con el patrón ViewHolder y existe una nueva clase LatoutManager encargada de la gestion de reciclado.

Código fuente en GitHub - tag 0.1

Libros relacionados

Android Programming: The Big Nerd Ranch Guide
Android Programming In a Day!: The Power Guide for Beginners In Android App Programming

No hay comentarios:

Publicar un comentario