Ejemplo de Tutorial de Android RecyclerView, Android CardView

Android RecyclerView y Android CardView fueron introducidos en Android Lollipop con Material Design. Para aquellos que no están al tanto del Material Design, es una guía completa de widgets de interfaz de usuario introducidos desde Android 5.0 y mejora el atractivo visual de las aplicaciones.

Android RecyclerView

Android RecyclerView es una versión más avanzada, poderosa y flexible del ListView. Android RecyclerView es similar a ListView excepto que nos obliga a usar la clase RecyclerView.ViewHolder para contener los elementos, lo cual no es una obligación en ListView. Como sugiere el nombre, Android RecyclerView se utiliza para reutilizar celdas al desplazarse hacia arriba y hacia abajo mediante el reciclaje de los elementos en la lista. Otra mejora en RecyclerView es que nos permite establecer los LayoutManagers dinámicamente en tiempo de ejecución, a diferencia de ListView que solo estaba disponible en una lista de desplazamiento vertical. RecyclerView nos permite establecer los siguientes tipos de diseños en tiempo de ejecución.

  • LinearLayoutManager: admite listas tanto verticales como horizontales
  • StaggeredLayoutManager: admite listas escalonadas
  • GridLayoutManager: admite la visualización de cuadrículas como se ve en GalleryView anteriormente

Clases de Android RecyclerView

  • La clase RecyclerView.ItemAnimator proporciona un mejor soporte para animar las vistas a diferencia de las ListViews
  • La clase RecyclerView.ItemDecorator proporciona un mejor soporte cuando se trata de agregar bordes y divisores, otorgando así un gran control a nosotros

Por lo tanto, un RecyclerView es más personalizable en comparación con un ListView y brinda un mayor control a los usuarios. El RecyclerView está disponible en la biblioteca de soporte. Por lo tanto, debemos modificar nuestro script de Gradle para agregar la siguiente dependencia.

dependencies {
       compile 'com.android.support:recyclerview-v7:21.0.0-rc1'
 }

Android CardView

El componente de interfaz de usuario Android CardView muestra información dentro de tarjetas. Este componente se utiliza generalmente para mostrar información de contacto. Este componente está disponible en otra biblioteca de soporte, por lo que también debemos agregar su dependencia.

dependencies {
        compile 'com.android.support:cardview-v7:21.0.0-rc1'
        compile 'com.android.support:recyclerview-v7:21.0.0-rc1'
 }

El widget CardView de Android nos permite controlar el color de fondo, la sombra, el radio de las esquinas, la elevación, etc. Para utilizar los atributos personalizados en XML, necesitamos agregar la siguiente declaración de espacio de nombres al diseño principal. A continuación se muestra la declaración de espacio de nombres con algunos atributos de nuestro proyecto.

<android.support.v7.widget.CardView
        android:id="@+id/card_view"
        xmlns:card_view="https://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardBackgroundColor="@color/grey_300"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp"
        card_view:cardUseCompatPadding="true">

Los atributos importantes utilizados anteriormente son:

  • card_view:cardCornerRadius: Se utiliza para establecer el radio de las esquinas en nuestros diseños
  • card_view:cardBackgroundColor: Se utiliza para establecer el color de fondo de la vista

En nuestro proyecto de ejemplo, agregaremos un RecyclerView para mostrar una lista de CardViews que contienen nombres y números de versiones de Android junto con un logotipo de muestra. El evento onclick del CardView está programado para eliminar esa tarjeta de la lista. Hemos agregado una opción de menú en la ActionBar para agregar de nuevo las tarjetas eliminadas en orden. Nota: Las imágenes de logotipos se toman al azar de Google. Por lo tanto, los tamaños pueden variar.

Ejemplo de RecyclerView y CardView de Android

El proyecto consiste en una MainActivity que muestra el RecyclerView. El CardView se añade al RecyclerView desde la clase CustomAdapter. El DataModel se utiliza para recuperar los datos de cada CardView a través de los getters. La clase MyData contiene los arrays de textviews y drawables junto con sus identificadores.

Ejemplo de código de Android RecyclerView y CardView

El archivo activity_main.xml contiene el RecyclerView dentro de un RelativeLayout como se muestra a continuación. Código activity_main.xml:

<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context=".MainActivity"
    android:background="@color/grey_300"
    >

    <android.support.v7.widget.RecyclerView
        android:id="@+id/my_recycler_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="vertical"
        />

</RelativeLayout>

El diseño de CardView de Android está definido a continuación: Código cards_layout.xml:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="https://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:tag="cards main container">

    <android.support.v7.widget.CardView
        android:id="@+id/card_view"
        xmlns:card_view="https://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        card_view:cardBackgroundColor="@color/color_white"
        card_view:cardCornerRadius="10dp"
        card_view:cardElevation="5dp"
        card_view:cardUseCompatPadding="true">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >

            <ImageView
                android:id="@+id/imageView"
                android:tag="image_tag"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_margin="5dp"
                android:layout_weight="1"
                android:src="@drawable/ic_launcher"/>

            <LinearLayout
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_marginTop="12dp"
                android:layout_weight="2"
                android:orientation="vertical"
                >

                <TextView
                    android:id="@+id/textViewName"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="10dp"
                    android:text="Android Name"
                    android:textAppearance="?android:attr/textAppearanceLarge"/>

                <TextView
                    android:id="@+id/textViewVersion"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_gravity="center_horizontal"
                    android:layout_marginTop="10dp"

                    android:text="Android Version"
                    android:textAppearance="?android:attr/textAppearanceMedium"/>

            </LinearLayout>
        </LinearLayout>

    </android.support.v7.widget.CardView>

</LinearLayout>

CardView de Android contiene un ImageView junto con dos TextViews en un Nested Linear Layout. El archivo menu_main.xml contiene un único elemento para agregar las tarjetas eliminadas. Código menu_main.xml:

<menu xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:app="https://schemas.android.com/apk/res-auto"
    xmlns:tools="https://schemas.android.com/tools"
    tools:context=".MainActivity">
    <item android:id="@+id/add_item"
        android:title="Add"
        android:orderInCategory="100"
        app:showAsAction="always"/>
</menu>

La clase MainActivity.java está definida a continuación:

package com.journaldev.recyclerviewcardview;

import android.content.Context;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private static RecyclerView.Adapter adapter;
    private RecyclerView.LayoutManager layoutManager;
    private static RecyclerView recyclerView;
    private static ArrayList data;
    static View.OnClickListener myOnClickListener;
    private static ArrayList removedItems;

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

        myOnClickListener = new MyOnClickListener(this);

        recyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
        recyclerView.setHasFixedSize(true);

        layoutManager = new LinearLayoutManager(this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setItemAnimator(new DefaultItemAnimator());

        data = new ArrayList();
        for (int i = 0; i < MyData.nameArray.length; i++) {
            data.add(new DataModel(
                    MyData.nameArray[i],
                    MyData.versionArray[i],
                    MyData.id_[i],
                    MyData.drawableArray[i]
            ));
        }

        removedItems = new ArrayList();

        adapter = new CustomAdapter(data);
        recyclerView.setAdapter(adapter);
    }


    private static class MyOnClickListener implements View.OnClickListener {

        private final Context context;

        private MyOnClickListener(Context context) {
            this.context = context;
        }

        @Override
        public void onClick(View v) {
            removeItem(v);
        }

        private void removeItem(View v) {
            int selectedItemPosition = recyclerView.getChildPosition(v);
            RecyclerView.ViewHolder viewHolder
                    = recyclerView.findViewHolderForPosition(selectedItemPosition);
            TextView textViewName
                    = (TextView) viewHolder.itemView.findViewById(R.id.textViewName);
            String selectedName = (String) textViewName.getText();
            int selectedItemId = -1;
            for (int i = 0; i < MyData.nameArray.length; i++) {
                if (selectedName.equals(MyData.nameArray[i])) {
                    selectedItemId = MyData.id_[i];
                }
            }
            removedItems.add(selectedItemId);
            data.remove(selectedItemPosition);
            adapter.notifyItemRemoved(selectedItemPosition);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);
        getMenuInflater().inflate(R.menu.menu_main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        super.onOptionsItemSelected(item);
        if (item.getItemId() == R.id.add_item) {
           //comprobar si hay elementos para agregar
            if (removedItems.size() != 0) {
                addRemovedItemToList();
            } else {
                Toast.makeText(this, "Nothing to add", Toast.LENGTH_SHORT).show();
            }
        }
        return true;
    }

    private void addRemovedItemToList() {
        int addItemAtListPosition = 3;
        data.add(addItemAtListPosition, new DataModel(
                MyData.nameArray[removedItems.get(0)],
                MyData.versionArray[removedItems.get(0)],
                MyData.id_[removedItems.get(0)],
                MyData.drawableArray[removedItems.get(0)]
        ));
        adapter.notifyItemInserted(addItemAtListPosition);
        removedItems.remove(0);
    }
}

El método removeItems() se invoca desde el método del escuchador para eliminar el CardView clicado. Su respectivo id se almacena en un array para recuperarlo más tarde. Para añadir la vista más tarde, hemos implementado otro método llamado addRemovedItemToList(). En este método, añadimos esa vista en una posición predefinida en la lista y eliminamos su id del array removedItems. El CustomAdapter es notificado en ambos casos. La clase CustomeAdapter.java se define a continuación :

package com.journaldev.recyclerviewcardview;

import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;

import java.util.ArrayList;

public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {

    private ArrayList<DataModel> dataSet;

    public static class MyViewHolder extends RecyclerView.ViewHolder {

        TextView textViewName;
        TextView textViewVersion;
        ImageView imageViewIcon;

        public MyViewHolder(View itemView) {
            super(itemView);
            this.textViewName = (TextView) itemView.findViewById(R.id.textViewName);
            this.textViewVersion = (TextView) itemView.findViewById(R.id.textViewVersion);
            this.imageViewIcon = (ImageView) itemView.findViewById(R.id.imageView);
        }
    }

    public CustomAdapter(ArrayList<DataModel> data) {
        this.dataSet = data;
    }

    @Override
    public MyViewHolder onCreateViewHolder(ViewGroup parent,
                                           int viewType) {
        View view = LayoutInflater.from(parent.getContext())
                .inflate(R.layout.cards_layout, parent, false);

        view.setOnClickListener(MainActivity.myOnClickListener);

        MyViewHolder myViewHolder = new MyViewHolder(view);
        return myViewHolder;
    }

    @Override
    public void onBindViewHolder(final MyViewHolder holder, final int listPosition) {

        TextView textViewName = holder.textViewName;
        TextView textViewVersion = holder.textViewVersion;
        ImageView imageView = holder.imageViewIcon;

        textViewName.setText(dataSet.get(listPosition).getName());
        textViewVersion.setText(dataSet.get(listPosition).getVersion());
        imageView.setImageResource(dataSet.get(listPosition).getImage());
    }

    @Override
    public int getItemCount() {
        return dataSet.size();
    }
}

En el código anterior, hemos implementado nuestro propio ViewHolder extendiendo RecyclerView.ViewHolder. La vista se infla desde el cards_layout.xml que hemos definido en el directorio de diseños. El onClickListener en MainActivity está adjunto a esta vista en el siguiente fragmento de código.

view.setOnClickListener(MainActivity.myOnClickListener);

Un ArrayList almacena todos los datos en forma de un objeto de clase DataModel en un ArrayList y los añade a las respectivas tarjetas en la lista. Las clases DataModel.java y MyData.java que contienen los datos específicos de esta aplicación se muestran a continuación :

package com.journaldev.recyclerviewcardview;

public class DataModel {

    String name;
    String version;
    int id_;
    int image;

    public DataModel(String name, String version, int id_, int image) {
        this.name = name;
        this.version = version;
        this.id_ = id_;
        this.image=image;
    }

    public String getName() {
        return name;
    }

    public String getVersion() {
        return version;
    }

    public int getImage() {
        return image;
    }

    public int getId() {
        return id_;
    }
}
package com.journaldev.recyclerviewcardview;

public class MyData {

    static String[] nameArray = {"Cupcake", "Donut", "Eclair", "Froyo", "Gingerbread", "Honeycomb", "Ice Cream Sandwich","JellyBean", "Kitkat", "Lollipop", "Marshmallow"};
    static String[] versionArray = {"1.5", "1.6", "2.0-2.1", "2.2-2.2.3", "2.3-2.3.7", "3.0-3.2.6", "4.0-4.0.4", "4.1-4.3.1", "4.4-4.4.4", "5.0-5.1.1","6.0-6.0.1"};

    static Integer[] drawableArray = {R.drawable.cupcake, R.drawable.donut, R.drawable.eclair,
            R.drawable.froyo, R.drawable.gingerbread, R.drawable.honeycomb, R.drawable.ics,
            R.drawable.jellybean, R.drawable.kitkat, R.drawable.lollipop,R.drawable.marsh};

    static Integer[] id_ = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
}

A continuación se muestra la salida producida por nuestra aplicación de ejemplo de RecyclerView y CardView para Android. Como puedes ver, el elemento eliminado siempre se añade en el tercer índice (cuarta posición en la lista). Esto pone fin a este tutorial sobre RecyclerView y CardView para Android. Puedes descargar el Proyecto de Ejemplo de Android RecyclerView CardView desde el siguiente enlace.

Descargar Proyecto de Ejemplo de Android RecyclerView CardView

Source:
https://www.digitalocean.com/community/tutorials/android-recyclerview-android-cardview-example-tutorial