问题描述
我已经实现Android应用程序应该从服务器下载图片,并在ListView中显示出来,但是当图像被下载很有意思的事情occures
正如你可以在其中有没有下载是由那些已经被下载了psented重新$ P $视频画面看到的。怎么可能发生?我一直在想这件事近两天。
http://www.youtube.com/watch? V = LXY-HAuJO0o和放大器;配备= youtu.be
这是我的$ C $的ListView适配器的温度。
公共类MoviesAdapter扩展ArrayAdapter< ParkCinema> {
私人的ArrayList< ParkCinema> movieDataItems;
私人活动范围内;
公共MoviesAdapter(活动的背景下,INT textViewResourceId,ArrayList的< ParkCinema> movieDataItems){
超(背景下,textViewResourceId,movieDataItems);
this.context =背景;
this.movieDataItems = movieDataItems;
}
@覆盖
公共查看getView(INT位置,查看convertView,ViewGroup中父){
如果(convertView == NULL){
LayoutInflater VI =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row,NULL);
}
ParkCinema电影= movieDataItems.get(位置);
如果(电影!= NULL){
ImageView的ImageView的=(ImageView的)convertView.findViewById(R.id.movie_thumb_icon);
字符串URL = movie.poster();
如果(网址!= NULL){
点阵位图= fetchBitmapFromCache(URL);
如果(位图== NULL){
新BitmapDownloaderTask(ImageView的).execute(URL);
}
其他 {
imageView.setImageBitmap(位);
}
}
}
返回convertView;
}
私人LinkedHashMap的<字符串,位图> bitmapCache =新的LinkedHashMap<字符串,位图>();
私人无效addBitmapToCache(字符串URL,位图位图){
如果(位图!= NULL){
同步(bitmapCache){
bitmapCache.put(URL,位图);
}
}
}
私人位图fetchBitmapFromCache(字符串URL){
同步(bitmapCache){
最后的位图位= bitmapCache.get(URL);
如果(位图!= NULL){
返回的位图;
}
}
返回null;
}
私有类BitmapDownloaderTask扩展的AsyncTask<字符串,太虚,位图> {
私人字符串URL;
私人最终的WeakReference< ImageView的> imageViewReference;
公共BitmapDownloaderTask(ImageView的ImageView的){
imageViewReference =新的WeakReference< ImageView的>(ImageView的);
}
@覆盖
受保护的位图doInBackground(字符串...源){
URL =源[0];
位图图像;
尝试{
图像= BitmapFactory.de codeStream(新网址(URL).openConnection()的getInputStream());
返回形象;
}
赶上(例外五){Log.e(错误,e.getMessage()); e.printStackTrace();}
返回null;
}
@覆盖
保护无效onPostExecute(位图位图){
addBitmapToCache(URL,位图);
imageViewReference.get()setImageBitmap(位图)。
}
}
}
编辑3:
公开查看getView(INT位置,查看convertView,ViewGroup中父){
如果(convertView == NULL){
LayoutInflater VI =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row,NULL);
}
ParkCinema电影= movieDataItems.get(位置);
ImageView的ImageView的=(ImageView的)convertView.findViewById(R.id.movie_thumb_icon);
如果(电影!= NULL){
字符串URL = movie.poster();
如果(网址!= NULL){
点阵位图= fetchBitmapFromCache(URL);
如果(位图== NULL){
imageView.setImageResource(R.drawable.no_image);
新BitmapDownloaderTask(ImageView的).execute(URL);
}
其他 {
imageView.setImageBitmap(位);
}
}
其他 {
imageView.setImageResource(R.drawable.no_image);
}
}
其他 {
imageView.setImageResource(R.drawable.no_image);
}
返回convertView;
}
啊哈!我想我可能知道的问题。现在,你的 getView
方法设置你的的ImageView
是这样的:
- 在获取电影目标的位置
- 抽影片的缩略图网址
- 在使用这个URL,它试图在缓存中找到 图像
- 如果它发现的形象,它设置为
- 如果无法找到图像,它启动一个异步网络请求去得到它,并将它它就会被下载后。
您伊苏斯出现,因为的ListView
重用其行查看
秒。当第一个查看
滚出屏幕,而不是夸大一个新的,的ListView
通现在屏幕外行的查看
作为 convertView
为你重用(这是为了提高效率)。
当你的 getView
得到了 convertView
,这是获得重用,其 ImageView的
已设置从以前有它的行,所以你看从屏幕外行的查看
的旧形象。根据您当前的 getView
的过程中,您检查新行的形象,并没有找到它在缓存中,它开始请求下载。虽然它正在下载,你看到的旧形象,直到你得到新的形象。
要解决这个问题,你需要确保你设置在该行的查看各个领域
马上,以确保你没有任何查看
正在展示过期数据。我建议你设置的ImageView
为默认的绘制
资源(你在设置你的 R.layout.movie_data_row
),而你的网络下载等获取图像。
@覆盖
公共查看getView(INT位置,查看convertView,ViewGroup中父){
如果(convertView == NULL){
LayoutInflater VI =(LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row,NULL);
}
ParkCinema电影= movieDataItems.get(位置);
ImageView的ImageView的=(ImageView的)convertView.findViewById(R.id.movie_thumb_icon);
如果(电影!= NULL){
字符串URL = movie.poster();
如果(网址!= NULL){
点阵位图= fetchBitmapFromCache(URL);
如果(位图== NULL){
//设置影片缩略图的默认图标正在下载
//实像
imageView.setImageResource(R.drawable.movie_thumb_icon);
新BitmapDownloaderTask(ImageView的).execute(URL);
}
其他 {
//图像设置为我们从缓存中获取的位图
imageView.setImageBitmap(位);
}
}
其他 {
//设置影片缩略图的默认图标,因为它没有
//有一个缩略图网址
imageView.setImageResource(R.drawable.movie_thumb_icon);
}
}
其他 {
//设置影片缩略图的默认图标,因为没有
//此行电影数据
imageView.setImageResource(R.drawable.movie_thumb_icon);
}
- 编辑 -
更新为更强大,使用绘制
。你也有一个问题,你的 BitmapDownloaderTask
,它不处理错误/空。尝试添加这一点。
@覆盖
保护无效onPostExecute(位图位图){
addBitmapToCache(URL,位图);
如果(位图== NULL){
//设置影片缩略图的默认图标,因为在下载时出错
。imageViewReference.get()setImageResource(R.drawable.movie_thumb_icon);
}
其他 {
imageViewReference.get()setImageBitmap(位图)。
}
}
I have implemented android app which should download images from server and display them in ListView, but very interesting thing occures while images are downloading
As you can see in video pictures which haven't been downloaded yet are represented by those which have been already downloaded. How that can happen? I've thinking about it almost two days.
http://www.youtube.com/watch?v=lxY-HAuJO0o&feature=youtu.be
here is my code of ListView adapter.
public class MoviesAdapter extends ArrayAdapter<ParkCinema> {
private ArrayList<ParkCinema> movieDataItems;
private Activity context;
public MoviesAdapter(Activity context, int textViewResourceId, ArrayList<ParkCinema> movieDataItems) {
super(context, textViewResourceId, movieDataItems);
this.context = context;
this.movieDataItems = movieDataItems;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row, null);
}
ParkCinema movie = movieDataItems.get(position);
if (movie!=null){
ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon);
String url = movie.poster();
if (url!=null) {
Bitmap bitmap = fetchBitmapFromCache(url);
if (bitmap==null) {
new BitmapDownloaderTask(imageView).execute(url);
}
else {
imageView.setImageBitmap(bitmap);
}
}
}
return convertView;
}
private LinkedHashMap<String, Bitmap> bitmapCache = new LinkedHashMap<String, Bitmap>();
private void addBitmapToCache(String url, Bitmap bitmap) {
if (bitmap != null) {
synchronized (bitmapCache) {
bitmapCache.put(url, bitmap);
}
}
}
private Bitmap fetchBitmapFromCache(String url) {
synchronized (bitmapCache) {
final Bitmap bitmap = bitmapCache.get(url);
if (bitmap != null) {
return bitmap;
}
}
return null;
}
private class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> {
private String url;
private final WeakReference<ImageView> imageViewReference;
public BitmapDownloaderTask(ImageView imageView) {
imageViewReference = new WeakReference<ImageView>(imageView);
}
@Override
protected Bitmap doInBackground (String... source) {
url = source[0];
Bitmap image;
try{
image = BitmapFactory.decodeStream(new URL(url).openConnection().getInputStream());
return image;
}
catch(Exception e){Log.e("Error", e.getMessage()); e.printStackTrace();}
return null;
}
@Override
protected void onPostExecute(Bitmap bitmap) {
addBitmapToCache(url, bitmap);
imageViewReference.get().setImageBitmap(bitmap);
}
}
}
Edit 3:
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row, null);
}
ParkCinema movie = movieDataItems.get(position);
ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon);
if (movie!=null){
String url = movie.poster();
if (url != null) {
Bitmap bitmap = fetchBitmapFromCache(url);
if (bitmap == null) {
imageView.setImageResource(R.drawable.no_image);
new BitmapDownloaderTask(imageView).execute(url);
}
else {
imageView.setImageBitmap(bitmap);
}
}
else {
imageView.setImageResource(R.drawable.no_image);
}
}
else {
imageView.setImageResource(R.drawable.no_image);
}
return convertView;
}
Aha! I think I may know the issue. Right now, your getView
method sets your ImageView
like this:
- Gets movie object at position
- Pulls out the movie's thumbnail url
- Using that url, it tries to find the image in the cache
- If it finds the image, it sets it
- If it can't find the image, it starts an async network request to go get it, and sets it after it gets downloaded.
Your issus arises since ListView
reuses its rows' View
s. When the first View
scrolls off the screen, rather than inflate a new one, ListView
passes the now offscreen row's View
in as convertView
for you to reuse (this is for efficiency).
When your getView
gets a convertView
that is getting reused, its ImageView
has already been set from the row that had it before, so you see the old image from the offscreen row's View
. With your current getView
process, you check for the new row's image, and it doesn't find it in the cache, it starts a request to download it. While it is downloading, you see the old image until you get the new image.
To fix this, you need to make sure you set every field in the row's View
immediately, to make sure you don't have any View
s showing stale data. I would suggest you set the ImageView
to the default drawable
resource (you have set in your R.layout.movie_data_row
) while you wait for the network download to get the image.
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater vi = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = vi.inflate(R.layout.movie_data_row, null);
}
ParkCinema movie = movieDataItems.get(position);
ImageView imageView = (ImageView) convertView.findViewById(R.id.movie_thumb_icon);
if (movie != null) {
String url = movie.poster();
if (url != null) {
Bitmap bitmap = fetchBitmapFromCache(url);
if (bitmap == null) {
// Set the movie thumbnail to the default icon while we load
// the real image
imageView.setImageResource(R.drawable.movie_thumb_icon);
new BitmapDownloaderTask(imageView).execute(url);
}
else {
// Set the image to the bitmap we get from the cache
imageView.setImageBitmap(bitmap);
}
}
else {
// Set the movie thumbnail to the default icon, since it doesn't
// have a thumbnail URL
imageView.setImageResource(R.drawable.movie_thumb_icon);
}
}
else {
// Set the movie thumbnail to the default icon, since there's no
// movie data for this row
imageView.setImageResource(R.drawable.movie_thumb_icon);
}
-Edit-
Updated to be even more robust, using your drawable
. You also have an issue with your BitmapDownloaderTask
, it does not handle errors/null. Try adding this as well.
@Override
protected void onPostExecute(Bitmap bitmap) {
addBitmapToCache(url, bitmap);
if (bitmap == null) {
// Set the movie thumbnail to the default icon, since an error occurred while downloading
imageViewReference.get().setImageResource(R.drawable.movie_thumb_icon);
}
else {
imageViewReference.get().setImageBitmap(bitmap);
}
}
这篇关于图像中重复的ListView的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!