2011-05-16 85 views
4

我使用谷歌的ImageDownloader所看到在他们的多线程性能博客文章:http://android-developers.blogspot.com/2010/07/multithreading-for-performance.html如何指定占位符图像?

我希望能够指定一个默认的占位符图像,而图像被下载了互联网。我怎样才能做到这一点?

下面是代码:

/** 
* This helper class download images from the Internet and binds those with the provided ImageView. 
* 
* <p>It requires the INTERNET permission, which should be added to your application's manifest 
* file.</p> 
* 
* A local cache of downloaded images is maintained internally to improve performance. 
*/ 
public class ImageDownloader { 
    private static final String LOG_TAG = "ImageDownloader"; 

    public enum Mode { NO_ASYNC_TASK, NO_DOWNLOADED_DRAWABLE, CORRECT } 
    private Mode mode = Mode.CORRECT; //Mode.NO_ASYNC_TASK; 

    /** 
    * Download the specified image from the Internet and binds it to the provided ImageView. The 
    * binding is immediate if the image is found in the cache and will be done asynchronously 
    * otherwise. A null bitmap will be associated to the ImageView if an error occurs. 
    * 
    * @param url The URL of the image to download. 
    * @param imageView The ImageView to bind the downloaded image to. 
    */ 
    public void download(String url, ImageView imageView) { 
     resetPurgeTimer(); 
     Bitmap bitmap = getBitmapFromCache(url); 

     if (bitmap == null) { 
      forceDownload(url, imageView); 
     } else { 
      cancelPotentialDownload(url, imageView); 
      imageView.setImageBitmap(bitmap); 
     } 
    } 

    /* 
    * Same as download but the image is always downloaded and the cache is not used. 
    * Kept private at the moment as its interest is not clear. 
     private void forceDownload(String url, ImageView view) { 
      forceDownload(url, view, null); 
     } 
    */ 

    /** 
    * Same as download but the image is always downloaded and the cache is not used. 
    * Kept private at the moment as its interest is not clear. 
    */ 
    private void forceDownload(String url, ImageView imageView) { 
     // State sanity: url is guaranteed to never be null in DownloadedDrawable and cache keys. 
     if (url == null) { 
      imageView.setImageDrawable(null); 
      return; 
     } 

     if (cancelPotentialDownload(url, imageView)) { 
      switch (mode) { 
       case NO_ASYNC_TASK: 
        Bitmap bitmap = downloadBitmap(url); 
        addBitmapToCache(url, bitmap); 
        imageView.setImageBitmap(bitmap); 
        break; 

       case NO_DOWNLOADED_DRAWABLE: 
        imageView.setMinimumHeight(156); 
        BitmapDownloaderTask task = new BitmapDownloaderTask(imageView); 
        task.execute(url); 
        break; 

       case CORRECT: 
        task = new BitmapDownloaderTask(imageView); 
        DownloadedDrawable downloadedDrawable = new DownloadedDrawable(task); 
        imageView.setImageDrawable(downloadedDrawable); 
        imageView.setMinimumHeight(156); 
        task.execute(url); 
        break; 
      } 
     } 
    } 

    /** 
    * Returns true if the current download has been canceled or if there was no download in 
    * progress on this image view. 
    * Returns false if the download in progress deals with the same url. The download is not 
    * stopped in that case. 
    */ 
    private static boolean cancelPotentialDownload(String url, ImageView imageView) { 
     BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 

     if (bitmapDownloaderTask != null) { 
      String bitmapUrl = bitmapDownloaderTask.url; 
      if ((bitmapUrl == null) || (!bitmapUrl.equals(url))) { 
       bitmapDownloaderTask.cancel(true); 
      } else { 
       // The same URL is already being downloaded. 
       return false; 
      } 
     } 
     return true; 
    } 

    /** 
    * @param imageView Any imageView 
    * @return Retrieve the currently active download task (if any) associated with this imageView. 
    * null if there is no such task. 
    */ 
    private static BitmapDownloaderTask getBitmapDownloaderTask(ImageView imageView) { 
     if (imageView != null) { 
      Drawable drawable = imageView.getDrawable(); 
      if (drawable instanceof DownloadedDrawable) { 
       DownloadedDrawable downloadedDrawable = (DownloadedDrawable)drawable; 
       return downloadedDrawable.getBitmapDownloaderTask(); 
      } 
     } 
     return null; 
    } 

    Bitmap downloadBitmap(String url) { 
     //final int IO_BUFFER_SIZE = 4 * 1024; 

     // AndroidHttpClient is not allowed to be used from the main thread 
     final HttpClient client = (mode == Mode.NO_ASYNC_TASK) ? new DefaultHttpClient() : 
      AndroidHttpClient.newInstance("Android"); 
     final HttpGet getRequest = new HttpGet(url); 

     try { 
      HttpResponse response = client.execute(getRequest); 
      final int statusCode = response.getStatusLine().getStatusCode(); 
      if (statusCode != HttpStatus.SC_OK) { 
       Log.w("ImageDownloader", "Error " + statusCode + 
         " while retrieving bitmap from " + url); 
       return null; 
      } 

      final HttpEntity entity = response.getEntity(); 
      if (entity != null) { 
       InputStream inputStream = null; 
       try { 
        inputStream = entity.getContent(); 
        // return BitmapFactory.decodeStream(inputStream); 
        // Bug on slow connections, fixed in future release. 
        return BitmapFactory.decodeStream(new FlushedInputStream(inputStream)); 
       } finally { 
        if (inputStream != null) { 
         inputStream.close(); 
        } 
        entity.consumeContent(); 
       } 
      } 
     } catch (IOException e) { 
      getRequest.abort(); 
      Log.w(LOG_TAG, "I/O error while retrieving bitmap from " + url, e); 
     } catch (IllegalStateException e) { 
      getRequest.abort(); 
      Log.w(LOG_TAG, "Incorrect URL: " + url); 
     } catch (Exception e) { 
      getRequest.abort(); 
      Log.w(LOG_TAG, "Error while retrieving bitmap from " + url, e); 
     } finally { 
      if ((client instanceof AndroidHttpClient)) { 
       ((AndroidHttpClient) client).close(); 
      } 
     } 
     return null; 
    } 

    /* 
    * An InputStream that skips the exact number of bytes provided, unless it reaches EOF. 
    */ 
    static class FlushedInputStream extends FilterInputStream { 
     public FlushedInputStream(InputStream inputStream) { 
      super(inputStream); 
     } 

     @Override 
     public long skip(long n) throws IOException { 
      long totalBytesSkipped = 0L; 
      while (totalBytesSkipped < n) { 
       long bytesSkipped = in.skip(n - totalBytesSkipped); 
       if (bytesSkipped == 0L) { 
        int b = read(); 
        if (b < 0) { 
         break; // we reached EOF 
        } else { 
         bytesSkipped = 1; // we read one byte 
        } 
       } 
       totalBytesSkipped += bytesSkipped; 
      } 
      return totalBytesSkipped; 
     } 
    } 

    /** 
    * The actual AsyncTask that will asynchronously download the image. 
    */ 
    class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> { 
     private String url; 
     private final WeakReference<ImageView> imageViewReference; 

     public BitmapDownloaderTask(ImageView imageView) { 
      imageViewReference = new WeakReference<ImageView>(imageView); 
     } 

     /** 
     * Actual download method. 
     */ 
     @Override 
     protected Bitmap doInBackground(String... params) { 
      url = params[0]; 
      return downloadBitmap(url); 
     } 

     /** 
     * Once the image is downloaded, associates it to the imageView 
     */ 
     @Override 
     protected void onPostExecute(Bitmap bitmap) { 
      if (isCancelled()) { 
       bitmap = null; 
      } 

      addBitmapToCache(url, bitmap); 

      if (imageViewReference != null) { 
       ImageView imageView = imageViewReference.get(); 
       BitmapDownloaderTask bitmapDownloaderTask = getBitmapDownloaderTask(imageView); 
       // Change bitmap only if this process is still associated with it 
       // Or if we don't use any bitmap to task association (NO_DOWNLOADED_DRAWABLE mode) 
       if ((this == bitmapDownloaderTask) || (mode != Mode.CORRECT)) { 
        imageView.setImageBitmap(bitmap); 
       } 
      } 
     } 
    } 


    /** 
    * A fake Drawable that will be attached to the imageView while the download is in progress. 
    * 
    * <p>Contains a reference to the actual download task, so that a download task can be stopped 
    * if a new binding is required, and makes sure that only the last started download process can 
    * bind its result, independently of the download finish order.</p> 
    */ 
    static class DownloadedDrawable extends ColorDrawable { 
     private final WeakReference<BitmapDownloaderTask> bitmapDownloaderTaskReference; 

     public DownloadedDrawable(BitmapDownloaderTask bitmapDownloaderTask) { 
      super(Color.BLACK); 
      bitmapDownloaderTaskReference = 
       new WeakReference<BitmapDownloaderTask>(bitmapDownloaderTask); 
     } 

     public BitmapDownloaderTask getBitmapDownloaderTask() { 
      return bitmapDownloaderTaskReference.get(); 
     } 
    } 

    public void setMode(Mode mode) { 
     this.mode = mode; 
     clearCache(); 
    } 


    /* 
    * Cache-related fields and methods. 
    * 
    * We use a hard and a soft cache. A soft reference cache is too aggressively cleared by the 
    * Garbage Collector. 
    */ 

    private static final int HARD_CACHE_CAPACITY = 10; 
    private static final int DELAY_BEFORE_PURGE = 10 * 1000; // in milliseconds 

    // Hard cache, with a fixed maximum capacity and a life duration 
    private final HashMap<String, Bitmap> sHardBitmapCache = 
     new LinkedHashMap<String, Bitmap>(HARD_CACHE_CAPACITY/2, 0.75f, true) { 
     /** 
      * 
      */ 
      private static final long serialVersionUID = 1L; 

     @Override 
     protected boolean removeEldestEntry(LinkedHashMap.Entry<String, Bitmap> eldest) { 
      if (size() > HARD_CACHE_CAPACITY) { 
       // Entries push-out of hard reference cache are transferred to soft reference cache 
       sSoftBitmapCache.put(eldest.getKey(), new SoftReference<Bitmap>(eldest.getValue())); 
       return true; 
      } else 
       return false; 
     } 
    }; 

    // Soft cache for bitmaps kicked out of hard cache 
    private final static ConcurrentHashMap<String, SoftReference<Bitmap>> sSoftBitmapCache = 
     new ConcurrentHashMap<String, SoftReference<Bitmap>>(HARD_CACHE_CAPACITY/2); 

    private final Handler purgeHandler = new Handler(); 

    private final Runnable purger = new Runnable() { 
     public void run() { 
      clearCache(); 
     } 
    }; 

    /** 
    * Adds this bitmap to the cache. 
    * @param bitmap The newly downloaded bitmap. 
    */ 
    private void addBitmapToCache(String url, Bitmap bitmap) { 
     if (bitmap != null) { 
      synchronized (sHardBitmapCache) { 
       sHardBitmapCache.put(url, bitmap); 
      } 
     } 
    } 

    /** 
    * @param url The URL of the image that will be retrieved from the cache. 
    * @return The cached bitmap or null if it was not found. 
    */ 
    public Bitmap getBitmapFromCache(String url) { 
     // First try the hard reference cache 
     synchronized (sHardBitmapCache) { 
      final Bitmap bitmap = sHardBitmapCache.get(url); 
      if (bitmap != null) { 
       // Bitmap found in hard cache 
       // Move element to first position, so that it is removed last 
       sHardBitmapCache.remove(url); 
       sHardBitmapCache.put(url, bitmap); 
       return bitmap; 
      } 
     } 

     // Then try the soft reference cache 
     SoftReference<Bitmap> bitmapReference = sSoftBitmapCache.get(url); 
     if (bitmapReference != null) { 
      final Bitmap bitmap = bitmapReference.get(); 
      if (bitmap != null) { 
       // Bitmap found in soft cache 
       return bitmap; 
      } else { 
       // Soft reference has been Garbage Collected 
       sSoftBitmapCache.remove(url); 
      } 
     } 

     return null; 
    } 

    /** 
    * Clears the image cache used internally to improve performance. Note that for memory 
    * efficiency reasons, the cache will automatically be cleared after a certain inactivity delay. 
    */ 
    public void clearCache() { 
     sHardBitmapCache.clear(); 
     sSoftBitmapCache.clear(); 
    } 

    /** 
    * Allow a new delay before the automatic cache clear is done. 
    */ 
    private void resetPurgeTimer() { 
     purgeHandler.removeCallbacks(purger); 
     purgeHandler.postDelayed(purger, DELAY_BEFORE_PURGE); 
    } 
} 

回答

4

设置ImageView的占位符download()调用forceDownload()之前。

+0

我接着说:imageView.setImageResource(R.drawable.icon);右'在forceDownload()之前,图像加载后图像背景是黑色的,一旦它完成,它显示了下载图像的占位符... – 2011-05-18 10:44:53

+2

@Sheehan Alam:'Ima geView'不支持叠加。你在一个地方更新背景,在另一个地方更新前景。 – CommonsWare 2011-05-18 10:47:23

+0

我还将代码更改为'imageView.setImageBitmap(placeholderBitmap)',结果相同。我不确定背景和前景在哪里设置? – 2011-05-18 11:23:45

0

我不确定我完全理解你的问题,但是这里有一些代码,可能会对你有所帮助。

public class Downloader { 
private ImageView someImageView; 

    public Downloader() { 
     someImageView = new ImageView(getApplicationContext()); 
     new BitmapDownloaderTask.execute("http://url/to/your/image.jpg") 
    } 

    class BitmapDownloaderTask extends AsyncTask<String, Void, Bitmap> { 
     @Override 
     protected onPreExecute() { 
      someImageView.setImageBitmap(getResources().getDrawable(R.drawable.placeholder)); 
     } 
     @Override 
     protected Bitmap doInBackground(String... params) { 
      return downloadBitmap(params[0]); 
     } 

     @Override 
     protected void onPostExecute(Bitmap bitmap) { 
      someImageView.setImageBitmap(bitmap); 
     } 
    } 
} 
3

首先解码你想放置的位图。将它传递给扩展BitmapDrawable的DownloadedDrawable。当我创建类时,我用有效的上下文填充mContext。

生效下载

Bitmap bitmap = BitmapFactory.decodeResource(mContext.getResources(), R.resource.placeholder); 

imageView.setImageDrawable(new DownloadedDrawable(mContext, handler, bitmap)); 

的downloadedDrawable类:

static class DownloadedDrawable extends BitmapDrawable { 
    private final SoftReference<BitmapDownloaderTask> bitmapDownloaderTaskReference; 

    public DownloadedDrawable(Context context, BitmapDownloaderTask bitmapDownloaderTask, Bitmap bitmap) { 
     super(context.getResources(), bitmap); 

     bitmapDownloaderTaskReference = 
       new SoftReference<BitmapDownloaderTask>(bitmapDownloaderTask); 
    } 

    public BinaryHttpResponseHandler get BitmapDownloaderTask() { 
     return bitmapDownloaderTaskReference.get(); 
    } 
} 

希望它能帮助:>