2011-12-27 17 views
6

Il mio problema è che non so se utilizzare la visualizzazione elenco multipla o un adattatore per listview personalizzato che può crescere in modo dinamico. Ad esempio, per un particolare utente, possono avere molteplici attività:
- Scatta una foto
- Dire qualcosa
- Il check-in
- ...Come aggiungere una vista dinamica a un oggetto ListView in fase di runtime?

A quanto pare, questa lista può cresce come l'utente ha fatto più attività. La maggior parte del tempo, ho spesso creano una scheda di elemento personalizzato che si estende dal BaseAdapter e utilizzare il modello ItemHolder come segue:

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; 
    } 
} 

Utilizzando questo metodo, il numero widget GUI è fisso, che significa che non posso fare il mio ListView l'articolo assomiglia all'immagine qui sotto.

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

Il mio approccio iniziale era quello di creare una vista dinamica nidificato all'interno di un elemento adattatore, tuttavia esso produrrà vista duplicati. Per evitare la visualizzazione doppia, ho impostato il valore convertView su null, il che significa che ogni volta che viene caricato, viene creato un nuovo ItemViewHolder che alla fine mangia tutta la mia memoria. :(Così come potrei gestire questa situazione? Un esempio di lavoro minimo sarebbe molto apprezzato.

Duplica vista

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; 
    } 
} 

Memory Usage Ampia

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

risposta

9

Se avete piccolo numero di possibili varianti (sui vostri screenshot posso vedere 2 diversi elementi dell'elenco) Si hanno due possibili varianti: conteggio

  1. Setup di diversi tipi da this metodo, e fornire type per ogni elemento - e puoi usare convertView.

  2. Crea una vista "completa" della lista e imposta la visibilità per gli elementi che non vuoi vedere in particolare.

del codice per 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

Potresti fornire un esempio di lavoro minimo per il tuo metodo 1? – Chan

+0

vedi la mia risposta modificata – Jin35

+0

Questa è la strada da percorrere. Preferisco usare un'interfaccia 'ItemViewType' che fornisce i metodi per creare la vista (nel caso convertView è' null') e legare i dati alla vista. Le classi di implementazione dichiarano il loro specifico 'ViewHolder' come classe interna. Si potrebbe estendere l'interfaccia con un metodo di accpets per aiutarvi con 'getItemViewType' della scheda' – Oderik

2

Un adattatore personalizzato risolverebbe il tuo problema. Questo perché è possibile modificare le visualizzazioni che vengono aggiunte a ogni riga in Listview, poiché è possibile modificare il contenuto tramite la logica implementata nell'adattatore personalizzato.

Quando il metodo getView() restituisce una vista non nullo, ciò significa che per quella particolare riga è presente una vista già presente. Pertanto, se questo è il caso, è possibile o meno voler cambiare il contenuto in quella vista specifica. Oppure potresti creare una nuova vista con contenuti dinamici per quella particolare riga.

Una cosa da notare è che getView() verrà chiamato tutte le volte che ci sono elementi trovati nell'adattatore.

1

Ecco un'idea che probabilmente consentono di introdurre il maggior numero di tipi di elemento si vuole senza dover modificare l'adattatore ogni volta che fate:

@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) 
    } 
} 

... e quindi estendi AbstractItem e aggiungi istanze all'adattatore, non è più necessario aggiungere clausole if a getView().

Problemi correlati