2011-12-27 17 views
6

Moim problemem jest to, że nie wiem, czy powinienem używać wielu widoków listy lub niestandardowego adaptera listview, który może rosnąć dynamicznie. Na przykład, dla danego użytkownika, mogą mieć wiele działań:
- Zrób zdjęcie
- powiedzmy coś
- Kontrola w
- ...Jak dodać widok dynamiczny do elementu ListView w czasie wykonywania?

Widocznie, lista ta może rośnie jak użytkownik ma Zrobiłem więcej działań. Większość czasu, często stworzyć adapter niestandardowy element, który rozciąga się od BaseAdapter i użyć deseniu ItemHolder następująco:

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

Stosując tę ​​metodę, liczba widgetów GUI jest stała, co oznacza, że ​​nie może zrobić mój ListView przedmiot wygląda jak na poniższym obrazku.

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

Moje początkowe podejście polegało na utworzeniu widoku dynamicznego zagnieżdżonego w elemencie adaptera, jednak spowoduje on duplikowanie widoków. Aby uniknąć powielonego widoku, ustawiłem convertView na wartość null, co oznacza, że ​​za każdym razem, gdy się załaduje, utworzy nowy ItemViewHolder, który ostatecznie pożera całą moją pamięć. :(Jak więc sobie radzić w tej sytuacji? Minimalny przykładem pracy byłoby bardzo mile widziane.

Duplikat Zobacz

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

Obszerne Memory Usage

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

Odpowiedz

9

Jeśli masz małą liczbę możliwych wariantów (na twoich screenach widzę 2 różne elementy listy) Masz dwa możliwe warianty: Ilość

  1. konfiguracji różnych typów przez this metody i zapewnić type dla każdy przedmiot - i możesz użyć convertView.

  2. Utwórz "pełny" widok pozycji listy i ustaw widoczność dla elementów, których nie chcesz widzieć w konkretnym elemencie.

jakiś kod na # 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

Czy możesz podać minimalny przykład pracy dla swojej metody 1? – Chan

+0

zobacz moją zmienioną odpowiedź – Jin35

+0

To jest droga. Wolę używać interfejsu 'ItemViewType', który zapewnia metody tworzenia widoku (w przypadku, gdy convertView ma wartość' null') i powiązania danych z widokiem. Wdrażające klasy deklarują swoje specyficzne 'ViewHolder' jako klasę wewnętrzną. Można rozszerzyć interfejs za pomocą metody accpets, aby pomóc w '' getItemViewType'' adaptera – Oderik

2

Niestandardowy adapter rozwiązuje problem. Dzieje się tak dlatego, że można zmieniać widoki dodawane do każdego wiersza w widoku listy, ponieważ można zmienić zawartość za pomocą logiki implementowanej w niestandardowej karcie.

Gdy metoda getView() zwraca widok, który nie jest pusty, oznacza to, że dla tego wiersza istnieje widok, który już tam był. Jeśli tak jest, możesz lub nie chcesz zmienić treści w tym konkretnym widoku. Możesz też stworzyć zupełnie nowy widok z dynamiczną treścią dla tego wiersza.

Należy pamiętać, że funkcja getView() będzie wywoływana tyle razy, ile elementów znajdzie się w adapterze.

1

Oto pomysł, że prawdopodobnie będziemy pozwalają wprowadzić wiele typów elementów, jak chcesz, bez konieczności modyfikowania adapterowi za każdym razem zrobić:

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

... a następnie rozszerzyć AbstractItem i dodać instancje do adaptera, nie trzeba dodawać żadnych klauzul, aby uzyskać getView().