Как добавить динамическое представление к элементу ListView во время выполнения? - PullRequest
8 голосов
/ 28 декабря 2011

Моя проблема в том, что я не знаю, должен ли я использовать несколько списков или настраиваемый адаптер элементов списка, который может динамически расти.Например, для конкретного пользователя они могут выполнять несколько действий:
- Сделать снимок
- Сказать что-то
- Регистрация
- ...

Видимо, этот списокМожет расти, как пользователь сделал больше действий.В большинстве случаев я часто создаю пользовательский адаптер элементов, который расширяется от BaseAdapter, и использую шаблон ItemHolder следующим образом:

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

При использовании этого метода фиксированные графические виджеты с числом GUI означают, что яне могу сделать мой элемент списка похожим на рисунок ниже.

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

Мой первоначальный подход заключался в создании динамического представления, вложенного в элемент адаптера, однако оно будет создавать дублированные представления.Чтобы избежать дублирования просмотра, я установил convertView в ноль, что означает, что каждый раз, когда он загружается, он создает новый ItemViewHolder, который в конечном итоге пожирает всю мою память.:( Так как я могу справиться с этой ситуацией? Был бы очень признателен за минимальный рабочий пример.

Дубликат просмотра

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

Расширенное использование памяти

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

Ответы [ 3 ]

9 голосов
/ 23 января 2012

Если у вас есть небольшое количество возможных вариантов (на ваших скриншотах я вижу 2 разных элемента списка) У вас есть два возможных варианта:

  1. Установите подсчет различных типов с помощью этого метода и предоставьте type для каждого элемента - и вы можете использовать convertView.

  2. Создание «полного» представления элемента списка и настройка видимости элементов, которые вы не хотите видеть в конкретном элементе.

Код для # 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;
    }
}
}
2 голосов
/ 21 января 2012

Индивидуальный адаптер решит вашу проблему. Это потому, что вы можете изменить представления, которые добавляются к каждой строке в ListView, потому что вы можете изменить содержимое с помощью логики, которую вы реализуете в пользовательском адаптере.

Когда метод getView () возвращает представление, которое не является нулевым, это означает, что для этой конкретной строки есть представление, которое уже было там. Таким образом, в этом случае вы можете или не можете изменять контент в этом конкретном представлении. Или вы можете создать совершенно новое представление с динамическим контентом для этой конкретной строки.

Следует отметить, что getView () будет вызываться столько раз, сколько элементов найдено в вашем адаптере.

1 голос
/ 25 января 2012

Вот идея, которая, вероятно, позволит вам вводить столько типов элементов, сколько вам нужно, без необходимости каждый раз менять адаптер:

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

... а затем расширять AbstractItem и добавлять экземплярык адаптеру, нет необходимости добавлять какие-либо предложения if для getView ().

...