13

我想知道是否有办法将项目添加到 ListView 而不会导致重新加载整个列表。

我有一个 BaseAdapter 派生的 ListView 适配器,当底层模型添加了一个新元素时,它会调用 notifyDataSetChanged(),这会触发 ListView 重新加载。

列表中的元素具有图像,这些图像根据元素的内容动态加载。问题是,在重新加载过程中调用 getView() 时,传递用于重用的 convertView 参数来自之前不同的位置,因此图像也必须重新加载,这会导致相当难看的闪烁。

那么,如果我只在最后添加一个项目(这将是添加新项目的唯一方法),有没有办法不重新加载整个列表?或者,如果可能的话,至少以某种方式重复使用相同位置的单元格,以避免代价高昂的图像重新加载?

4

4 回答 4

5

没有朋友,在 android 中你不可能在不刷新列表的情况下添加一个项目,因为当你添加一个项目时,它会改变列表视图的高度。参见下面的链接,Romain Guy 在里面说了同样的话。

http://groups.google.com/group/android-developers/browse_thread/thread/7e54522c37772d04/05abfd17b59b07f7?lnk=gst&q=how+to+add+list+item+in+listview+without+reload+the+listview+# 05abfd17b59b07f7

于 2011-11-23T05:08:18.150 回答
1

这样做而不是调用 notifyDataSetChanged():

int firstVisiblePosition = getFirstVisiblePosition();
View view = getChildAt(0);
int distFromTop = (view == null) ? 0 : view.getTop();
setSelectionFromTop(firstVisiblePosition, distFromTop);
于 2012-05-25T11:43:15.653 回答
1

嗯,这是可能的。

覆盖 ArrayAdapter 的 getView 和 add 方法

例如,我做了什么作为测试用例:

public class custom_row extends ArrayAdapter<String> {

String newItem = "";
Boolean doRefresh = true;

public custom_row(Context context, int resource, ArrayList<String> objects) {
    super(context, resource, objects);
}

public custom_row(Context context, int resource, String[] objects) {
    super(context, resource, objects);
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    String itemValue = getItem(position);

    if (doRefresh == false && itemValue != newItem) {
        return convertView;
    }

    LayoutInflater inflater = LayoutInflater.from(getContext());

    View customView = inflater.inflate(R.layout.customr_row, parent, false);
    ImageView myImageView = (ImageView) customView.findViewById(R.id.imageView);

    String url = "https://urltoimage/image.jpg";

    (new DownloadImageTask(myImageView)).execute(url);

    TextView myTextView = (TextView) customView.findViewById(R.id.myCustomText);
    myTextView.setText(itemValue);

    return customView;
}

public void addNewItemToList(String item) {
    this.newItem = item;
    this.doRefresh = false;
    add(item);
}

private class DownloadImageTask extends AsyncTask<String, Integer, Bitmap>{
    private ImageView mImageView;

    public DownloadImageTask(ImageView imageView) {
        this.mImageView = imageView;
    }

    @Override
    protected Bitmap doInBackground(String... params) {
        URL url = null;
        Bitmap bmp = null;

        try {
            url = new URL(params[0]);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }

        try {
            InputStream stream = url.openStream();
            bmp = BitmapFactory.decodeStream(stream);

        } catch (IOException e) {
            e.printStackTrace();
        }

        return bmp;
    }

    @Override
    protected void onPostExecute(Bitmap bmp) {
        this.mImageView.setImageBitmap(bmp);
    }
}

}

第一次加载所有项目,图像由异步线程动态加载。getView 方法中的以下代码部分可防止列表完全刷新,从而防止图像闪烁:

    String itemValue = getItem(position);

    if (doRefresh == false && itemValue != newItem) {
        return convertView;
    }

当新项目添加到列表中时,ArrayAdapter 将再次遍历列表。只需检查当前位置的项目是否是新添加的项目。如果不是,则返回作为旧视图的 convertView,否则加载项目并返回自定义视图。

于 2016-03-03T11:45:59.803 回答
1

如果您将适配器设置为具有稳定的 ID,那么它应该防止getView(...)调用在调用之前已经可见的视图notifyDataSetChanged()。这可以通过覆盖来完成,hasStableIds()以便它返回true,然后确保您的实现getItemId(...)实际上是返回一个稳定且唯一的id。

@Override
public long getItemId(int position) {
    //you can use position if you only append items, otherwise you will
    //need to handle the ids on your own and keep them stable and unique
    final long id = position;

    return id;
}

@Override
public final boolean hasStableIds() {
    return true;
}
于 2016-08-22T21:20:08.017 回答