2011-12-27 21 views
6

Mi problema es que no sé si debo usar una vista de lista múltiple o un adaptador de elemento de vista de lista personalizado que puede crecer dinámicamente. Por ejemplo, para un usuario particular, pueden tener múltiples actividades:
- Tome una fotografía
- Di algo
- Comprobación en
- ...¿Cómo agregar una vista dinámica a un elemento ListView en tiempo de ejecución?

Al parecer, esta lista puede crece a medida que el usuario tiene hecho más actividades. La mayoría de las veces, a menudo crear un adaptador de elemento personalizado que se extiende desde BaseAdapter y utilizar el patrón ItemHolder de la siguiente manera:

public class PlaceItemAdapter extends BaseAdapter { 
    private Activity  context; 
    private List<Place>  places; 
    private boolean   notifyChanged = false; 

    public PlaceItemAdapter(Activity context, List<Place> places) { 
     super(); 
     this.context = context; 
     this.places = places; 
    } 

    public int getCount() { 
     return places.size(); 
    } 

    public Object getItem(int position) { 
     return places.get(position); 
    } 

    public long getItemId(int position) { 
     return position; 
    } 

    public static class ItemViewHolder { 
     TextView nameTextView; 
     TextView typesTextView; 
     TextView ratingTextView; 
     ImageView mapIconImageView; 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 
     ItemViewHolder holder; 
     LayoutInflater inflater = context.getLayoutInflater(); 
     if (convertView == null) { 
      convertView = inflater.inflate(R.layout.place_item, null); 
      holder = new ItemViewHolder(); 
      holder.nameTextView = (TextView) convertView.findViewById(R.id.place_item_xml_textview_name); 
      holder.typesTextView = (TextView) convertView.findViewById(R.id.place_item_xml_textview_address); 
      holder.ratingTextView = (TextView) convertView.findViewById(R.id.place_item_xml_textview_rating); 
      holder.mapIconImageView = (ImageView) convertView.findViewById(R.id.place_item_xml_imageview_location_icon); 

      convertView.setTag(holder); 
     } 
     else { 
      holder = (ItemViewHolder) convertView.getTag(); 
     } 

     holder.nameTextView.setText(places.get(position).getName()); 
     holder.typesTextView.setText(places.get(position).getAddress()); 
     holder.ratingTextView.setText(Integer.toString(places.get(position).getRating())); 
     /* 
     * This task is time consuming! 
     * TODO: find a workaround to handle the image 
     */ 
     // holder.mapIconImageView.setImageBitmap(DownloadImageHelper.downloadImage(places.get(position).getIconUrl())); 
     holder.mapIconImageView.setImageResource(R.drawable.adium); 
     return convertView; 
    } 

    public void notifyDataSetChanged() { 
     super.notifyDataSetChanged(); 
     notifyChanged = true; 
    } 
} 

Usando este método, el número de widgets GUI se fija lo que significa que no puedo hacer que mi vista de lista elemento se parece a la imagen de abajo.

public static class ItemViewHolder { 
     TextView nameTextView; 
     TextView typesTextView; 
     TextView ratingTextView; 
     ImageView mapIconImageView; 
    } 

Mi planteamiento inicial fue crear una visión dinámica anidado dentro de un elemento adaptador, sin embargo se producirá vistas duplicadas. Para evitar la vista duplicada, configuré convertView como nulo, lo que significa que cada vez que se carga creará un nuevo ItemViewHolder que eventualmente consumirá toda mi memoria. :(Así que ¿cómo podría manejar esta situación? Un ejemplo de trabajo mínima sería muy apreciada.

Duplicar Ver

public class FriendFeedItemAdapter extends BaseAdapter { 
    private List<FriendFeedItem> items; 
    private Activity context; 
    private static LayoutInflater inflater; 
    public ImageLoader imageLoader; 
    private ItemViewHolder viewHolder; 

    public FriendFeedItemAdapter(Activity context, List<FriendFeedItem> items) { 
     this.context = context; 
     this.items = items; 
     inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     imageLoader = new ImageLoader(context.getApplicationContext()); 
    } 

    public int getCount() { 
     return items.size(); 
    } 

    public Object getItem(int position) { 
     return items.get(position); 
    } 

    public long getItemId(int position) { 
     return position; 
    } 

    public static class ItemViewHolder { 
     TableLayout table; 
     ImageView imageViewUserPicture; 
     TextView textViewUsername; 
     TextView textViewWhatUserDo; 
     TextView textViewWhere; 
     TextView textViewTime; 
     ImageView imageViewSnapPictureBox; 
     TextView textViewWriteOnWallMessageBox; 
    } 

    public View getView(int position, View convertView, ViewGroup parent) { 
     if (convertView == null) { 
      convertView = inflater.inflate(R.layout.friend_list_feed_item, null); 
      viewHolder = new ItemViewHolder(); 
      viewHolder.table = (TableLayout) convertView.findViewById(R.id.friend_list_feed_item_xml_tablelayout_table); 
      viewHolder.imageViewUserPicture = (ImageView) convertView.findViewById(R.id.friend_list_feed_item_xml_imageview_user_picture); 
      viewHolder.textViewUsername = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_username); 
      viewHolder.textViewWhatUserDo = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_what_user_do); 
      viewHolder.textViewWhere = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_where); 
      viewHolder.textViewTime = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_at_what_time); 

      convertView.setTag(viewHolder); 
     } 
     else { 
      viewHolder = (ItemViewHolder) convertView.getTag(); 
     } 

     imageLoader.displayImage(items.get(position).getFriendPictureUrl(), viewHolder.imageViewUserPicture); 
     viewHolder.textViewUsername.setText(items.get(position).getFriendName()); 
     viewHolder.textViewWhere.setText("at " + items.get(position).getPlaceName()); 
     viewHolder.textViewTime.setText("@" + items.get(position).getActivityTime()); 

     if (items.get(position).getChallengeType() == Challenge.Type.CHECK_IN) { 
      viewHolder.textViewWhatUserDo.setText("has checked in."); 
     } 
     else if (items.get(position).getChallengeType() == Challenge.Type.SNAP_PICTURE) { 
      viewHolder.textViewWhatUserDo.setText("has snap a picture."); 
      // add picture box 
      View rowView = inflater.inflate(R.layout.snap_picture_row_item, null); 
      viewHolder.imageViewSnapPictureBox = (ImageView) rowView.findViewById(R.id.snap_picture_row_item_xml_imageview_picture); 
      imageLoader.displayImage(items.get(position).getActivitySnapPictureUrl(), viewHolder.imageViewSnapPictureBox); 
      viewHolder.table.addView(rowView); 
     } 
     else if (items.get(position).getChallengeType() == Challenge.Type.WRITE_ON_WALL) { 
      viewHolder.textViewWhatUserDo.setText("has written a message on wall."); 
      // add message box 
      View rowView = inflater.inflate(R.layout.write_on_wall_row_item, null); 
      viewHolder.textViewWriteOnWallMessageBox = (TextView) rowView.findViewById(R.id.write_on_wall_row_item_xml_textview_wall_message); 
      viewHolder.textViewWriteOnWallMessageBox.setText(items.get(position).getActivityComment()); 
      viewHolder.table.addView(rowView); 
     } 
     else if (items.get(position).getChallengeType() == Challenge.Type.QUESTION_ANSWER) { 
      viewHolder.textViewWhatUserDo.setText("has answered a question."); 
     } 
     else { // Challenge.Type.OTHER 
      viewHolder.textViewWhatUserDo.setText("has done some other challenges."); 
     } 

     return convertView; 
    } 
} 

extenso uso de la memoria

public View getView(int position, View convertView, ViewGroup parent) { 
     ItemViewHolder holder = null; 
     LayoutInflater inflater = context.getLayoutInflater(); 

     convertView = inflater.inflate(R.layout.friend_list_feed_item, null); 
     // create holder 
     holder = new ItemViewHolder(); 
     // default field 
     holder.table = (TableLayout) convertView.findViewById(R.id.friend_list_feed_item_xml_tablelayout_table); 
     holder.imageViewUserPicture = (ImageView) convertView.findViewById(R.id.friend_list_feed_item_xml_imageview_user_picture); 
     holder.textViewUsername = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_username); 
     holder.textViewWhatUserDo = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_what_user_do); 
     holder.textViewWhere = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_where); 
     holder.textViewTime = (TextView) convertView.findViewById(R.id.friend_list_feed_item_xml_textview_at_what_time); 
     convertView.setTag(holder); 

     holder.imageViewUserPicture.setImageURI(items.get(position).getFriendPictureUri()); 
     holder.textViewUsername.setText(items.get(position).getFriendName()); 
     holder.textViewWhere.setText("at " + items.get(position).getPlaceName()); 
     holder.textViewTime.setText("@" + items.get(position).getActivityTime()); 

     if (items.get(position).getChallengeType() == Challenge.Type.CHECK_IN) { 
      holder.textViewWhatUserDo.setText("has checked in."); 
     } 
     else if (items.get(position).getChallengeType() == Challenge.Type.SNAP_PICTURE) { 
      holder.textViewWhatUserDo.setText("has snap a picture."); 
      // add picture box 
      View rowView = inflater.inflate(R.layout.snap_picture_row_item, null); 
      holder.imageViewSnapPictureBox = (ImageView) rowView.findViewById(R.id.snap_picture_row_item_xml_imageview_picture); 
      holder.imageViewSnapPictureBox.setImageURI(items.get(position).getActivitySnapPictureUri()); 
      holder.table.addView(rowView); 
     } 
     else if (items.get(position).getChallengeType() == Challenge.Type.WRITE_ON_WALL) { 
      holder.textViewWhatUserDo.setText("has written a message on wall."); 
      // add message box 
      View rowView = inflater.inflate(R.layout.write_on_wall_row_item, null); 
      holder.textViewWriteOnWallMessageBox = (TextView) rowView.findViewById(R.id.write_on_wall_row_item_xml_textview_wall_message); 
      holder.textViewWriteOnWallMessageBox.setText(items.get(position).getActivityComment()); 
      holder.table.addView(rowView); 
     } 
     else if (items.get(position).getChallengeType() == Challenge.Type.QUESTION_ANSWER) { 
      holder.textViewWhatUserDo.setText("has answered a question."); 
     } 
     else { // Challenge.Type.OTHER 
      holder.textViewWhatUserDo.setText("has done some other challenges."); 
     } 

     return convertView; 
    } 

enter image description here

enter image description here

Respuesta

9

Si tiene pequeño número de posibles variantes (en sus capturas de pantalla puedo ver 2 elementos de la lista diferentes) Usted tiene dos variantes posibles: recuento

  1. de configuración de los diferentes tipos de this método, y proporcionar type de cada elemento, y puede usar convertView.

  2. Cree una vista de elemento de lista "completa" y establezca la visibilidad de los elementos que no desea ver en un elemento determinado.

algo de código para # 2:

public class ListTestActivity extends Activity { 
@Override 
public void onCreate(Bundle savedInstanceState) { 
    super.onCreate(savedInstanceState); 
    setContentView(R.layout.main); 

    List<Element> list = new ArrayList<Element>(); 
    list.add(new Element(0)); 
    list.add(new Element(0)); 
    list.add(new Element(1)); 
    list.add(new Element(0)); 
    list.add(new Element(1)); 
    list.add(new Element(1)); 
    list.add(new Element(0)); 
    list.add(new Element(0)); 
    list.add(new Element(1)); 
    list.add(new Element(1)); 
    list.add(new Element(1)); 
    list.add(new Element(0)); 
    list.add(new Element(0)); 
    list.add(new Element(1)); 
    list.add(new Element(0)); 
    list.add(new Element(0)); 
    ((ListView) findViewById(android.R.id.list)).setAdapter(new SampleAdapter(this, list)); 
} 

private class SampleAdapter extends BaseAdapter { 

    private List<Element> list; 
    private Context context; 

    public SampleAdapter(Context context, List<Element> list) { 
     this.list = list; 
     this.context = context; 
    } 

    @Override 
    public int getCount() { 
     return list.size(); 
    } 

    @Override 
    public Element getItem(int position) { 
     return list.get(position); 
    } 

    @Override 
    public View getView(int position, View convertView, ViewGroup parent) { 
     if (convertView == null) 
      switch (getItemViewType(position)) { 
      case 0: 
       convertView = new CheckBox(context); 
       break; 
      default: 
       convertView = new Button(context); 
       break; 
      } 
     // Output here shows that you can lay on getItemViewType(position) as indicator of convertView type or structure 
     Log.e("test", getItemViewType(position) + ": " + convertView.getClass().getSimpleName()); 
     return convertView; 
    } 

    @Override 
    public int getItemViewType(int position) { 
     return getItem(position).type; 
    } 

    @Override 
    public int getViewTypeCount() { 
     return 2; 
    } 

    @Override 
    public long getItemId(int position) { 
     return position; 
    } 
} 

private class Element { 
    public int type; 

    public Element(int type) { 
     this.type = type; 
    } 
} 
} 
+0

¿Podría dar un ejemplo de trabajo mínimo para su método 1? – Chan

+0

ver mi respuesta editada – Jin35

+0

Ese es el camino a seguir. Prefiero usar una interfaz 'ItemViewType' que proporciona los métodos para crear la vista (en caso de que convertView sea' nulo') y datos vinculantes para la vista. Las clases de implementación declaran su 'ViewHolder' específico como clase interna. Puede extender la interfaz con un método accpets para ayudarlo con el 'getItemViewType' de su adaptador – Oderik

2

Un adaptador personalizado resolvería su problema. Esto se debe a que puede cambiar las vistas que se agregan a cada fila en la vista de lista, ya que puede cambiar el contenido mediante la lógica que implementa en el adaptador personalizado.

Cuando el método getView() devuelve una vista que no es nula, esto significa que para esa fila en particular hay una vista que ya estaba allí. Como tal, si este es el caso, puede o no querer cambiar el contenido en esa vista específica. O podría construir una vista nueva con contenido dinámico para esa fila en particular.

Una cosa a tener en cuenta es que se llamará a getView() tantas veces como elementos se encuentren en su adaptador.

1

Aquí es una idea que probablemente le va a permitir introducir tantos tipos de elementos como desee sin tener que modificar el adaptador cada vez que haces:

@Override 
public View getView(int position, View convertView, ViewGroup parent) { 
    AbstractItem abstractItem = ((AbstractItem)getItem(position)); 
    // if null or new item type is different than the one already there 
    if (convertView == null || (convertView.getTag() != null 
      && ((AbstractItem)convertView.getTag()).getType().equals(abstractItem.getType())) { 
      convertView = abstractItem.inflateSelf(getContext()); 
    } 

    abstractItem.fillViewWithData(convertView); 
    convertView.setTag(abstractItem); 
    return convertView; 
} 

public class AbstractItem { 
    public abstract View inflateSelf(Context context); 
    public abstract String getType(); 
    public abstract void fillViewWithData(View view); 
} 

public class PictureSnapItem extends AbstractItem { 
    // data fields 
    WeakReference<Bitmap> wBitmap; 
    String pictureComment; 
    ... 

    public abstract View inflateSelf(Context context) { 
     // get layout inflater, inflate a layout resource, return 
     return ((Activity)context).getLayoutInflater.inflate(R.layout.picture_snap_item); 
    } 
    public abstract String getType() { 
     // return something class-specific, like this 
     return getClass().getCanonicalName(); 
    } 
    public abstract void fillViewWithData(View view) { 
     // fill the view with data from fields, assuming view has been 
     // inflated by this very class's inflateSelf(), maybe throw exception if 
     // required views can't be found 
     ImageView img = (ImageView) view.findViewById(R.id.picture); 
     TextView comment = (TextView) view.findViewById(R.id.picture_comment) 
    } 
} 

... y luego extienda AbstractItem y agregue instancias al adaptador, sin necesidad de agregar ninguna cláusula if para getView().

Cuestiones relacionadas