在 ListView 中加载异步图像时遇到问题
Having a trouble with Asynchronous image loading in ListView
这对我来说是个大麻烦,我已经为此工作了几个小时,但毫无头绪!
我在 ListView 中加载异步图像时遇到问题。
我正在使用 Parse.com 作为我的应用程序的后端,我正在从 Parse.com class.
中检索消息及其图像
一切正常,当我滚动 Up/Down,或再次加载列表视图时,图片混合,所有图片以另一个顺序重新加载,几秒钟后它们将按我想要的顺序排列,但是这对我来说是个大问题。
我认为这是因为我正在使用 class 来上传图片
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
无论如何,我的适配器 class 如下:
public class adapterview extends ArrayAdapter<Message> {
Bitmap image;
public adapterview(Context context, ArrayList<Message> Messages) {
super(context, 0, Messages);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
final Message m = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.custom2, parent, false);
}
TextView message = (TextView) convertView.findViewById(R.id.message);
TextView date = (TextView) convertView.findViewById(R.id.date);
TextView user = (TextView) convertView.findViewById(R.id.user);
message.setText(m.getMessage());
user.setText(m.getUser());
new DownloadImageTask((ImageView) convertView.findViewById(R.id.imageView1))
.execute(m.getImage());
return convertView;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
这是我的信息 class :
public class Message {
String message;
String user;
String phone;
String image;
String date;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
这就是我向列表中添加项目的方式:
contacts c = new contacts();
c.setName(object.get("name").toString());
c.setNumero(object.get("phone").toString());
ParseFile image = (ParseFile)object.get("Profil");
c.setProfil(image.getUrl());
messagelist.addcontact(c);
这是我如何从 MainActivity 填充列表视图的代码:
adapterview adapter = new adapterview(MainActivity.this, (ArrayList<Message>) messagelist);
list.setAdapter(adapter);
这是我向下滚动然后向上滚动时的两个屏幕截图,因此第一个 listItem 的图片发生了变化!!无缘无故,然后几秒钟后恢复正常画面。
如果您能建议我如何将图像从 URL 上传到 ImageView 的新方法,那就太好了。
上次我也是手动下载联系人头像的,但是不好用。
我认为你的问题:
- 下载头像,然后设置为ImageView
- 但是,您使用的是 ViewHolder 模式,因此新下载的头像将设置为许多 imageview > 指定联系人的错误头像(这仅 运行 当所有头像都已下载时)
首先,查看Universal Image Loader库,将头像URL设置为视图,它会自动下载,将您的头像缓存到内存中。
然后post这里如果还是搞错头像问题
什么是列表视图单元格布局 xml 文件?
我认为 Android 无法确定列表视图的单元格大小。这会导致一些问题。
如果您想使用异步加载图像,解决该问题的一种方法是修复图像大小。例如
<ImageView
android:id="@+id/row_icon1"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_gravity="center_vertical"
/>
the Pictures Mixed, and all pictures Reload in another order, and
after few seconds they will ordered as I want
这种奇怪的行为是由于 AsyncTask
在 getView
方法中没有 View Holder Pattern.
为了避免再次渲染已经渲染过的缩略图图像并在滚动列表视图中从网络加载新图像,请使用 View Holder 模式。
有关使用 View Holder 创建具有良好滚动和加载性能的 ListView 的有用教程,请参阅以下内容:
我认为你观察到的是由于 listview.You 的 viewholder 模式正在尝试通过 getview() 中的 aysnctask 下载图像并将其设置为图像,但是当你向上或向下滚动时,视图会被回收当你再次到达那个位置时,你的 getview 调用了那个位置,所以 asynctask 再次执行,它再次下载 imageview 中的图像设置,因此你会看到延迟,我建议你使用 PICASSO 来自 square 的 librarray 它对于图像下载和缓存图像非常有效这里也是 link http://square.github.io/picasso/ or use Glide which is now officially recommended by Google,here is the github link Glide
发生这种情况是因为 ListView 在您滚动时回收视图,然后当您调用异步任务下载图片并滚动时,下载完成但引用指向回收视图和一个或多个异步任务指向到相同的观点。避免这种情况的一种方法是在下载开始和下载结束时查看其标记,然后在绘制图像之前检查其实际引用是否正确,然后绘制图像。
您可以使用图像的 URL 标记视图,因为它是唯一值。
在下载的预执行中,您必须使用 URL(String) 标记 ImageView。
imageView.setTag(url);
当任务完成并在绘制图像之前,恢复标签并检查它。
String tag = (String)imageView.getTag();
if(tag != null && tag.equals(url)){
//Draw the image
} else {
//Draw the placeholder or clean the ImageView
}
并且您可以使用 ViewHolder 模式升级 ListView 的性能,以及为什么当 Parse 可以通过他们的 API 执行它时手动下载图像,对于 Parse 方式和使用缓存策略,当你下载一个图像自动缓存位图,当你再次要求下载时,只是检索缓存的结果。
干杯!
我通过使用回收视图 onBindViewHolder
解决了
@Override
public void onBindViewHolder(final MyViewHolder viewHolder, int i) {
CartsViewModel currentData = data.get(i);
ParseFile image = currentData.getParseImage();
if(image!=null){
Log.d(TAG,"image not null. Downloading....");
image.getDataInBackground(new GetDataCallback() {
@Override
public void done(byte[] bytes, ParseException e) {
if(e==null){
Log.d(TAG,"donload finished");
Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
viewHolder.cartImage.setImageBitmap(bmp);
}else{
viewHolder.cartImage.setImageBitmap(null);
}
}else{
viewHolder.cartImage.setImageBitmap(null);
}
});
}
viewHolder.cartName.setText(currentData.getCartName());
viewHolder.cartAddress.setText(currentData.getCartAddress());
viewHolder.rating.setText(currentData.getRating());
viewHolder.reviewCount.setText(currentData.getReviewCount());
}
parse 方法非常有效。
这对我来说是个大麻烦,我已经为此工作了几个小时,但毫无头绪!
我在 ListView 中加载异步图像时遇到问题。
我正在使用 Parse.com 作为我的应用程序的后端,我正在从 Parse.com class.
中检索消息及其图像一切正常,当我滚动 Up/Down,或再次加载列表视图时,图片混合,所有图片以另一个顺序重新加载,几秒钟后它们将按我想要的顺序排列,但是这对我来说是个大问题。
我认为这是因为我正在使用 class 来上传图片
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap>
无论如何,我的适配器 class 如下:
public class adapterview extends ArrayAdapter<Message> {
Bitmap image;
public adapterview(Context context, ArrayList<Message> Messages) {
super(context, 0, Messages);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
// Get the data item for this position
final Message m = getItem(position);
// Check if an existing view is being reused, otherwise inflate the view
if (convertView == null) {
convertView = LayoutInflater.from(getContext()).inflate(R.layout.custom2, parent, false);
}
TextView message = (TextView) convertView.findViewById(R.id.message);
TextView date = (TextView) convertView.findViewById(R.id.date);
TextView user = (TextView) convertView.findViewById(R.id.user);
message.setText(m.getMessage());
user.setText(m.getUser());
new DownloadImageTask((ImageView) convertView.findViewById(R.id.imageView1))
.execute(m.getImage());
return convertView;
}
private class DownloadImageTask extends AsyncTask<String, Void, Bitmap> {
ImageView bmImage;
public DownloadImageTask(ImageView bmImage) {
this.bmImage = bmImage;
}
protected Bitmap doInBackground(String... urls) {
String urldisplay = urls[0];
Bitmap mIcon11 = null;
try {
InputStream in = new java.net.URL(urldisplay).openStream();
mIcon11 = BitmapFactory.decodeStream(in);
} catch (Exception e) {
Log.e("Error", e.getMessage());
e.printStackTrace();
}
return mIcon11;
}
protected void onPostExecute(Bitmap result) {
bmImage.setImageBitmap(result);
}
}
这是我的信息 class :
public class Message {
String message;
String user;
String phone;
String image;
String date;
public String getDate() {
return date;
}
public void setDate(String date) {
this.date = date;
}
public String getImage() {
return image;
}
public void setImage(String image) {
this.image = image;
}
public String getPhone() {
return phone;
}
public void setPhone(String phone) {
this.phone = phone;
}
public String getUser() {
return user;
}
public void setUser(String user) {
this.user = user;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
}
这就是我向列表中添加项目的方式:
contacts c = new contacts();
c.setName(object.get("name").toString());
c.setNumero(object.get("phone").toString());
ParseFile image = (ParseFile)object.get("Profil");
c.setProfil(image.getUrl());
messagelist.addcontact(c);
这是我如何从 MainActivity 填充列表视图的代码:
adapterview adapter = new adapterview(MainActivity.this, (ArrayList<Message>) messagelist);
list.setAdapter(adapter);
这是我向下滚动然后向上滚动时的两个屏幕截图,因此第一个 listItem 的图片发生了变化!!无缘无故,然后几秒钟后恢复正常画面。
如果您能建议我如何将图像从 URL 上传到 ImageView 的新方法,那就太好了。
上次我也是手动下载联系人头像的,但是不好用。 我认为你的问题:
- 下载头像,然后设置为ImageView
- 但是,您使用的是 ViewHolder 模式,因此新下载的头像将设置为许多 imageview > 指定联系人的错误头像(这仅 运行 当所有头像都已下载时)
首先,查看Universal Image Loader库,将头像URL设置为视图,它会自动下载,将您的头像缓存到内存中。
然后post这里如果还是搞错头像问题
什么是列表视图单元格布局 xml 文件? 我认为 Android 无法确定列表视图的单元格大小。这会导致一些问题。 如果您想使用异步加载图像,解决该问题的一种方法是修复图像大小。例如
<ImageView
android:id="@+id/row_icon1"
android:layout_width="60dip"
android:layout_height="60dip"
android:layout_gravity="center_vertical"
/>
the Pictures Mixed, and all pictures Reload in another order, and after few seconds they will ordered as I want
这种奇怪的行为是由于 AsyncTask
在 getView
方法中没有 View Holder Pattern.
为了避免再次渲染已经渲染过的缩略图图像并在滚动列表视图中从网络加载新图像,请使用 View Holder 模式。
有关使用 View Holder 创建具有良好滚动和加载性能的 ListView 的有用教程,请参阅以下内容:
我认为你观察到的是由于 listview.You 的 viewholder 模式正在尝试通过 getview() 中的 aysnctask 下载图像并将其设置为图像,但是当你向上或向下滚动时,视图会被回收当你再次到达那个位置时,你的 getview 调用了那个位置,所以 asynctask 再次执行,它再次下载 imageview 中的图像设置,因此你会看到延迟,我建议你使用 PICASSO 来自 square 的 librarray 它对于图像下载和缓存图像非常有效这里也是 link http://square.github.io/picasso/ or use Glide which is now officially recommended by Google,here is the github link Glide
发生这种情况是因为 ListView 在您滚动时回收视图,然后当您调用异步任务下载图片并滚动时,下载完成但引用指向回收视图和一个或多个异步任务指向到相同的观点。避免这种情况的一种方法是在下载开始和下载结束时查看其标记,然后在绘制图像之前检查其实际引用是否正确,然后绘制图像。
您可以使用图像的 URL 标记视图,因为它是唯一值。
在下载的预执行中,您必须使用 URL(String) 标记 ImageView。
imageView.setTag(url);
当任务完成并在绘制图像之前,恢复标签并检查它。
String tag = (String)imageView.getTag();
if(tag != null && tag.equals(url)){
//Draw the image
} else {
//Draw the placeholder or clean the ImageView
}
并且您可以使用 ViewHolder 模式升级 ListView 的性能,以及为什么当 Parse 可以通过他们的 API 执行它时手动下载图像,对于 Parse 方式和使用缓存策略,当你下载一个图像自动缓存位图,当你再次要求下载时,只是检索缓存的结果。
干杯!
我通过使用回收视图 onBindViewHolder
@Override
public void onBindViewHolder(final MyViewHolder viewHolder, int i) {
CartsViewModel currentData = data.get(i);
ParseFile image = currentData.getParseImage();
if(image!=null){
Log.d(TAG,"image not null. Downloading....");
image.getDataInBackground(new GetDataCallback() {
@Override
public void done(byte[] bytes, ParseException e) {
if(e==null){
Log.d(TAG,"donload finished");
Bitmap bmp = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
viewHolder.cartImage.setImageBitmap(bmp);
}else{
viewHolder.cartImage.setImageBitmap(null);
}
}else{
viewHolder.cartImage.setImageBitmap(null);
}
});
}
viewHolder.cartName.setText(currentData.getCartName());
viewHolder.cartAddress.setText(currentData.getCartAddress());
viewHolder.rating.setText(currentData.getRating());
viewHolder.reviewCount.setText(currentData.getReviewCount());
}
parse 方法非常有效。