sexta-feira, 30 de novembro de 2012

Android - Expandable ListView com CheckBox

Estava eu dando uma brincada com Android implementando uma Expandable ListView com CheckBox,
quando me deparei com uma situação chata: Ao marcar alguns checkboxs, e usar o scroll da
app, percebi que alguns itens que não tinha marcado, misteriosamente apareciam marcados, e os
itens que tinha marcado antes dos scrooling, já não esatavam mais marcados.

Antes e depois de utilizar o scroll
Pois bem, após algumas pesquisas vi que esse problema estava acontecendo com varias pessoas, porém
não achei nenhuma solução funcionando, e resolvi disponibilizar por aqui.

Primeiramente, os check box estavam mudando de estado por causa do ListView Recycle no meu Adapter,
que resolvi armazenando o estado em uma coleção no onClick... e no momento antes do retorno do metodo getChildView(...)
eu veficido se um determinado identificador existe.


expandable.xml
 


    


list_item_group.xml
 


    

list_item_child.xml
 
 

 
 
    

    
    
  
    
 

ChildEntity.java
 package com.blogspot.receitastecnologicas;

import java.math.BigDecimal;
import java.text.ParseException;

public class ChildEntity {
 private Integer id;

 private String descricao;

 private BigDecimal valor;

 public ChildEntity() {
  // TODO Auto-generated constructor stub
 }

 public ChildEntity(int id, String descr, BigDecimal valor) {
  this.id = id;
  this.descricao = descr;
  this.valor = valor;
 }

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getDescricao() {
  return descricao;
 }

 public void setDescricao(String descricao) {
  this.descricao = descricao;
 }

 public BigDecimal getValor() {
  return valor;
 }

 public void setValor(BigDecimal valor) {
  this.valor = valor;
 }

 public String getValorFormatado() throws ParseException {
  // TODO Auto-generated method stub
  return this.getValor().toString();
 }
}

GrupoEntity.java
 package com.blogspot.receitastecnologicas;

import java.util.List;

public class GrupoEntity {
 private Integer id;
 private String descricao;
 private String tipo;
 private List listChild;

 public GrupoEntity() {
 }

 public GrupoEntity(Integer id, String descricao, String tipo) {
  this.id = id;
  this.descricao = descricao;
  this.tipo = tipo;
 }

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getDescricao() {
  return descricao;
 }

 public void setDescricao(String descricao) {
  this.descricao = descricao;
 }

 public String getTipo() {
  return tipo;
 }

 public void setTipo(String tipo) {
  this.tipo = tipo;
 }

 public List getListChild() {
  return listChild;
 }

 public void setListChild(List listChild) {
  this.listChild = listChild;
 }

 @Override
 public String toString() {
  return this.getId() + " - " + this.getDescricao();
 }
}

MainActivity.java
 package com.blogspot.receitastecnologicas;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;

import android.app.Activity;
import android.os.Bundle;
import android.widget.ExpandableListView;

import com.blogspot.receitastecnologicas.R;

public class MainActivity extends Activity {
 ExpandableListView explistView;

 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.expandable);
  explistView = (ExpandableListView) findViewById(R.id.list_expandable);

  List listgrupo = new ArrayList();
  GrupoEntity grupo = new GrupoEntity(1, "Street Fighter", "D");
  List child = new ArrayList();
  child.add(new ChildEntity(1, "Ryu", new BigDecimal(10)));
  child.add(new ChildEntity(2, "Blanka", new BigDecimal(20)));
  child.add(new ChildEntity(3, "Vega", new BigDecimal(30)));
  grupo.setListChild(child);
  listgrupo.add(grupo);

  GrupoEntity grupo1 = new GrupoEntity(2, "Mortal Kombat", "D");
  List child1 = new ArrayList();
  child1.add(new ChildEntity(4, "Sub Zero", new BigDecimal(10)));
  child1.add(new ChildEntity(5, "Scorpion", new BigDecimal(20)));
  child1.add(new ChildEntity(6, "Rayden", new BigDecimal(30)));
  grupo1.setListChild(child1);
  listgrupo.add(grupo1);

  GrupoEntity grupo2 = new GrupoEntity(3, "Oh my Dog!", "D");
  List child2 = new ArrayList();
  child2.add(new ChildEntity(7, "Allejo", new BigDecimal(10)));
  child2.add(new ChildEntity(8, "Chuck Norris", new BigDecimal(20)));
  child2.add(new ChildEntity(9, "Jeremias", new BigDecimal(30)));
  grupo2.setListChild(child2);
  listgrupo.add(grupo2);
  // sets the adapter that provides data to the list.
  explistView.setAdapter(new ExpandableAdapter(MainActivity.this,
    listgrupo));
 }

} 

ExpandableAdapter.java
 package com.blogspot.receitastecnologicas;

import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.CheckBox;
import android.widget.TextView;

import com.blogspot.receitastecnologicas.R;

public class ExpandableAdapter extends BaseExpandableListAdapter {
 private Context ctx;
 private List lista;
 private Map childSelected = new HashMap();

 public ExpandableAdapter(Context ctx, List lista) {
  super();
  this.ctx = ctx;
  this.lista = lista;
 }

 // interface

 public Object getChild(int groupPosition, int childPosition) {
  return lista.get(groupPosition).getListChild().get(childPosition);
 }

 public long getChildId(int groupPosition, int childPosition) {
  return childPosition;
 }

 public View getChildView(final int groupPosition, final int childPosition,
   boolean isLastChild, View convertView, ViewGroup parent) {
  final ViewChildHolder holder;
  final ChildEntity child = lista.get(groupPosition).getListChild()
    .get(childPosition);
  if (convertView == null) {
   convertView = LayoutInflater.from(ctx).inflate(
     R.layout.list_item_child, null);// carregando layout
   holder = new ViewChildHolder();
   holder.id = (TextView) convertView.findViewById(R.id.txtid);
   holder.txtValor = (TextView) convertView
     .findViewById(R.id.txtValorTitulo);

   holder.check = (CheckBox) convertView.findViewById(R.id.chkItem);

   holder.check.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
     CheckBox chk = (CheckBox) v;
     Log.d("Check", "Selecionado " + holder.check.getText()
       + " Holder:" + holder);

     if (chk.isChecked()) {
      if (!childSelected.containsKey(holder.id.getText()))
       childSelected.put(holder.id.getText().toString(),
         true);
     } else {
      if (childSelected.containsKey(holder.id.getText()))
       childSelected.remove(holder.id.getText());
     }
     holder.check.setChecked(chk.isChecked());

    }

   });
   convertView.setTag(holder);

  } else {
   holder = (ViewChildHolder) convertView.getTag();
  }
  holder.check.setText(child.getDescricao());
  holder.txtValor.setText(child.getValor().toString());
  holder.id.setText(child.getId().toString());
  holder.check.setChecked(childSelected.containsKey(holder.id.getText()));

  return convertView;
 }

 static class ViewChildHolder {
  TextView id;
  TextView txtValor;
  CheckBox check;
 }

 public int getChildrenCount(int groupPosition) {
  return lista.get(groupPosition).getListChild().size();
 }

 public Object getGroup(int groupPosition) {
  return lista.get(groupPosition);
 }

 public int getGroupCount() {
  return lista.size();
 }

 public long getGroupId(int groupPosition) {
  return groupPosition;
 }

 public View getGroupView(int groupPosition, boolean isExpanded,
   View convertView, ViewGroup parent) {
  ViewGroupHolder holder;
  GrupoEntity grupo = lista.get(groupPosition);
  if (convertView == null) {
   convertView = LayoutInflater.from(ctx).inflate(
     R.layout.list_item_group, null);// carregando layout
   holder = new ViewGroupHolder();

   holder.txtDescricao = (TextView) convertView
     .findViewById(R.id.txt_item_group);
   convertView.setTag(holder);
  } else {
   holder = (ViewGroupHolder) convertView.getTag();
  }

  holder.txtDescricao.setText(grupo.getDescricao());

  return convertView;
 }

 static class ViewGroupHolder {
  TextView txtDescricao;
 }

 public boolean hasStableIds() {
  return true;
 }

 public boolean isChildSelectable(int groupPosition, int childPosition) {
  return true;
 }

}

http://developer.android.com/reference/android/widget/ExpandableListView.html