3

У меня есть список, который загружает изображения в каждую ячейку в async. Когда я пытаюсь медленно прокручивать (после загрузки всех изображений в текущем представлении), он работает безупречно. Но когда я пытаюсь прокрутить вниз, прежде чем они будут загружены и прокручиваются вверх, я столкнулся с этой проблемой. Клетки начинают показывать изображения, которые им не соответствуют.Почему неправильные образы загружаются в ListView иногда?

метод

Моего GetView выглядит следующим образом:

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

    // TODO Auto-generated method stub 

    View rowView = null; 

    if(convertView == null) { 
     rowView = inflater.inflate(R.layout.list_posts_item, null); 
     final Holder holder=new Holder(); 
     holder.tvTitle=(TextView) rowView.findViewById(R.id.tvTitleNamePost); 
     holder.ivPrimaryImage=(ImageView) rowView.findViewById(R.id.ivPrimaryImage); 
     holder.tvLocality=(TextView) rowView.findViewById(R.id.tvLocalityPosts); 
     holder.tvDateCreated=(TextView) rowView.findViewById(R.id.tvDateCreated); 
     rowView.setTag(holder); 
    }else { 
     rowView=convertView; 
    } 

    Holder holder = (Holder)rowView.getTag(); 
    holder.ivPrimaryImage.setId(position); 
    holder.ivPrimaryImage.setTag(listOfPosts.get(position).getPostId()); 
    holder.ivPrimaryImage.setImageBitmap(null); // Added for flickering issue 
    holder.tvTitle.setText(listOfPosts.get(position).getTitle()); 
    holder.tvLocality.setText(listOfPosts.get(position).getLocality()); 
    holder.tvDateCreated.setText(listOfPosts.get(position).getCreatedDate()); 
    postId = listOfPosts.get(position).getPostId(); 
    Image image = new Image(); 
    image.setImg(holder.ivPrimaryImage); 

    if (!"N".equalsIgnoreCase(listOfPosts.get(position).getHasImage())) { 
     if(!tagsCaching.containsKey(postId)) 
      new GetPrimaryImages().execute(image); 
     else 
      holder.ivPrimaryImage.setImageBitmap(tagsCaching.get(postId)); 
    } 

    return rowView; 
} 

И мой класс Асинхронного вызова выглядит следующим образом:

public class GetPrimaryImages extends AsyncTask<Image, Void, Bitmap> { 

ImageView imageView = null; 
    protected Bitmap doInBackground(Image... images) { 
    this.imageView=images[0].getImg(); 



     // Building Parameters 
     List<NameValuePair> params = new ArrayList<NameValuePair>(); 
     params.add(new BasicNameValuePair("postid",(String)(this.imageView.getTag()))); 


      json = jsonParser.makeHttpRequest(CommonResources.getURL("get_primary_image"), 
        "POST", params); 


     if(json == null){ 
      return null; 
     } 
     Log.d("Fetching Image",imageView.getTag()+ json.toString()); 
     tagsDownloaded.add((String)imageView.getTag()); 
     // check for success tag 
     String TAG_SUCCESS = "success"; 
     try { 
      int success = json.getInt(TAG_SUCCESS); 

      if (success == 0) { 
       image = json.getString("primaryimage"); 

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

     return getImage(image); 

    } 

    /** 
    * After completing background task Dismiss the progress dialog 
    * **/ 
    protected void onPostExecute(Bitmap result) { 
     tagsCaching.put((String)imageView.getTag(), result); 
     imageView.setImageBitmap(result); 

    } 

    public Bitmap getImage(String imageString) { 
     if("null".equalsIgnoreCase(imageString)){ 
      return null; 
     }else{ 
      byte[] decodedString = Base64.decode(imageString, Base64.DEFAULT); 
      Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length); 
      //image.setImageBitmap(decodedByte); 
      return decodedByte; 
     } 

    } 


} 

Edit:

Я добавил новый переменный экземпляр Владелец:

public class Holder 
{ 
    TextView tvTitle; 
    ImageView ivPrimaryImage; 
    TextView tvLocality; 
    TextView tvDateCreated; 
    int position; 
} 

Установите то же самое в getView: holder.position = position; И передал объект держателя в задачу Async: новый GetPrimaryImages (позиция, держатель) .execute (изображение);

И модифицировали класс вызовов Async следующим образом: 1. Добавлена ​​возможность отмены в HTTP вызова 2. Изменен метод onPostExecute

public class GetPrimaryImages extends AsyncTask<Image, Void, Bitmap> { 

    int mPosition; 
    Holder mHolder; 
    public GetPrimaryImages(int position, Holder holder){ 
     mPosition = position; 
     mHolder = holder; 
    } 

    ImageView imageView = null; 
    protected Bitmap doInBackground(Image... images) { 
    this.imageView=images[0].getImg(); 



     List<NameValuePair> params = new ArrayList<NameValuePair>(); 
     params.add(new BasicNameValuePair("postid",(String)(this.imageView.getTag()))); 


     JSONObject json; 
     if(mHolder.position == mPosition) 
      json = jsonParser.makeHttpRequest(CommonResources.getURL("get_primary_image"), 
        "POST", params); 

     else { 
      json = null; 
      cancel(true); 
     } 

     // check log cat fro response 
     if(json == null){ 
      return null; 
     } 
     Log.d("Fetching Image",imageView.getTag()+ json.toString()); 
     tagsDownloaded.add((String)imageView.getTag()); 
     // check for success tag 
     String TAG_SUCCESS = "success"; 
     try { 
      int success = json.getInt(TAG_SUCCESS); 

      if (success == 0) { 
       image = json.getString("primaryimage"); 

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

     return getImage(image); 

    } 


    protected void onPostExecute(Bitmap result) { 
     if (mHolder.position == mPosition) { 
      tagsCaching.put((String) imageView.getTag(), result); 
      imageView.setImageBitmap(result); 
     } 

    } 

    public Bitmap getImage(String imageString) { 


     //needs to wait 
     if("null".equalsIgnoreCase(imageString)){ 
      return null; 
     }else{ 
      byte[] decodedString = Base64.decode(imageString, Base64.DEFAULT); 
      Bitmap decodedByte = BitmapFactory.decodeByteArray(decodedString, 0, decodedString.length); 
      //image.setImageBitmap(decodedByte); 
      return decodedByte; 
     } 

    } 


} 

Это, кажется, работает. :)

Теперь мое сомнение в том, что было бы лучшим способом кэширования изображений? Должно ли записываться в файл? и читать его от него каждый раз, когда я прокручиваю вверх?

+0

еще один «if without else» в getView ([просмотр повторного использования] »(https://www.youtube.com/watch?v=wDBM6wVEO70)) + неправильное время с помощью AsyncTask (что, если асинтаза закончена после просмотра, будет повторно использована ?) ... ** answer: узнайте о повторном использовании вида в ListView и используйте любую библиотеку загрузчиков изображений ** – Selvin

+0

@Selvin Похоже, что неправильное время - проблема. Как синхронизировать его с задачей Async? Или есть способ пропустить задачу Async для просмотров, которые используются повторно? – pagalpanda

+0

использовать LIBRARY, большинство из них отменяют загрузку фона, если вы устанавливаете новый запрос ... это не так просто (не стесняйтесь взглянуть на источники) ... у вас недостаточно навыков для создания собственного загрузчика. а также 'if (!" N "...' не имеет 'else' .... – Selvin

ответ

3

Проблема заключается в том, что ваша задача async завершает работу фона, связанный с ней элемент был переработан для хранения другого элемента вашей коллекции.

Давайте сосредоточимся на позиции элементов, и предположим, что ваш список может отображать до 4 элементов.

Первый раз, когда listview вызывает getview для первых 4 элементов и создаются и запускаются четыре asynctasks. Затем вы прокручиваете позиции 11 - 15, а первый элемент (тот, который относится к позиции 1) получает рециркуляцию для позиции 11 до окончания асинтезы.

Тогда AsyncTask заканчивается, и то, что вы видите, это изображение, связанное с сообщением 11 с растровым связанным размещать 1.

способа избежать это, зная, в AsyncTask, что вид был повторно, как это было предложено в этом старом посте от Лукаса Роча.

Performance tips with listview

Проверьте сообщение для идеи о том, как ListView тоже работает:

enter image description here

+0

@fedpaol Я изменил следующее в своем асинхронном вызове и, кажется, работает if (mHolder.position == mPosition) = jsonParser.makeHttpRequest JSON (CommonResources.getURL ("get_primary_image"), "POST", PARAMS); еще { JSON = NULL; отменить (истина); } защищен недействительным onPostExecute (результат Bitmap) { if (mHolder.position == mPositi on) { tagsCaching.put ((String) imageView.getTag(), result); imageView.setImageBitmap (результат); } } Спасибо – pagalpanda

+0

Не забудьте принять ответ тогда :) – fedepaol

0

Главная "проблема" является с реализацией ListViews многократного использования взглядов и последовательного обеспечения AsyncTasks.

1) В адаптере ListView вы правильно реализуете повторное использование элементов. В ListView отображаются только несколько элементов (элементы, видимые на экране + несколько верхних и нижних).Если вы начинаете прокрутку элементов, которые вышли из экрана, уничтожаются, а их представления передаются asi-параметром в public View getView(final int position, View convertView, ViewGroup parent) как convertView.

Это первая проблема. Список повторное использование элементов.

2) Второе, что AsyncTask выполняется в другом потоке, но больше экземпляров asyncTask выполняются в одном и том же потоке. это означает, что они выполняются серийно. Если вы прокрутите достаточно быстро, вы можете сделать больше запросов на загрузку изображений с помощью AsyncTask. В свое время может быть только несколько (я думаю, 5) заданий AsyncTask, ожидающих очереди. Другое восхождение игнорируется. Поэтому, если вы достаточно быстро проведите по экрану, вы получите эту очередь из 5 полных и еще один запрос изображения: игнорируется.

И это Он. Вы прокручиваете, вы загружаете несколько изображений, а новые отображаемые изображения игнорируются. Когда вы остановитесь после того, как все AsyncTasks закончите, и у вас появилось некоторое «случайное» (предыдущее отображение) изображение, загруженное в ваш элемент списка.

гуманного

Эта вещь была обсуждена раз Manny и решается. Достаточно использовать, например Picaso библиотеки:

https://futurestud.io/blog/picasso-adapter-use-for-listview-gridview-etc/

+0

У picasso нет объяснений, позволяющих делать асинхронные звонки. Я вижу, что у них есть URL-адреса для изображений. Как это сделать? – pagalpanda

+0

С picasso автоматически async =). Библиотека автоматически кэширует изображения (из Интернета или файловой системы) и будет повторно использовать их. Поэтому вам не нужно беспокоиться об этом. Вот официальный сайт с примерами: [Picasso] (http://square.github.io/picasso/). Я скажу это еще раз: не делайте этого YourSelf! Используйте библиотеку. Не изобретайте колесо;) Если вы удовлетворены, пожалуйста, примите ответ –

+0

Мое сомнение в том, как получить изображения с помощью Picasso, с веб-сервиса. Образ помещается в mysql db как тип blob. – pagalpanda

0

Чтобы добавить ответы, я бы реализовать кэш изображения (например, как HashMap URL-к-WeakReference к изображению). getView() будет обращаться к этому кешу и, если изображение отсутствует, оставьте запрос. Когда изображение загружено, кеш будет проверять список запросов и уведомлять представления, которые отправили запросы (передавая их как URL, так и изображение). Представления сравнивали бы URL-адрес, переданный в уведомлении, с их текущим URL-адресом и либо использовали изображение, либо игнорировали его (если вид вышел из экрана или был повторно использован, изображение должно быть проигнорировано).

Зачем нужен список запросов. Возможно, что нескольким представлениям удается запросить некоторое изображение и повторно использовать его до загрузки изображения (особенно если вы пролистаете список вверх и вниз несколько раз).