我想知道是否有办法将项目添加到ListView而不会导致重新加载整个列表。
我有一个BaseAdapter派生的ListView适配器,当底层模型添加一个新元素时,它会调用notifyDataSetChanged(),这会触发ListView重新加载。
列表中的元素包含图像,这些图像根据元素的内容动态加载。问题是,当在重新加载期间调用getView()时,为了重用而传递的convertView参数之前来自不同的位置,因此必须重新加载图像,这会导致相当难看的闪烁。
如果我只在最后添加一个项目(并且这将是添加新项目的唯一方法),那么有没有办法不重新加载整个列表?或者至少以某种方式将单元重用于相同的位置,如果可能的话,以避免昂贵的图像重载?
答案 0 :(得分:5)
没有哥们在Android中不可能你可以在不刷新列表的情况下添加项目,因为当你添加一个项目时它会改变列表视图的高度。参见下面的链接,Romain Guy说了同样的事情。
答案 1 :(得分:1)
这样做而不是调用notifyDataSetChanged():
int firstVisiblePosition = getFirstVisiblePosition();
View view = getChildAt(0);
int distFromTop = (view == null) ? 0 : view.getTop();
setSelectionFromTop(firstVisiblePosition, distFromTop);
答案 2 :(得分:1)
嗯,这是可能的。
重写方法getView并添加ArrayAdapter
我作为测试用例做了什么:
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);
}
}
}
第一次加载所有项目,图像由Async线程动态加载。方法getView中的代码的以下部分阻止列表完全刷新它,从而防止图像闪烁:
String itemValue = getItem(position);
if (doRefresh == false && itemValue != newItem) {
return convertView;
}
当新项目添加到列表时,ArrayAdapter将再次浏览列表。只需检查当前位置的项目是否是新添加的项目。如果没有,则返回convertView,这是旧视图,否则加载项目并返回自定义视图。
答案 3 :(得分:1)
如果您将适配器设置为具有稳定的ID,则应该在调用getView(...)
之前阻止为已经可见的视图调用notifyDataSetChanged()
。这可以通过覆盖hasStableIds()
来完成,以便它返回true
,然后确保getItemId(...)
的实现实际上返回稳定且唯一的 ID。< / p>
@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;
}