domingo, 2 de septiembre de 2012

ExpandableListView

En Android podemos encontrar listas simples (ListView) o bien listas que permiten expandir su contenido (ExpandableListView).
Una ListView nos permite ver información, normalmente, resumida del contenido de ese elemento el cuál podemos acceder para ver más información. En cambio, las ExpandableListView se usan para agrupar elementos que tienen algo en común. Por ejemplo, podríamos tener una aplicación que tratara sobre vehículos. Entonces podríamos estructurar la vista de manera que cada elemento que se muestre dentro de la lista sean: coches, autobuses, trenes... Al acceder al elemento coche se expandiese una nueva lista dentro de coches con las diferentes marcas a consultar (ídem para autobuses y trenes).

A continuación vamos a ver un ejemplo genérico de como crear nuestra propia ExpandableListView. Para ello vamos a crear:

/*Vista principal que contiene la ExpandableListView que vamos a mostrar*/
activity_expandable.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical" >
    <ExpandableListView
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="fill_parent" >
    </ExpandableListView>
</LinearLayout>


/*Vista "customizada" de los elementos que conforman los grupos de la lista*/
group_layout.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center" >
    <TextView
        android:id="@+id/tvGroup"
        android:layout_width="wrap_content"
        android:layout_height="40dip"
        android:paddingLeft="5dip"
        android:text="Groups"
        android:gravity="center"
        android:textColor="#000000"
        android:textSize="17sp"
        android:textStyle="bold" >
    </TextView>
</LinearLayout>


/*Vista "customizada" de los elementos que conforman los hijos de la lista*/
child_layout.xml

<?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"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/tvChild"
        android:layout_width="fill_parent"
        android:layout_height="45dip"
        android:gravity="center_vertical"
        android:paddingLeft="5dip"
        android:paddingRight="5dip"
        android:text="Children"
        android:textColor="#ffCCCC22"
        android:textSize="17sp"
        android:textStyle="bold" >
    </TextView>
</LinearLayout>


/*Clase que se encargará de gestionar la información que se mostrará en la lista y como se va a mostrar (añadiendo la información en los respectivos TextView, ImageView...)*/
ExpandableListAdapter.java

package com.example.blogexpandablelistview;
import java.util.ArrayList;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.TextView;

public class ExpandableListAdapter extends BaseExpandableListAdapter {
@Override
public boolean areAllItemsEnabled() {
return true;
}
private Context context;
private ArrayList<String> groups;
private ArrayList<ArrayList<AbstractGroupItem>> children;

public ExpandableListAdapter(Context context, ArrayList<String> groups,
ArrayList<ArrayList<AbstractGroupItem>> children) {
this.context = context;
this.groups = groups;
this.children = children;
}

//Esta función nos añade la información que queremos mostrar en el adapter
public void addItem(AbstractGroupItem groupItem) {
if (!groups.contains(groupItem.getGroup())) {
groups.add(groupItem.getGroup());
}
int index = groups.indexOf(groupItem.getGroup());
if (children.size() < index + 1) {
children.add(new ArrayList<AbstractGroupItem>());
}
children.get(index).add(groupItem);
}

@Override
public Object getChild(int groupPosition, int childPosition) {
return children.get(groupPosition).get(childPosition);
}

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

@Override
public View getChildView(int groupPosition, int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
AbstractGroupItem groupItem = (AbstractGroupItem) getChild(groupPosition, childPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.child_layout, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.tvChild);
tv.setText("   " + groupItem.getName());

tv.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0);
return convertView;
}

@Override
public int getChildrenCount(int groupPosition) {
return children.get(groupPosition).size();
}

@Override
public Object getGroup(int groupPosition) {
return groups.get(groupPosition);
}

@Override
public int getGroupCount() {
return groups.size();
}

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

@Override
public View getGroupView(int groupPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
String group = (String) getGroup(groupPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.group_layout, null);
}
TextView tv = (TextView) convertView.findViewById(R.id.tvGroup);
tv.setText(group);
return convertView;
}

@Override
public boolean hasStableIds() {
return true;
}

@Override
public boolean isChildSelectable(int arg0, int arg1) {
return true;
}
}

/*Clase que se encargará de gestionar la información para rellenar la lista y su adaptador*/
ExpandableListActivity.java
package com.example.blogexpandablelistview;

import java.util.ArrayList;

import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ExpandableListView.OnGroupClickListener;
import android.widget.Toast;

public class ExpandableListActivity extends Activity {

private ExpandableListAdapter mAdapter;
private final int TOTAL_GROUP_1 = 3;
private final int TOTAL_GROUP_2 = 4;
private final int TOTAL_GROUP_3 = 5;

@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_expandable_list);

ExpandableListView listView = (ExpandableListView) findViewById(R.id.listView);

listView.setOnChildClickListener(new OnChildClickListener() {

@Override
public boolean onChildClick(ExpandableListView arg0, View arg1,
int arg2, int arg3, long arg4) {
Toast.makeText(getBaseContext(), "Child clicked",
Toast.LENGTH_LONG).show();
return false;
}
});

listView.setOnGroupClickListener(new OnGroupClickListener() {

@Override
public boolean onGroupClick(ExpandableListView parent, View v,
int groupPosition, long id) {

Toast.makeText(getBaseContext(), "Group clicked",
Toast.LENGTH_LONG).show();
return false;
}
});

mAdapter = new ExpandableListAdapter(this, new ArrayList<String>(),
new ArrayList<ArrayList<AbstractGroupItem>>());

setupData();

listView.setAdapter(mAdapter);
}

private void setupData() {
AbstractGroupItem groupItem = null;
for (int i = 0; i < TOTAL_GROUP_1; i++) {
groupItem = new GroupItem_1("item child " + i);
mAdapter.addItem(groupItem);
}
for (int i = 0; i < TOTAL_GROUP_2; i++) {
groupItem = new GroupItem_2("item child " + i);
mAdapter.addItem(groupItem);
}
for (int i = 0; i < TOTAL_GROUP_3; i++) {
groupItem = new GroupItem_3("item child " + i);
mAdapter.addItem(groupItem);
}
}
}

A continuación, creamos las distintas clases que conforman el modelo. Como veremos tenemos una clase llamada AbstractGroupItem que nos servirá para tener compartidas las funciones de obtener los grupos y los hijos. Las clases GroupItem_(1,2,3) van a extender de ella para así poder usar esas funciones.

AbstractGroupItem.java
package com.example.blogexpandablelistview;

public class AbstractGroupItem {
private String name;
private String group;

public String getGroup() {
return group;
}

public void setGroup(String group) {
this.group = group;
}

public AbstractGroupItem(String name) {
this.name = name;
}

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

GroupItem_1.java
package com.example.blogexpandablelistview;
public class GroupItem_1 extends AbstractGroupItem {
    public GroupItem_1(String name) {
        super(name);
        setGroup(GroupItem_1.class.getSimpleName());
    }
}

GroupItem_2.java
package com.example.blogexpandablelistview;
public class GroupItem_2 extends AbstractGroupItem {
    public GroupItem_2(String name) {
        super(name);
        setGroup(GroupItem_2.class.getSimpleName());
    }
}



GroupItem_1.java
package com.example.blogexpandablelistview;
public class GroupItem_3 extends AbstractGroupItem {
    public GroupItem_3(String name) {
        super(name);
        setGroup(GroupItem_3.class.getSimpleName());
    }
}

Finalmente se adjuntan dos imagenes que nos muestran como queda nuestro proyecto:



It works!

Roger Sala,



4 comentarios:

  1. Saludos. Buen Tutorial.
    Estoy haciendo una app donde manejo un ExpandibleTextView. Todo en el diseño me queda bien, pero no se como lanzar otra activity al seleccionar un item "hijo" donde me muestre el contenido relacionado.
    Por ejemplo tengo como Listado de continentes, y dentro paises, y cuando seleccione el país me lance otra activity con la imagen de la bandera de dicho país. Saludos y espero me pudieras orientar.
    anexo correo: nekrad_me@hotmail.com

    ResponderEliminar
    Respuestas
    1. Este comentario ha sido eliminado por el autor.

      Eliminar
    2. NOMBRETULISTVIEW.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
      @Override
      public boolean onChildClick(ExpandableListView parent, View v,
      int groupPosition, int childPosition, long id) {


      }
      });

      Eliminar
  2. Este comentario ha sido eliminado por el autor.

    ResponderEliminar