Diseño Android: ListView con checkbox


Última actualización: 22/08/2015

android

En este tutorial vamos a ver cómo crear un ListView cuyas filas contengan un Checkbox de forma similar a Yahoo Mail.



yahoo mail app

En principio puede parecer sencillo, pero no basta con añadir un Checkbox al layout que usemos para las filas del ListView. Tal y como veremos, si queremos obtener un resultado óptimo tanto desde el punto de vista funcional como estético tendremos que ir solventando pequeños problemas que vamos a ir encontrando a medida que vayamos desarrollando esta funcionalidad.

Nota: Para personalizar el estilo de un checkbox, consultar el tip Android #07.

Proyecto de ejemplo

El proyecto de ejemplo de partida estará basado vagamente en el Tip Android #30: ListView estilo Google Play.Veamos en primer lugar el layout con el listview:

listview.xml

<?xml version="1.0" encoding="utf-8"?>

<ListView
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@android:id/list"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@color/background"
    android:cacheColorHint="@android:color/transparent"
    android:divider="@android:color/transparent"
    android:dividerHeight="8dp"
    android:scrollbarStyle="outsideOverlay"
    android:paddingBottom="8dp" 
    android:paddingLeft="5dp"
    android:paddingRight="5dp"
    android:paddingTop="15dp" >

</ListView>

Para las filas usaremos un Checkbox y dos TextView en un Relative Layout.

listview_row.xml

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" 
    android:background="@drawable/listview_selector">

    <CheckBox
        android:id="@+id/checkBox"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"/>

    <TextView
        android:id="@+id/textViewSubtitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignLeft="@+id/textViewTitle"
        android:layout_below="@+id/textViewTitle"
        android:textAppearance="?android:attr/textAppearanceMedium" 
        android:textColor="@android:color/black"/>

    <TextView
        android:id="@+id/textViewTitle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignBottom="@+id/checkBox"
        android:layout_toRightOf="@+id/checkBox"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:textColor="@android:color/black" />

</RelativeLayout>

El ListView mostrará objetos de la clase Row.

package com.danielme.blog.demo.listviewcheckbox;

/**
 * 
 * @author danielme.com
 *
 */
public class Row
{
	private String title;

	private String subtitle;

	private boolean checked;

	public String getTitle()
	{
		return title;
	}

	public void setTitle(String title)
	{
		this.title = title;
	}

	public String getSubtitle()
	{
		return subtitle;
	}

	public void setSubtitle(String subtitle)
	{
		this.subtitle = subtitle;
	}

	public boolean isChecked()
	{
		return checked;
	}

	public void setChecked(boolean checked)
	{
		this.checked = checked;
	}

}

La aplicación tiene una única Activity. Crearemos la lista con los datos de prueba y mostraremos el elemento seleccionado en un Toast.

package com.danielme.blog.demo.listviewcheckbox;

import java.util.ArrayList;
import java.util.List;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Toast;
import android.widget.AdapterView.OnItemClickListener;

/**
 * 
 * @author danielme.com
 * 
 */
public class MainActivity extends AppCompatActivity {
	List<Row> rows;

	private ListView listView;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.listview);
		listView = (ListView) findViewById(android.R.id.list);
		
		
		rows = new ArrayList<Row>(30);
		Row row = null;
		for (int i = 1; i < 31; i++) {
			row = new Row();
			row.setTitle("Title " + i);
			row.setSubtitle("Subtitle " + i);
			rows.add(row);
		}

		rows.get(3).setChecked(true);
		rows.get(6).setChecked(true);
		rows.get(9).setChecked(true);

		listView.setAdapter(new CustomArrayAdapter(this, rows));

		listView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				Toast.makeText(MainActivity.this,
						rows.get(position).getTitle(), Toast.LENGTH_SHORT)
						.show();
			}
		});
	}
}

Obsérvese que la Activity hereda de AppCompatActivity para utilizar la ActionBar proporcionada por la librería de compatibilidad. En este proyecto se utiliza para aplicar uno de los temas predefinidos.

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar" />
</resources>

Para poder utilizar la librería de compatibilidad es necesario incluirla en el proyecto como una dependencia. En Android Studio basta con definirla en el fichero /app/build.gradle, mientras que en Eclipse ADT hay que importarla como un proyecto con recursos. Para más información sobre la importación de librerías en Eclipse ADT y Android Studio, consultar este artículo
Cursos aplicaciones móviles

El Adapter para nuestras filas siguiendo el ViewHolderPattern es el siguiente (de momento nos olvidamos de la gestión de los Checkbox).

package com.danielme.blog.demo.listviewcheckbox;

import java.util.List;

import com.danielme.blog.demo.listviewcheckbox.R;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;

/**
 * Custom adapter - "View Holder Pattern".
 * 
 * @author danielme.com
 * 
 */
public class CustomArrayAdapter extends ArrayAdapter<Row>
{
	private LayoutInflater layoutInflater;

	public CustomArrayAdapter(Context context, List<Row> objects)
	{
		super(context, 0, objects);
		layoutInflater = LayoutInflater.from(context);
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent)
	{
		// holder pattern
		Holder holder = null;
		if (convertView == null)
		{
			holder = new Holder();

			convertView = layoutInflater.inflate(R.layout.listview_row, null);
			holder.setTextViewTitle((TextView) convertView.findViewById(R.id.textViewTitle));
			holder.setTextViewSubtitle((TextView) convertView.findViewById(R.id.textViewSubtitle));
			convertView.setTag(holder);
		}
		else
		{
			holder = (Holder) convertView.getTag();
		}

		Row row = getItem(position);
		holder.getTextViewTitle().setText(row.getTitle());
		holder.getTextViewSubtitle().setText(row.getSubtitle());
		return convertView;
	}

static class Holder
{
	TextView textViewTitle;
	TextView textViewSubtitle;
        CheckBox checkBox;

	public TextView getTextViewTitle()
	{
		return textViewTitle;
	}

	public void setTextViewTitle(TextView textViewTitle)
	{
		this.textViewTitle = textViewTitle;
	}

	public TextView getTextViewSubtitle()
	{
		return textViewSubtitle;
	}

	public void setTextViewSubtitle(TextView textViewSubtitle)
	{
		this.textViewSubtitle = textViewSubtitle;
	}
        public CheckBox getCheckBox() 
        {
		return checkBox;
        }
        public void setCheckBox(CheckBox checkBox) 
        {
		this.checkBox = checkBox;
	}

    }
}


Marcado del checkbox y selección de la fila

Si ejecutamos la app, encontraremos un problema: podemos marcar y desmarcar el checkbox pero no pulsar la fila. Esto es debido a que en nuestra fila hay elementos «focusables» que evitan que la fila completa pueda ser pulsada. Es un comportamiento un tanto desconcertante, pero podemos solventarlo simplemente haciendo los elementos de la fila del ListView como no «focusable» añadiendo el siguiente atributo:

android:focusable="false"

Para nuestro ejemplo es suficiente, pero hay una solución más práctica en el caso de que en nuestra fila tengamos más de un widget focusable por defecto: aplicar al layout padre de la fila el atributo descendantFocusability:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" 
    android:background="@drawable/listview_selector"
    android:descendantFocusability="blocksDescendants">

Nota: esta solución no es válida para aplicaciones que deban ser navegables mediante un pad o cruceta.

Tras este cambio, tanto el checkbox como la fila están plenamente operativos. Pero nuevamente podemos ver otro problema: al pulsar la fila, también se selecciona el checkbox y se le aplica el estilo correspondiente:

listview checkbox

Para solucionar este nuevo contratiempo utilizo la solución propuesta por Cyril Mottier, en este artículo. Consiste en implementar nuestro propio Checkbox, o el widget que sea, para que no se seleccione cuando lo haga el padre, es decir, la fila de la lista. Así pues, añadimos la siguiente clase al proyecto.

package com.danielme.blog.demo.listviewcheckbox;

import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.CheckBox;


/**
 * Prevents the checkboxes from being highlighted when the user presses the itemview
 * {@link http://android.cyrilmottier.com/?p=525}
 * @author Cyril Mottier
 *
 */
public class DontPressWhenPressParentCheckBox extends CheckBox
{

	public DontPressWhenPressParentCheckBox(Context context)
	{
		super(context);
	}

	public DontPressWhenPressParentCheckBox(Context context, AttributeSet attrs)
	{
		super(context, attrs);
	}

	public DontPressWhenPressParentCheckBox(Context context, AttributeSet attrs, int defStyle)
	{
		super(context, attrs, defStyle);
	}

	@Override
	public void setPressed(boolean pressed)
	{
		if (pressed && getParent() instanceof View && ((View) getParent()).isPressed())
		{
			return;
		}
		super.setPressed(pressed);
	}
}

Ahora se utiliza este «nuevo» widget como reemplazo del checkbox de Android


    <com.danielme.blog.demo.listviewcheckbox.DontPressWhenPressParentCheckBox
        android:id="@+id/checkBox"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentLeft="true"
        android:layout_centerVertical="true"/>

Volvemos a nuestra app y comprobamos que ahora no se selecciona el checkbox al pulsar la fila.

listview checkbox 2

Gestión del checkbox

Ahora que ya tenemos la interfaz gráfica plenamente operativa, es hora de volver al Adapter e implementar la gestión del checkbox. Lo que haremos será capturar la pulsación en el checkbox mediante el evento View.OnClickListener y actualizar en el listado el valor del atributo checked de la clase Row. Para saber el elemento de la lista al que corresponde el checkbox que se ha pulsado se guardará en el tag del widget Checkbox la posición. El evento puede ser implementado por la propia clase del Adapter ya que se puede utilizar la misma instancia del listener para todos los checkbox.

El adapter quedará tal que así:

package com.danielme.blog.demo.listviewcheckbox;

import java.util.List;

import com.danielme.blog.demo.listviewcheckbox.R;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Custom adapter - "View Holder Pattern".
 * 
 * @author danielme.com
 * 
 */
public class CustomArrayAdapter extends ArrayAdapter<Row> implements
		View.OnClickListener {
	
	private LayoutInflater layoutInflater;

	public CustomArrayAdapter(Context context, List<Row> objects) {
		super(context, 0, objects);
		layoutInflater = LayoutInflater.from(context);
	}

	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		// holder pattern
		Holder holder = null;
		if (convertView == null) {
			holder = new Holder();

			convertView = layoutInflater.inflate(R.layout.listview_row, parent, false);
			holder.setTextViewTitle((TextView) convertView
					.findViewById(R.id.textViewTitle));
			holder.setTextViewSubtitle((TextView) convertView
					.findViewById(R.id.textViewSubtitle));
			holder.setCheckBox((CheckBox) convertView
					.findViewById(R.id.checkBox));
			convertView.setTag(holder);
		} else {
			holder = (Holder) convertView.getTag();
		}

		final Row row = getItem(position);
		holder.getTextViewTitle().setText(row.getTitle());
		holder.getTextViewSubtitle().setText(row.getSubtitle());
		holder.getCheckBox().setTag(position);
		holder.getCheckBox().setChecked(row.isChecked());
		holder.getCheckBox().setOnClickListener(this);

		return convertView;
	}


	@Override
	public void onClick(View v) {

		CheckBox checkBox = (CheckBox) v;
		int position = (Integer) v.getTag();
		getItem(position).setChecked(checkBox.isChecked());

		String msg = this.getContext().getString(R.string.check_toast,
				position, checkBox.isChecked());
		Toast.makeText(this.getContext(), msg, Toast.LENGTH_SHORT).show();
	}

	static class Holder {
		...

	}

}

El toque final

En principio ya tenemos totalmente listo y operativo nuestro ListView, pero vamos a darle un pequeño toque para redondear el resultado final: hacer que las filas con el checkbox marcado tengan un color que indique claramente que están seleccionadas.

Para conseguir esto, se un drawable selector para el ListView distinto para las filas marcadas y las desmarcadas. Echemos un vistazo al fichero /res/drawable/listview_selector.xml que define los estilos actuales del ListView.


   <?xml version="1.0" encoding="utf-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android">
 
    <item
        android:state_pressed="true"
        android:drawable="@drawable/highlight_pressed" />
 
    <item
        android:state_focused="true"
        android:drawable="@drawable/highlight_focused" />
 
    <item
        android:drawable="@android:color/white" />
 
</selector>

Como podemos ver, por defecto el color de una fila no seleccionada o presionada es blanco. Hagamos otro selector cuyo color por defecto sea el highlight_focused y lo aplicaremos a las filas que estén seleccionadas, esto es, que tengan el checkbox marcado. El selector actual lo aplicaremos para las filas con el checkbox desmarcado.

listview_selector_checked

<?xml version="1.0" encoding="utf-8"?>
<selector
    xmlns:android="http://schemas.android.com/apk/res/android">
 
    <item
        android:state_pressed="true"
        android:drawable="@drawable/highlight_pressed" />
 
    <item
        android:drawable="@drawable/highlight_focused" />
 
</selector>

El Adapter quedará definitivamente tal que así (se cambia el drawable selector cada vez que se marque o desmarque el checkbox):

package com.danielme.blog.demo.listviewcheckbox;

import java.util.List;

import com.danielme.blog.demo.listviewcheckbox.R;

import android.content.Context;
import android.graphics.drawable.Drawable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.CheckBox;
import android.widget.TextView;
import android.widget.Toast;

/**
 * Custom adapter - "View Holder Pattern".
 * 
 * @author danielme.com
 * 
 */
public class CustomArrayAdapter extends ArrayAdapter<Row> implements
		View.OnClickListener {
	
	private LayoutInflater layoutInflater;

	public CustomArrayAdapter(Context context, List<Row> objects) {
		super(context, 0, objects);
		layoutInflater = LayoutInflater.from(context);
	}

	@Override
	public View getView(final int position, View convertView, ViewGroup parent) {
		// holder pattern
		Holder holder = null;
		if (convertView == null) {
			holder = new Holder();

			convertView = layoutInflater.inflate(R.layout.listview_row, parent, false);
			holder.setTextViewTitle((TextView) convertView
					.findViewById(R.id.textViewTitle));
			holder.setTextViewSubtitle((TextView) convertView
					.findViewById(R.id.textViewSubtitle));
			holder.setCheckBox((CheckBox) convertView
					.findViewById(R.id.checkBox));
			convertView.setTag(holder);
		} else {
			holder = (Holder) convertView.getTag();
		}

		final Row row = getItem(position);
		holder.getTextViewTitle().setText(row.getTitle());
		holder.getTextViewSubtitle().setText(row.getSubtitle());
		holder.getCheckBox().setTag(position);
		holder.getCheckBox().setChecked(row.isChecked());
		holder.getCheckBox().setOnClickListener(this);

		changeBackground(getContext(), holder.getCheckBox());

		return convertView;
	}


	@Override
	public void onClick(View v) {

		CheckBox checkBox = (CheckBox) v;
		int position = (Integer) v.getTag();
		getItem(position).setChecked(checkBox.isChecked());

		changeBackground(CustomArrayAdapter.this.getContext(), checkBox);
		String msg = this.getContext().getString(R.string.check_toast,
				position, checkBox.isChecked());
		Toast.makeText(this.getContext(), msg, Toast.LENGTH_SHORT).show();
	}
	

	/**
	 * Set the background of a row based on the value of its checkbox value.
	 * Checkbox has its own style.
	 */
	@SuppressWarnings("deprecation")
	private void changeBackground(Context context, CheckBox checkBox) {
		View row = (View) checkBox.getParent();
		Drawable drawable = context.getResources().getDrawable(
				R.drawable.listview_selector_checked);
		if (checkBox.isChecked()) {
			drawable = context.getResources().getDrawable(
					R.drawable.listview_selector_checked);
		} else {
			drawable = context.getResources().getDrawable(
					R.drawable.listview_selector);
		}
		row.setBackgroundDrawable(drawable);
	}

	static class Holder {
		...

	}

}

El resultado final en Lollipop



Resumen

Los pasos que se han seguido son:

  1. Hacer el checkbox no focusable para que las filas sean seleccionables.
  2. Hacer nuestro propio checkbox para que al seleccionar la fila no se aplique al checkbox su estilo de seleccionado.
  3. En el Adapter, capturar el evento OnCLickListener para atender la pulsación del checkbox.
  4. Hacer que las filas con el checkbox marcado tengan un color distinto.

Código completo

El proyecto completo para Eclipse ADT se encuentra en Github. Para más información sobre cómo utilizar GitHub, consultar este artículo.

26 comentarios sobre “Diseño Android: ListView con checkbox

  1. Hola, soy nueva en esto y no entiendo muy bien donde estas cambiando el estado a checked. No veo por ningun sitio una llamada a setChecked a parte de las del main. ¿Se hacen automaticamente al seleccionar un item? Un saludo

    1. La gestión de la pulsación del checkbox se realiza en el Adapter en el listener OnCheckedChangeListener; los cambios se van guardando en los objetos Row que conforman la lista mostrada en pantalla.

  2. Hola, primero muchas gracias por el aporte, segundo, quisiera como tengo que hacer para tener un solo checkbox pulsado a la vez, o sea que los demas se desmarquen solos. Muchas gracias

    1. Al marcar un checkbox desactiva los otros y aplica los cambios:
      row.setChecked(isChecked);
      changeBackground(CustomArrayAdapter.this.getContext(), fila, isChecked);
      //desmarca todas los demás
      Row row = null;
      for(int i=0 ; i<getCount() ; i++)
      {
      if (i != position && isChecked)
      {
      row = (Row) getItem(i);
      row.setChecked(false);
      }
      }
      notifyDataSetChanged();

  3. Lo primero felicitarte por el blog. Siendo nuevo en esto es de mucha ayuda. En este post haces el listview siendo este el unico elemento de la activity principal. Lo que me gustaria sería incluirlo en un proyecto que tengo hecho, pero mi MainActivity extiende de ActionBarActivity. ¿como deberia incluirlo?
    Muchas gracias por adelantado!!

    1. ListView se puede utilizar de igual forma que cualquier otro widget, no es necesario heredar de ListActivity. En el ejemplo se puede hacer más o menos así:
      private ListView listView;
      @Override
      protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.listview);
      listView = (ListView) findViewById(android.R.id.list);

      listView.setAdapter(new CustomArrayAdapter(this, rows));
      listView.setOnItemClickListener(new OnItemClickListener()

      Una alternativa más avanzada es trabajar con ListFragment

  4. Hola Daniel,

    sigo luchando con el listview :). Ahora tengo me gustaría cargarlo con datos recogidos con un string-array que tengo definido en el fichero strings.xml, pero no soy capaz. Cualquier ayuda será bien recibida.
    Un salduo.

      1. Hola de nuevo Daniel,

        Muchas gracias por tu anterior respuesta, he revisado la documentación que me has pasado pero sigo sin resolver el problemilla. Te cuento con un poco más de detalle por si me pudieras ayudar.

        Estoy intentando implementar un listview personalizado con un textview y un checkbox. Mi problema es que no soy capaz de poblar el listview con un array definido en el fichero Strings.xml . El adapter que tengo ahora mismo es este:
        class RespuestaAdapter extends ArrayAdapter{

        private List respuestaList;
        private Context context;

        public RespuestaAdapter(List respuestaList, Context context){
        super(context, R.layout.single_listview_item, respuestaList);
        this.respuestaList = respuestaList;
        this.context = context;
        }

        private static class RespuestaHolder{
        public TextView textViewRespuesta;
        public CheckBox checkBoxRespuesta;
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {

        View view = convertView;

        RespuestaHolder respuestaHolder = new RespuestaHolder();

        if(convertView == null){

        LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
        view = layoutInflater.inflate(R.layout.single_listview_item, null);

        respuestaHolder.textViewRespuesta = (TextView) view.findViewById(R.id.textViewRespuesta);
        respuestaHolder.checkBoxRespuesta = (CheckBox) view.findViewById(R.id.checkBoxRespuesta);

        }else{
        respuestaHolder = (RespuestaHolder) view.getTag();
        }

        Respuesta respuesta = respuestaList.get(position);
        respuestaHolder.textViewRespuesta.setText(respuesta.getRespuesta());
        respuestaHolder.checkBoxRespuesta.setChecked(respuesta.isChecked());
        respuestaHolder.checkBoxRespuesta.setTag(respuesta);

        return view;
        }

        }

        De momento los datos los introduzco en el Main.java de esta manera:
        respuestaList.add(new Respuesta(“Alerta”));

        Y me gustaría poder hacerlo con un string-array definido en Strings.xml . Sé que la definición del array sería algo así: String [ ] miArray = getResources().getStringArray(R.array.miArray_array);

        Pero a partir de aquí no sé como modificar mi adapter para que funcione.

        Muchas gracias por adelantado y un saludo.

      2. Realmente ya lo tienes, simplemente añade a respuestaList las respuestas tal y como haces ahora pero utilizando como texto los valores del array que creo que es lo que intentas hacer. En el Adapter no hace falta cambiar nada, sigue utilizando la lista.

  5. ¿Has considerado habilitar un evento onclick a nivel de actividad y asociarlo a los checkbox para que cuando éstos sean pulsados, y con el dato de posición previamente guardado en el tag del checkbox,, donde se modifiquen los datos de estado boolean asociados? Mediante….

    Rows.getRow[(int)view.getTag()].setChecked (((CheckBox) view.findViewById((R.id.checkBox)).isChecked());

    Porque me da la sensación que el evento que asocias responde a los cambios de estado y ello hace que se ejecute sin necesidad en movimientos de la lista hacia arriba o hacia abajo, esto es, cada vez que aparecen o desaparecen filas. Cuando en realidad solo hay que actualizar los datos cuando se hace click en un checkbox.

    Saludos

    1. Efectivamente, en este caso no tiene sentido atender al cambio de estado del checkbox (de hecho supone un problema)
      y es suficiente con detectar el onclick del usuario y tener guardado en el checkbox la posición del listado.

      Gracias por la corrección, ya he actualizado artículo y proyecto.

  6. Hola, soy nuevo en android y te felicito y agradezco por el tutorial tan bien explicado.

    Por favor podrias indicarme como colocar un boton al final del listview para enviar a otro activity las filas seleccionadas por el usuario?

    Eso me seria de mucha ayuda, Gracias.

  7. Muy bueno, llevaba casi dos días con un trabajo de clase y no había forma. Con esto lo he logrado.
    Es la primera vez que me encuentro con tu página, pero me da la impresión que volveré, ya está anotada como referencia.
    ¡Gracias!

  8. Estoy intentando que al seleccionar la fila el check permute entre true o false, lo he conseguido pero no consigo que se muestre en pantalla

Deja un comentario

Este sitio utiliza Akismet para reducir el spam. Conoce cómo se procesan los datos de tus comentarios.