如何在运行时向ListView项添加动态视图?

时间:2011-12-27 21:57:06

标签: android user-interface listview listadapter

我的问题是我不知道是否应该使用多个列表视图或可以动态增长的自定义列表视图项适配器。例如,对于特定用户,他们可以有多个活动:
- 拍照 - 说些什么 - 检查
- ......

显然,这个列表可以随着用户完成更多活动而增长。大多数情况下,我经常创建一个自定义项适配器,它从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小部件已修复,这意味着我无法使我的listview项看起来如下图所示。

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

我最初的方法是创建嵌套在适配器项中的动态视图,但是它会产生重复的视图。为了避免重复查看,我将convertView设置为null,这意味着每次加载时,它都会创建一个新的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 个答案:

答案 0 :(得分:9)

如果您有少量可能的变种(在截图中我可以看到2个不同的列表项)您有两种可能的变体:

  1. this方法设置不同类型的计数,并为每个项目提供type - 您可以使用convertView。

  2. 创建“完整”列表项视图,并设置您不希望在特定项目中看到的元素的可见性。

  3. #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;
        }
    }
    }
    

答案 1 :(得分:2)

自定义适配器可以解决您的问题。这是因为您可以更改添加到Listview中每行的视图,因为您可以通过在自定义适配器中实现的逻辑更改内容。

当getView()方法返回一个非null的视图时,这意味着该特定行有一个已存在的视图。因此,如果是这种情况,您可能希望也可能不希望更改该特定视图中的内容。或者,您可以为该特定行构建一个包含动态内容的全新视图。

需要注意的一点是,getView()的调用次数与适配器中的项目一样多。

答案 2 :(得分:1)

这个想法可能会让您在不必每次修改适配器的情况下引入任意数量的项目类型:

@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并将实例添加到适配器,不再需要向getView()添加任何if子句。