2012-03-15 3 views
2

Я изо всех сил стараюсь обновить представление списка с помощью данных из базы данных, это хорошо работает с помощью SimpleCursorAdapter. Но представление изображения в строках не обновляется при запуске, я должен прокручивать список несколько раз, и только тогда изображения загружаются в представление изображения.Android List view update

Это связующее я использую для SimpleCursorAdapter:

private class PromotionViewBinder implements SimpleCursorAdapter.ViewBinder { 
      private int done; 
      public boolean setViewValue(View view, Cursor cursor, int index) { 
       Log.e(""+cursor.getCount(),""); 
       View tmpview = view; 

       if (index == cursor.getColumnIndex(PromotionsTable.SEEN_COL)) { 
        boolean read = cursor.getInt(index) > 0 ? true : false; 
        TextView title = (TextView) tmpview; 
        if (!read) { 
         title.setTypeface(Typeface.DEFAULT_BOLD, 0); 
        } else { 
         title.setTypeface(Typeface.DEFAULT); 
        } 
        return true; 
       } else if (tmpview.getId() == R.id.promotions_list_row_image){ 
         String imageURL = cursor.getString(index); 
         Log.e("",imageURL); 
         imageRetriever.displayImage(imageURL, (ImageView)tmpview); 
         return true; 
       } else { 
        return false; 
       } 
      } 
     } 

Изображение класса ретривера является LazyList example from here. Как вы увидите, это использование runnable для извлечения изображений и после выполнения задачи автоматически обновляет данное изображениеView ... Как вы думаете, что ссылка на imageView теряется где-то на пути?

Thanx заранее, Ник

package com.tipgain.promotions; 

Класс изображения ретривера:

/** 
* This class is used for retrieving images from a given web link. it uses local 
* storage and memory to store the images. Once a image is downloaded 
* successfully the UI gets updated automatically. 
* 
* 
*/ 
public class ImageRetriever { 
    private final String TAG = ImageRetriever.class.getName(); 

    private MemoryImageCache memoryImgCache = new MemoryImageCache(); 
    private LocalStorageImageCache localFileCache; 
    private Map<ImageView, String> imageViewHolders = Collections 
      .synchronizedMap(new WeakHashMap<ImageView, String>()); 
    private ExecutorService execService; 
    final int defaultImageID = R.drawable.photo_not_available; 

    public ImageRetriever(Context context) { 
     localFileCache = new LocalStorageImageCache(context); 
     execService = Executors.newFixedThreadPool(5); 
    } 

    public void displayImage(String url, ImageView imageView) { 
     imageViewHolders.put(imageView, url); 
     Bitmap bmp = memoryImgCache.retrieve(url); 

     if (bmp != null) { 
      Log.e("case 1", " " + (bmp != null)); 
      imageView.setImageBitmap(bmp); 

     } else { 
      Log.e("case 2", " " + (bmp == null)); 
      addImageToQueue(url, imageView); 
      imageView.setImageResource(defaultImageID); 
     } 
    } 

    private void addImageToQueue(String url, ImageView imageView) { 
     NextImageToLoad img = new NextImageToLoad(url, imageView); 
     execService.submit(new ImagesRetriever(img)); 
    } 

    /** 
    * This method is used for retrieving the Bitmap Image. 
    * 
    * @param url 
    *   String representing the url pointing to the image. 
    * @return Bitmap representing the image 
    */ 
    private Bitmap getBitmap(String url) { 
     File imageFile = localFileCache.getFile(url); 

     // trying to get the bitmap from the local storage first 
     Bitmap bmp = decodeImageFile(imageFile); 
     if (bmp != null) 
      return bmp; 

     // if the file was not found locally we retrieve it from the web 
     try { 
      URL imageUrl = new URL(url); 
      HttpURLConnection conn = (HttpURLConnection) imageUrl 
        .openConnection(); 
      conn.setConnectTimeout(30000); 
      conn.setReadTimeout(30000); 
      conn.setInstanceFollowRedirects(true); 
      InputStream is = conn.getInputStream(); 
      OutputStream os = new FileOutputStream(imageFile); 
      Utils.CopyStream(is, os); 
      os.close(); 
      bmp = decodeImageFile(imageFile); 
      return bmp; 
     } catch (MalformedURLException e) { 
      Log.e(TAG, e.getMessage()); 
     } catch (FileNotFoundException e) { 
      Log.e(TAG, e.getMessage()); 
     } catch (IOException e) { 
      Log.e(TAG, e.getMessage()); 
     } 
     return null; 
    } 

    /** 
    * This method is used for decoding a given image file. Also, to reduce 
    * memory, the image is also scaled. 
    * 
    * @param imageFile 
    * @return 
    */ 
    private Bitmap decodeImageFile(File imageFile) { 
     try { 
      BitmapFactory.Options options = new BitmapFactory.Options(); 
      options.inJustDecodeBounds = true; 
      BitmapFactory.decodeStream(new FileInputStream(imageFile), null, 
        options); 

      // Find the correct scale value. It should be the power of 2. 
      // Deciding the perfect scaling value. (^2). 
      final int REQUIRED_SIZE = 100; 
      int tmpWidth = options.outWidth, tmpHeight = options.outHeight; 
      int scale = 1; 
      while (true) { 
       if (tmpWidth/2 < REQUIRED_SIZE 
         || tmpHeight/2 < REQUIRED_SIZE) 
        break; 
       tmpWidth /= 2; 
       tmpHeight /= 2; 
       scale *= 2; 
      } 

      // decoding using inSampleSize 
      BitmapFactory.Options option2 = new BitmapFactory.Options(); 
      option2.inSampleSize = scale; 
      return BitmapFactory.decodeStream(new FileInputStream(imageFile), 
        null, option2); 
     } catch (FileNotFoundException e) { 
      Log.e(TAG, e.getLocalizedMessage()); 
     } 
     return null; 
    } 

    private boolean reusedImage(NextImageToLoad image) { 
     Context c = image.imageView.getContext(); 
     c.getContentResolver().notifyChange(PromotionsProvider.CONTENT_URI, null); 

     String tag = imageViewHolders.get(image.imageView); 
     if ((tag == null) || (!tag.equals(image.url))) 
      return true; 
     return false; 
    } 

    /** 
    * Clears the Memory and Local cache 
    */ 
    public void clearCache() { 
     memoryImgCache.clear(); 
     localFileCache.clear(); 
    } 

    /** 
    * This class implements a runnable that is used for updating the promotions 
    * images on the UI 
    * 
    * 
    */ 
    class UIupdater implements Runnable { 
     Bitmap bmp; 
     NextImageToLoad image; 

     public UIupdater(Bitmap bmp, NextImageToLoad image) { 
      this.bmp = bmp; 
      this.image = image; 
      Log.e("", "ui updater"); 
     } 

     public void run() { 
      Log.e("ui updater", "ui updater"); 
      if (reusedImage(image)) 
       return; 
      Log.e("nick", "" + (bmp == null) + "  chberugv"); 
      if (bmp != null){ 
       image.imageView.setImageBitmap(bmp); 
       Context c = image.imageView.getContext(); 
       c.getContentResolver().notifyChange(PromotionsProvider.CONTENT_URI, null); 

      }else 
       image.imageView.setImageResource(defaultImageID); 

      } 
    } 

    private class ImagesRetriever implements Runnable { 
     NextImageToLoad image; 

     ImagesRetriever(NextImageToLoad image) { 
      this.image = image; 
     } 

     public void run() { 
      Log.e("images retirever", " images retriever"); 
      if (reusedImage(image)) 
       return; 
      Bitmap bmp = getBitmap(image.url); 
      memoryImgCache.insert(image.url, bmp); 
      if (reusedImage(image)) 
       return; 
      UIupdater uiUpdater = new UIupdater(bmp, image); 
      Activity activity = (Activity) image.imageView.getContext(); 
      activity.runOnUiThread(uiUpdater); 
      //Context c = image.imageView.getContext(); 
      //c.getContentResolver().notifyChange(PromotionsProvider.CONTENT_URI, null); 


     } 
    } 

    /** 
    * This class encapsulates the image being downloaded. 
    * 
    * @author Nicolae Anca 
    * 
    */ 
    private class NextImageToLoad { 
     public String url; 
     public ImageView imageView; 

     public NextImageToLoad(String u, ImageView i) { 
      url = u; 
      imageView = i; 
     } 
    } 

} 

Модифицированный Runnable:

class UIupdater implements Runnable { 
    Bitmap bmp; 
    NextImageToLoad image; 

    public UIupdater(Bitmap bmp, NextImageToLoad image) { 
     this.bmp = bmp; 
     this.image = image; 
    } 

    public void run() { 
     if (reusedImage(image)) 
      return; 
     if (bmp != null){ 
      image.imageView.setImageBitmap(bmp); 
      Context c = image.imageView.getContext(); 
      c.getContentResolver().notifyChange(PromotionsProvider.CONTENT_URI, null); 

     }else 
      image.imageView.setImageResource(defaultImageID); 

     } 
} 
+1

+1, очень описательный вопрос. – gkiar

ответ

0

Thats интересный способ делать то, что вы делаете. Вы пытались расширить Простой адаптер курсора?

  1. Что вы делаете, это реализовать ViewHolder и поместить в него ваше изображение.

  2. Затем в вашем ImageRetriever напишите прослушиватель, который будет вызываться после того, как изображение будет готово и извлечено.

  3. Внесите этот слушатель в держатель.

  4. Вы создаете представление в getView() и запрашиваете изображение в BindView().

  5. Как только изображение загрузится, список будет обновлен автоматически.

+0

Я нашел более простой способ сделать i, но я не уверен, что он стоит слишком много CPU или нет ..... в любой из runnables, которые у меня есть, я вызвал Context c = image.imageView.getContext() ; \t \t \t \t c.getContentResolver(). NotifyChange (PromotionsProvider.CONTENT_URI, null); и список обновляется? ... как вы думаете, это хорошее решение? – Nick

+0

Я добавил модифицированный класс runnable в первый пост, так что вы можете видеть, что я сделал ... большое спасибо – Nick

+0

Эта реализация становится все более интересной. Это нечто новое для меня. :) Что касается потребления процессора, я бы предпочел не комментировать. Я проведу когда-нибудь это правильно. Вы видите какие-то проблемы с производительностью? – Shubhayu

0

один из способов сделать это, позвонив notifyDataSetChenged на ListView , а другой - иметь адаптер в качестве переменной-члена и когда что-то чанг es on listview вы вызываете функцию, которая назначает новый адаптер listadapter для адаптера члена. Таким образом, ваш список будет перерисовываться при изменении.

+0

, но где я должен назвать notifiydatasetChange? .. в методе setViewValue? – Nick

+0

в любое время, когда вы видите, что данные были изменены или пользовательский интерфейс необходимо обновить по мере обновления базы данных, вы вызываете notifyDatasetChanget(), так как это вызовет переход и перерисовки. Вы также можете переопределить функцию notifyDatasetchanged для более сложной функции или по мере необходимости – Mayank

0

Я думаю, вы должны использовать некоторый обработчик, вызов после изображения нагрузки, которая будет вызывать notifyDataSetChanged для списка адаптера

+0

можете ли вы опубликовать какой-то пример о том, как это сделать? ... Я пытался загружать разные способы, но он просто не работает ... странно что после прокрутки несколько раз все изображения загружаются должным образом: D – Nick

+0

Извините, я не могу, но я думаю, вам нужно сделать пару изменений в imageRetriever для вашего дела. Возможно, вы можете опубликовать эту полную часть своего проекта где-то –

+0

определил первое сообщение с классом ImageRetriever ... ваша помощь приветствуется! ..спасибо – Nick