2011-03-04 9 views
13

Estoy buscando escribir mi propio ExpandableListAdapter que funciona de manera similar a ArrayAdapter. Mi modelo de datos es el siguiente:Cómo escribir personalizado ExpandableListAdapter

public class Group { 

    private String name; 

    private List<Child> children; 
} 

public class Child { 

    private String name; 
} 

Pretty simple. ¿Cómo puedo mapear esta relación en una implementación de ExpandableListAdapter? Tengo un trabajo SimpleExpandableListAdapter trabajando ahora mismo, pero necesito más control personalizado sobre los ítems (mostrando íconos, etc.). ¿Qué debería hacer para esto?

Lo principal es que necesito un método add() para poder agregar grupos e invalidar la lista cuando se agregan y eliminan elementos secundarios del adaptador. De hecho, me sorprende que no haya una implementación en el SDK (incluso una abstracta) que ayude a uno a lograr esto.

Respuesta

26

Aquí hay una implementación que acabo de impulsar. No tengo idea si funciona o no, pero me parece "inteligente" :) Por cierto, ¿cómo se debe tratar de obtener la identificación combinada de niño o la identificación de grupo combinado, así que estoy algo improvisado allí?

package example; 

import java.util.ArrayList; 
import java.util.List; 
import java.util.Map.Entry; 

import android.content.Context; 
import android.database.DataSetObservable; 
import android.database.DataSetObserver; 
import android.view.LayoutInflater; 
import android.view.View; 
import android.view.ViewGroup; 
import android.widget.ExpandableListAdapter; 

public abstract class AbstractExpandableListAdapter<A, B> implements ExpandableListAdapter { 

    private final List<Entry<A, List<B>>> objects; 

    private final DataSetObservable dataSetObservable = new DataSetObservable(); 

    private final Context context; 

    private final Integer groupClosedView; 

    private final Integer groupExpandedView; 

    private final Integer childView; 

    private final LayoutInflater inflater; 

    public AbstractExpandableListAdapter(Context context, int groupClosedView, 
      int groupExpandedView, int childView, List<Entry<A, List<B>>> objects) { 
     this.context = context; 
     this.objects = objects; 
     this.groupClosedView = new Integer(groupClosedView); 
     this.groupExpandedView = new Integer(groupExpandedView); 
     this.childView = new Integer(childView); 

     this.inflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
    } 

    public void add(Entry<A, List<B>> group) { 
     this.getObjects().add(group); 
     this.notifyDataSetChanged(); 
    } 

    public void remove(A group) { 
     for (Entry<A, List<B>> entry : this.getObjects()) { 
      if (entry != null && entry.getKey().equals(group)) { 
       this.getObjects().remove(group); 
       this.notifyDataSetChanged(); 
       break; 
      } 
     } 
    } 

    public void remove(Entry<A, List<B>> entry) { 
     remove(entry.getKey()); 
    } 

    public void addChild(A group, B child) { 
     for (Entry<A, List<B>> entry : this.getObjects()) { 
      if (entry != null && entry.getKey().equals(group)) { 
       if (entry.getValue() == null) 
        entry.setValue(new ArrayList<B>()); 

       entry.getValue().add(child); 
       this.notifyDataSetChanged(); 
       break; 
      } 
     } 
    } 

    public void removeChild(A group, B child) { 
     for (Entry<A, List<B>> entry : this.getObjects()) { 
      if (entry != null && entry.getKey().equals(group)) { 
       if (entry.getValue() == null) 
        return; 

       entry.getValue().remove(child); 
       this.notifyDataSetChanged(); 
       break; 
      } 
     } 
    } 

    public void notifyDataSetChanged() { 
     this.getDataSetObservable().notifyChanged(); 
    } 

    public void notifyDataSetInvalidated() { 
     this.getDataSetObservable().notifyInvalidated(); 
    } 

    public void registerDataSetObserver(DataSetObserver observer) { 
     this.getDataSetObservable().registerObserver(observer); 
    } 

    public void unregisterDataSetObserver(DataSetObserver observer) { 
     this.getDataSetObservable().unregisterObserver(observer); 
    } 

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

    public int getChildrenCount(int groupPosition) { 
     return getObjects().get(groupPosition).getValue().size(); 
    } 

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

    public Object getChild(int groupPosition, int childPosition) { 
     return getObjects().get(groupPosition).getValue().get(childPosition); 
    } 

    public long getGroupId(int groupPosition) { 
     return ((Integer)groupPosition).longValue(); 
    } 

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

    public boolean hasStableIds() { 
     return true; 
    } 

    public View getGroupView(int groupPosition, boolean isExpanded, 
      View convertView, ViewGroup parent) { 

     if (convertView != null && convertView.getId() != 
       (isExpanded ? getGroupExpandedView() : getGroupClosedView())) { 
//   do nothing, we're good to go, nothing has changed. 
     } else { 
//   something has changed, update. 
      convertView = inflater.inflate(isExpanded ? getGroupExpandedView() : 
        getGroupClosedView(), parent, false); 
      convertView.setTag(getObjects().get(groupPosition)); 
     } 

     return convertView; 
    } 

    public View getChildView(int groupPosition, int childPosition, 
      boolean isLastChild, View convertView, ViewGroup parent) { 

     if (convertView != null) { 
//   do nothing 
     } else { 
//   create 
      convertView = inflater.inflate(getChildView(), parent, false); 
      convertView.setTag(getObjects().get(groupPosition).getValue().get(childPosition)); 
     } 

     return convertView; 
    } 

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

    public boolean areAllItemsEnabled() { 
     return true; 
    } 

    public boolean isEmpty() { 
     return getObjects().size() == 0; 
    } 

    public void onGroupExpanded(int groupPosition) { 

    } 

    public void onGroupCollapsed(int groupPosition) { 

    } 

    public long getCombinedChildId(long groupId, long childId) { 
     return groupId * 10000L + childId; 
    } 

    public long getCombinedGroupId(long groupId) { 
     return groupId * 10000L; 
    } 

    protected DataSetObservable getDataSetObservable() { 
     return dataSetObservable; 
    } 

    protected List<Entry<A, List<B>>> getObjects() { 
     return objects; 
    } 

    protected Context getContext() { 
     return context; 
    } 

    protected Integer getGroupClosedView() { 
     return groupClosedView; 
    } 

    protected Integer getGroupExpandedView() { 
     return groupExpandedView; 
    } 

    protected Integer getChildView() { 
     return childView; 
    } 
} 

Cualquier comentario o crítica son bienvenidos.

+0

Por cierto, acabo de probar esto y parece que funciona muy bien. :) –

+0

¡Simplemente excepcional, gracias! – myforums

+0

Bastante impresionante para batirlo en el acto. –

4

Estaba bastante sorprendido de que no haya encontrado una mejor documentación sobre esto tampoco. Si encuentra uno, publíquelo aquí. El mejor ejemplo de implementación que encontré fue en ApiDemos. Hay un ExpandableListActivity que implementa un BaseExpandableListAdapter. La clase se llama ExpandableList1.java.

Deberá crear su propio método add() que agregue sus clases Group y Child al adaptador. No creo que sea tan difícil a primera vista. De hecho, es posible que solo pueda crear las referencias a los objetos de la clase. Cuando implementé el mío, mi conjunto de datos era pequeño y no cambiaba, así que solo necesitaba consultar mi archivo array.xml.

1
public class CustomExpandableAdapter extends BaseExpandableListAdapter { 
     private Context mContext; 
     private List<Group> mData; 
     private int mSelectedPosition = -1; 

     public CustomExpandableAdapter(Context context, List<Group> data) { 
      mData = data; 
      mContext = context; 

     } 

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

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

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

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

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

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

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

     @Override 
     public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { 
      HeaderViewHolder headerViewHolder = null; 
      if (convertView == null) { 
       convertView = LayoutInflater.from(mContext).inflate(R.layout.faq_header_text_layout, null); 
       headerViewHolder = new HeaderViewHolder(convertView); 
       convertView.setTag(headerViewHolder); 
      } 
      headerViewHolder = (HeaderViewHolder) convertView.getTag(); 

      headerViewHolder.mGroupHeader.setText(mData.get(groupPosition).name); 
      return convertView; 
     } 

     @Override 
     public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { 
      ChildViewHolder childViewHolder = null; 
      if (convertView == null) { 
       convertView = LayoutInflater.from(mContext).inflate(R.layout.faq_textview_layout, null); 
       childViewHolder = new ChildViewHolder(convertView); 
       convertView.setTag(childViewHolder); 
      } 
      childViewHolder = (ChildViewHolder) convertView.getTag(); 

         childViewHolder.mChildTitle.setText(mData.get(groupPosition).children.get(childPosition)); 
      return convertView; 
     } 

     @Override 
     public boolean isChildSelectable(int groupPosition, int childPosition) { 
      return false; 
     } 

     private static class HeaderViewHolder { 
      final TextView mGroupHeader; 

      private HeaderViewHolder(View group) { 
       mGroupHeader = (TextView) group.findViewById(R.id.txv_faq_header_text_layout); 
      } 
     } 

     private static class ChildViewHolder { 
      final TextView mChildTitle; 

      private ChildViewHolder(View group) { 
       mChildTitle = (TextView) group.findViewById(R.id.txv_faq_textview_layout); 
      } 
     } 

     @Override 
     public void unregisterDataSetObserver(DataSetObserver observer) { 
      if (observer != null) { 
       super.unregisterDataSetObserver(observer); 
      } 
     } 

     public void setSelectedPosition(int selectedPosition) { 
      mSelectedPosition = selectedPosition; 
     } 
    } 
1

Al ver la edad que este post y respuestas son, pensé que me gustaría señalar que hay un muy buen 3rd party library que llena sorta en esta brecha que falta. Si bien las soluciones personalizadas publicadas son buenas, todavía faltan algunas cosas y siguen el engorroso diseño de requerir que el programador genere una estructura de datos de estructuras de datos. A veces, solo desea organizar una lista en pequeños grupos agradables sin la molestia de hacerlo usted mismo.

Se llama el RolodexArrayAdapter y pueden ser fácilmente utilizados en la creación personalizada ExpandableListAdapters ... sin tener que preocuparse por todos los problemas de gestión de datos y funciones. Es compatible con métodos como agregar, agregar todo, eliminar, eliminar todo, mantener todo, contiene, clasificar, etc. También es compatible con funciones más avanzadas como ChoiceMode, Filtering y grupos de expansión automática.

Ejemplo:

class MovieAdapter extends RolodexArrayAdapter<Integer, MovieItem> { 
    public MovieAdapter(Context activity, List<MovieItem> movies) { 
     super(activity, movies); 
    } 

    @Override 
    public Integer createGroupFor(MovieItem childItem) { 
     //Lets organize our movies by their release year 
     return childItem.year; 
    } 

    @Override 
    public View getChildView(LayoutInflater inflater, int groupPosition, int childPosition, 
          boolean isLastChild, View convertView, ViewGroup parent) { 
     if (convertView == null) { 
      //Inflate your view 
     } 
     //Fill view with data 
     return convertView; 
    } 

    @Override 
    public View getGroupView(LayoutInflater inflater, int groupPosition, boolean isExpanded, 
          View convertView, ViewGroup parent) { 
     if (convertView == null) { 
      //Inflate your view 
     } 
     //Fill view with data 
     return convertView; 
    } 

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

    @Override 
    protected boolean isChildFilteredOut(MovieItem movie, CharSequence constraint) { 
     //Lets filter by movie title 
     return !movie.title.toLowerCase(Locale.US).contains(
       constraint.toString().toLowerCase(Locale.US)); 
    } 

    @Override 
    protected boolean isGroupFilteredOut(Integer year, CharSequence constraint) { 
     //Lets filter out everything whose year does not match the numeric values in the constraint. 
     return TextUtils.isDigitsOnly(constraint) && !year.toString().contains(constraint); 
    } 
} 
Cuestiones relacionadas