Android URL图像在listview中无序移动

Android URL图像在listview中无序移动,android,json,listview,Android,Json,Listview,我只是注意到,当我从一个url将我的图片加载到一个列表视图中时,有时它们会在找到正确的图片之前循环浏览。我怎样才能防止这种情况发生 这是来自自定义适配器的异步任务 private class AsyncDownloadTask extends AsyncTask<Object, String, Bitmap>{ private View view; private Bitmap bitmap = null; @Override

我只是注意到,当我从一个url将我的图片加载到一个列表视图中时,有时它们会在找到正确的图片之前循环浏览。我怎样才能防止这种情况发生

这是来自自定义适配器的异步任务

private class AsyncDownloadTask extends AsyncTask<Object, String, Bitmap>{

        private View view;
        private Bitmap bitmap = null;

        @Override
        protected void onPreExecute() {
            super.onPreExecute();
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if(bitmap!=null&&view!=null){
                ImageView newsIcon = (ImageView)view.getTag(R.id.newsIcon);
                newsIcon.setImageBitmap(bitmap);
            }
        }

        @Override
        protected Bitmap doInBackground(Object... params) {
            view = (View)params[0];
            String uri = (String)params[1];
            try{
                InputStream inputStream = new URL(uri).openStream();
                bitmap = BitmapFactory.decodeStream(inputStream);
            }catch (Exception e){
                e.printStackTrace();
            }
            return bitmap;
        }
    }

不确定它是否有效,但您也可以将图像uri字符串放入DataHandler中。并在postExecute中检查,以便仅当它与最后一个匹配时才应用它:

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if(bitmap!=null&&view!=null && ((DataHandler)view.getTag()).uri.equals(this.uri){
            ImageView newsIcon = (ImageView)view.getTag(R.id.newsIcon);
            newsIcon.setImageBitmap(bitmap);
        }
    }

然后在AsyncTask中也有一个私有字符串uri,该任务在
doInBackground

中设置,不确定它是否有效,但您也可以将图像uri字符串放入DataHandler中。并在postExecute中检查,以便仅当它与最后一个匹配时才应用它:

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if(bitmap!=null&&view!=null && ((DataHandler)view.getTag()).uri.equals(this.uri){
            ImageView newsIcon = (ImageView)view.getTag(R.id.newsIcon);
            newsIcon.setImageBitmap(bitmap);
        }
    }

然后在AsyncTask中还有一个私有字符串uri,该uri在
doInBackground

中设置。导致此问题的一个错误是,您将视图绑定到任务。从理论上讲,这将导致显示图像的不一致性,例如,如果您的图像视图引用到两个任务中,哪个任务最后完成将设置位图。如何解决此问题只需将任务引用设置为图像视图的标记,在创建新任务之前,首先从图像视图获取旧任务(如果该任务不为null且未完成),取消它并创建新任务

下面是位重构代码,请尝试以下操作

@Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View row = convertView;
        DataHandler dh;
        if(position==0){

            NewsObj currNews = news.get(position);
            if(convertView==null){
                LayoutInflater inflater = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = inflater.inflate(R.layout.firstnews,parent,false);
                dh = new DataHandler();
                dh.newsTitle = (TextView)row.findViewById(R.id.newsTitle);
                dh.newsDate = (TextView)row.findViewById(R.id.newsDate);
                dh.newsIcon = (ImageView)row.findViewById(R.id.newsIcon);
                row.setTag(dh);
            }else{
                dh = (DataHandler)row.getTag();
            }
            NewsObj no = (NewsObj)this.getItem(position);
            row.setTag(R.id.newsIcon,row.findViewById(R.id.newsIcon));
            dh.newsTitle.setText(no.getTitle());
            dh.newsDate.setText(no.getDate());

        }else{

            NewsObj currNews = news.get(position);
            if(convertView==null){
                LayoutInflater inflater = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = inflater.inflate(R.layout.newslist,parent,false);
                dh = new DataHandler();
                dh.newsTitle = (TextView)row.findViewById(R.id.newsTitle);
                dh.newsDate = (TextView)row.findViewById(R.id.newsDate);
                dh.newsIcon = (ImageView)row.findViewById(R.id.newsIcon);
                row.setTag(dh);
            }else{
                dh = (DataHandler)row.getTag();
            }
            NewsObj no = (NewsObj)this.getItem(position);
            row.setTag(R.id.newsIcon, row.findViewById(R.id.newsIcon));
            dh.newsTitle.setText(no.getTitle());
            dh.newsDate.setText(no.getDate());

        }
      AsyncTask task = (AsyncTask)dh.newsIcon.getTag();
      if(task!=null && task.getStatus()!=FINISHED){
      task.cancel();
      }
       task = new AsyncDownloadTask();
       dh.newsIcon.setTag(task);
       task.execute(row,no.getImgurl());

        return row;
    }

导致此问题的一个原因是您做错了,您将视图绑定到任务。从理论上讲,这将导致显示图像的不一致性,例如,如果您的图像视图引用到两个任务中,哪个任务最后完成将设置位图。如何解决此问题只需将任务引用设置为图像视图的标记,在创建新任务之前,首先从图像视图获取旧任务(如果该任务不为null且未完成),取消它并创建新任务

下面是位重构代码,请尝试以下操作

@Override
    public View getView(int position, View convertView, ViewGroup parent) {

        View row = convertView;
        DataHandler dh;
        if(position==0){

            NewsObj currNews = news.get(position);
            if(convertView==null){
                LayoutInflater inflater = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = inflater.inflate(R.layout.firstnews,parent,false);
                dh = new DataHandler();
                dh.newsTitle = (TextView)row.findViewById(R.id.newsTitle);
                dh.newsDate = (TextView)row.findViewById(R.id.newsDate);
                dh.newsIcon = (ImageView)row.findViewById(R.id.newsIcon);
                row.setTag(dh);
            }else{
                dh = (DataHandler)row.getTag();
            }
            NewsObj no = (NewsObj)this.getItem(position);
            row.setTag(R.id.newsIcon,row.findViewById(R.id.newsIcon));
            dh.newsTitle.setText(no.getTitle());
            dh.newsDate.setText(no.getDate());

        }else{

            NewsObj currNews = news.get(position);
            if(convertView==null){
                LayoutInflater inflater = (LayoutInflater)this.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                row = inflater.inflate(R.layout.newslist,parent,false);
                dh = new DataHandler();
                dh.newsTitle = (TextView)row.findViewById(R.id.newsTitle);
                dh.newsDate = (TextView)row.findViewById(R.id.newsDate);
                dh.newsIcon = (ImageView)row.findViewById(R.id.newsIcon);
                row.setTag(dh);
            }else{
                dh = (DataHandler)row.getTag();
            }
            NewsObj no = (NewsObj)this.getItem(position);
            row.setTag(R.id.newsIcon, row.findViewById(R.id.newsIcon));
            dh.newsTitle.setText(no.getTitle());
            dh.newsDate.setText(no.getDate());

        }
      AsyncTask task = (AsyncTask)dh.newsIcon.getTag();
      if(task!=null && task.getStatus()!=FINISHED){
      task.cancel();
      }
       task = new AsyncDownloadTask();
       dh.newsIcon.setTag(task);
       task.execute(row,no.getImgurl());

        return row;
    }


你试过使用像毕加索、弗雷斯科或格莱德这样的合适的图像加载器吗?不幸的是,要求不要使用任何外部图书馆。因为毕加索/格莱德是我的作品。是的,我不能使用它们。嗯,奇怪的要求。我看到您使用的是视图保持器模式,但是这很好,有两个问题1)内存:你正在将整个位图加载到一个微小的ImageView 2)网络:异步任务将在每次视图在帧内滚动时执行+图像不会在加载新图像时从内存中清除你是否尝试过使用合适的图像加载程序,如毕加索、弗雷斯科、,或者Glide?不幸的是,要求不要使用任何外部图书馆。这是毕加索/Glide为我设计的。是的,我不能不幸地使用它们。嗯,奇怪的要求。我看到您使用的是视图保持器模式,但是这很好,有2个问题1)内存:您正在将整个位图加载到一个微小的ImageView 2)网络:异步任务将在每次视图滚动进出帧时执行+图像不会在加载新图像时从内存中清除对不起,uri从何处获取?就在您执行
new AsyncDownloadTask()之前。执行(row,no.getImgurl());
您可以执行
dh.uri=no.getImgurl()
例如,对不起,我理解了第一部分,但是我不能声明dh.uri,因为dh链接了一个TextView。当然,dh只是您自己创建的一个类,您可以使用任何您喜欢或不喜欢的字段对其进行扩展?对不起,我从何处获取uri?就在您执行
新建AsyncDownloadTask()之前。执行(行,no.getImgurl());
你可以做
dh.uri=no.getImgurl()
例如,对不起,我理解第一部分,但我不能声明dh.uri,因为dh链接了一个textView。当然dh只是你自己创建的一个类,你可以用任何你喜欢或不喜欢的字段来扩展它。对不起,你能指出我在我的代码中在哪里做这件事吗?是这个吗?row.setTag(R.id.newsIcon,row.findviewbyd(R.id.newsIcon));新建AsyncDownloadTask().execute(row,no.getImgurl());在if和else子句中。对于第一个可见项,它可以正常工作,但只要您开始滚动每次创建新任务时都不会取消旧任务。那么我是否应该将其从if-else中删除并在外部声明?不确定是否需要此行row.setTag(R.id.newsIcon,row.findViewById(R.id.newsIcon));但您可以这样做Task=(Task)row.getTag();if(Task!=null&&Task.isNofinished){Task.cancel();}Task=new asynchdownloadtask();row.setTag(row),row.setTag(row);Task.execute(row,no.getImgurl());我需要任务的外部库吗?我似乎找不到它。您能指出我在代码中的什么地方做这件事吗?是row.setTag吗(R.id.newsIcon,row.findviewbyd(R.id.newsIcon));新建AsyncDownloadTask().execute(row,no.getImgurl());在if和else子句中。对于第一个可见项,它可以正常工作,但只要您开始滚动每次创建新任务时都不会取消旧任务。那么我是否应该将其从if-else中删除并在外部声明?不确定是否需要此行row.setTag(R.id.newsIcon,row.findViewById(R.id.newsIcon));但您可以这样做Task=(Task)row.getTag();if(Task!=null&&Task.isNofinished){Task.cancel();}Task=new AsyncDownloadTask();row.setTag(row);Task.execute(row,no.getImgurl());我需要任务的外部库吗?我似乎找不到它