From 861ddfe6fc1e8db349bc90fa10a94370daf8076d Mon Sep 17 00:00:00 2001 From: nostra13 Date: Sat, 2 Feb 2013 22:22:49 +0300 Subject: [PATCH] Avoided ImageLoader.getInstance() usages in the code (#158) Extracted ImageLoaderEngine from ImageLoader. Changed API: PauseOnScrollListener requires ImageLoader instance in constructor --- .../core/DisplayBitmapTask.java | 120 ++++++------- .../core/ImageLoader.java | 104 ++---------- .../core/ImageLoaderEngine.java | 142 ++++++++++++++++ .../core/LoadAndDisplayImageTask.java | 20 ++- .../core/assist/PauseOnScrollListener.java | 159 +++++++++--------- 5 files changed, 313 insertions(+), 232 deletions(-) create mode 100644 library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java diff --git a/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java b/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java index ace115302..a29c175ed 100644 --- a/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java +++ b/library/src/com/nostra13/universalimageloader/core/DisplayBitmapTask.java @@ -1,59 +1,61 @@ -package com.nostra13.universalimageloader.core; - -import com.nostra13.universalimageloader.core.assist.ImageLoadingListener; -import com.nostra13.universalimageloader.core.display.BitmapDisplayer; -import com.nostra13.universalimageloader.utils.L; - -import android.graphics.Bitmap; -import android.widget.ImageView; - -/** - * Displays bitmap in {@link ImageView}. Must be called on UI thread. - * - * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) - * @see ImageLoadingListener - * @see BitmapDisplayer - */ -final class DisplayBitmapTask implements Runnable { - - private static final String LOG_DISPLAY_IMAGE_IN_IMAGEVIEW = "Display image in ImageView [%s]"; - private static final String LOG_TASK_CANCELLED = "ImageView is reused for another image. Task is cancelled. [%s]"; - - private final Bitmap bitmap; - private final ImageView imageView; - private final String memoryCacheKey; - private final BitmapDisplayer bitmapDisplayer; - private final ImageLoadingListener listener; - - private boolean loggingEnabled; - - public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo) { - this.bitmap = bitmap; - imageView = imageLoadingInfo.imageView; - memoryCacheKey = imageLoadingInfo.memoryCacheKey; - bitmapDisplayer = imageLoadingInfo.options.getDisplayer(); - listener = imageLoadingInfo.listener; - } - - public void run() { - if (isViewWasReused()) { - if (loggingEnabled) L.i(LOG_TASK_CANCELLED, memoryCacheKey); - listener.onLoadingCancelled(); - } else { - if (loggingEnabled) L.i(LOG_DISPLAY_IMAGE_IN_IMAGEVIEW, memoryCacheKey); - Bitmap displayedBitmap = bitmapDisplayer.display(bitmap, imageView); - listener.onLoadingComplete(displayedBitmap); - ImageLoader.getInstance().cancelDisplayTask(imageView); - } - } - - /** Checks whether memory cache key (image URI) for current ImageView is actual */ - private boolean isViewWasReused() { - String currentCacheKey = ImageLoader.getInstance().getLoadingUriForView(imageView); - return !memoryCacheKey.equals(currentCacheKey); - } - - void setLoggingEnabled(boolean loggingEnabled) { - this.loggingEnabled = loggingEnabled; - } -} +package com.nostra13.universalimageloader.core; + +import com.nostra13.universalimageloader.core.assist.ImageLoadingListener; +import com.nostra13.universalimageloader.core.display.BitmapDisplayer; +import com.nostra13.universalimageloader.utils.L; + +import android.graphics.Bitmap; +import android.widget.ImageView; + +/** + * Displays bitmap in {@link ImageView}. Must be called on UI thread. + * + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) + * @see ImageLoadingListener + * @see BitmapDisplayer + */ +final class DisplayBitmapTask implements Runnable { + + private static final String LOG_DISPLAY_IMAGE_IN_IMAGEVIEW = "Display image in ImageView [%s]"; + private static final String LOG_TASK_CANCELLED = "ImageView is reused for another image. Task is cancelled. [%s]"; + + private final Bitmap bitmap; + private final ImageView imageView; + private final String memoryCacheKey; + private final BitmapDisplayer bitmapDisplayer; + private final ImageLoadingListener listener; + private final ImageLoaderEngine engine; + + private boolean loggingEnabled; + + public DisplayBitmapTask(Bitmap bitmap, ImageLoadingInfo imageLoadingInfo, ImageLoaderEngine engine) { + this.bitmap = bitmap; + imageView = imageLoadingInfo.imageView; + memoryCacheKey = imageLoadingInfo.memoryCacheKey; + bitmapDisplayer = imageLoadingInfo.options.getDisplayer(); + listener = imageLoadingInfo.listener; + this.engine = engine; + } + + public void run() { + if (isViewWasReused()) { + if (loggingEnabled) L.i(LOG_TASK_CANCELLED, memoryCacheKey); + listener.onLoadingCancelled(); + } else { + if (loggingEnabled) L.i(LOG_DISPLAY_IMAGE_IN_IMAGEVIEW, memoryCacheKey); + Bitmap displayedBitmap = bitmapDisplayer.display(bitmap, imageView); + listener.onLoadingComplete(displayedBitmap); + engine.cancelDisplayTaskFor(imageView); + } + } + + /** Checks whether memory cache key (image URI) for current ImageView is actual */ + private boolean isViewWasReused() { + String currentCacheKey = engine.getLoadingUriForView(imageView); + return !memoryCacheKey.equals(currentCacheKey); + } + + void setLoggingEnabled(boolean loggingEnabled) { + this.loggingEnabled = loggingEnabled; + } +} diff --git a/library/src/com/nostra13/universalimageloader/core/ImageLoader.java b/library/src/com/nostra13/universalimageloader/core/ImageLoader.java index e1151467d..cdae8b4af 100644 --- a/library/src/com/nostra13/universalimageloader/core/ImageLoader.java +++ b/library/src/com/nostra13/universalimageloader/core/ImageLoader.java @@ -1,18 +1,6 @@ package com.nostra13.universalimageloader.core; import java.lang.reflect.Field; -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; -import java.util.WeakHashMap; -import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ExecutorService; -import java.util.concurrent.Executors; -import java.util.concurrent.LinkedBlockingQueue; -import java.util.concurrent.ThreadPoolExecutor; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.locks.ReentrantLock; import android.content.Context; import android.graphics.Bitmap; @@ -27,9 +15,7 @@ import com.nostra13.universalimageloader.core.assist.ImageLoadingListener; import com.nostra13.universalimageloader.core.assist.ImageSize; import com.nostra13.universalimageloader.core.assist.MemoryCacheUtil; -import com.nostra13.universalimageloader.core.assist.QueueProcessingType; import com.nostra13.universalimageloader.core.assist.SimpleImageLoadingListener; -import com.nostra13.universalimageloader.core.assist.deque.LIFOLinkedBlockingDeque; import com.nostra13.universalimageloader.core.display.BitmapDisplayer; import com.nostra13.universalimageloader.core.display.FakeBitmapDisplayer; import com.nostra13.universalimageloader.utils.L; @@ -50,17 +36,11 @@ public class ImageLoader { private static final String LOG_LOAD_IMAGE_FROM_MEMORY_CACHE = "Load image from memory cache [%s]"; private ImageLoaderConfiguration configuration; - private ExecutorService imageLoadingExecutor; - private ExecutorService cachedImageLoadingExecutor; - private ExecutorService taskDistributor; + private ImageLoaderEngine engine; private final ImageLoadingListener emptyListener = new SimpleImageLoadingListener(); private final BitmapDisplayer fakeBitmapDisplayer = new FakeBitmapDisplayer(); - private final Map cacheKeysForImageViews = Collections.synchronizedMap(new HashMap()); - private final Map uriLocks = new WeakHashMap(); - private final AtomicBoolean paused = new AtomicBoolean(false); - private volatile static ImageLoader instance; /** Returns singleton class instance */ @@ -90,13 +70,14 @@ public synchronized void init(ImageLoaderConfiguration configuration) { throw new IllegalArgumentException(ERROR_INIT_CONFIG_WITH_NULL); } if (this.configuration == null) { + engine = new ImageLoaderEngine(configuration); this.configuration = configuration; } } /** - * Returns true - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with configuration}; - * false - otherwise + * Returns true - if ImageLoader {@linkplain #init(ImageLoaderConfiguration) is initialized with + * configuration}; false - otherwise */ public boolean isInited() { return configuration != null; @@ -170,7 +151,7 @@ public void displayImage(String uri, ImageView imageView, ImageLoadingListener l * @throws IllegalStateException if {@link #init(ImageLoaderConfiguration)} method wasn't called before * @throws IllegalArgumentException if passed imageView is null */ - public void displayImage(final String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) { + public void displayImage(String uri, ImageView imageView, DisplayImageOptions options, ImageLoadingListener listener) { checkConfiguration(); if (imageView == null) { throw new IllegalArgumentException(ERROR_WRONG_ARGUMENTS); @@ -183,7 +164,7 @@ public void displayImage(final String uri, ImageView imageView, DisplayImageOpti } if (uri == null || uri.length() == 0) { - cacheKeysForImageViews.remove(imageView.hashCode()); + engine.cancelDisplayTaskFor(imageView); listener.onLoadingStarted(); if (options.isShowImageForEmptyUri()) { imageView.setImageResource(options.getImageForEmptyUri()); @@ -196,7 +177,7 @@ public void displayImage(final String uri, ImageView imageView, DisplayImageOpti ImageSize targetSize = getImageSizeScaleTo(imageView); String memoryCacheKey = MemoryCacheUtil.generateKey(uri, targetSize); - cacheKeysForImageViews.put(imageView.hashCode(), memoryCacheKey); + engine.prepareDisplayTaskFor(imageView, memoryCacheKey); Bitmap bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp != null && !bmp.isRecycled()) { @@ -215,21 +196,9 @@ public void displayImage(final String uri, ImageView imageView, DisplayImageOpti } } - initExecutorsIfNeed(); - ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, options, listener, getLockForUri(uri)); - final LoadAndDisplayImageTask displayImageTask = new LoadAndDisplayImageTask(configuration, imageLoadingInfo, new Handler()); - - taskDistributor.submit(new Runnable() { - @Override - public void run() { - boolean isImageCachedOnDisc = configuration.discCache.get(uri).exists(); - if (isImageCachedOnDisc) { - cachedImageLoadingExecutor.submit(displayImageTask); - } else { - imageLoadingExecutor.submit(displayImageTask); - } - } - }); + ImageLoadingInfo imageLoadingInfo = new ImageLoadingInfo(uri, imageView, targetSize, options, listener, engine.getLockForUri(uri)); + final LoadAndDisplayImageTask displayImageTask = new LoadAndDisplayImageTask(engine, imageLoadingInfo, new Handler()); + engine.submit(displayImageTask); } } @@ -345,25 +314,6 @@ private void checkConfiguration() { } } - private void initExecutorsIfNeed() { - if (imageLoadingExecutor == null || imageLoadingExecutor.isShutdown()) { - imageLoadingExecutor = createTaskExecutor(); - } - if (cachedImageLoadingExecutor == null || cachedImageLoadingExecutor.isShutdown()) { - cachedImageLoadingExecutor = createTaskExecutor(); - } - if (taskDistributor == null || taskDistributor.isShutdown()) { - taskDistributor = Executors.newCachedThreadPool(); - } - } - - private ExecutorService createTaskExecutor() { - boolean lifo = configuration.tasksProcessingType == QueueProcessingType.LIFO; - BlockingQueue taskQueue = lifo ? new LIFOLinkedBlockingDeque() : new LinkedBlockingQueue(); - return new ThreadPoolExecutor(configuration.threadPoolSize, configuration.threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue, - configuration.displayImageThreadFactory); - } - /** * Returns memory cache * @@ -406,7 +356,7 @@ public void clearDiscCache() { /** Returns URI of image which is loading at this moment into passed {@link ImageView} */ public String getLoadingUriForView(ImageView imageView) { - return cacheKeysForImageViews.get(imageView.hashCode()); + return engine.getLoadingUriForView(imageView); } /** @@ -415,7 +365,7 @@ public String getLoadingUriForView(ImageView imageView) { * @param imageView {@link ImageView} for which display task will be cancelled */ public void cancelDisplayTask(ImageView imageView) { - cacheKeysForImageViews.remove(imageView.hashCode()); + engine.cancelDisplayTaskFor(imageView); } /** @@ -423,28 +373,17 @@ public void cancelDisplayTask(ImageView imageView) { * Already running tasks are not paused. */ public void pause() { - paused.set(true); + engine.pause(); } /** Resumes waiting "load&display" tasks */ public void resume() { - synchronized (paused) { - paused.set(false); - paused.notifyAll(); - } + engine.resume(); } /** Stops all running display image tasks, discards all other scheduled tasks */ public void stop() { - if (imageLoadingExecutor != null) { - imageLoadingExecutor.shutdownNow(); - } - if (cachedImageLoadingExecutor != null) { - cachedImageLoadingExecutor.shutdownNow(); - } - if (taskDistributor != null) { - taskDistributor.shutdownNow(); - } + engine.stop(); } /** @@ -487,17 +426,4 @@ private int getFieldValue(Object object, String fieldName) { } return value; } - - private ReentrantLock getLockForUri(String uri) { - ReentrantLock lock = uriLocks.get(uri); - if (lock == null) { - lock = new ReentrantLock(); - uriLocks.put(uri, lock); - } - return lock; - } - - AtomicBoolean getPause() { - return paused; - } } \ No newline at end of file diff --git a/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java b/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java new file mode 100644 index 000000000..4a081b572 --- /dev/null +++ b/library/src/com/nostra13/universalimageloader/core/ImageLoaderEngine.java @@ -0,0 +1,142 @@ +package com.nostra13.universalimageloader.core; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.WeakHashMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.locks.ReentrantLock; + +import android.widget.ImageView; + +import com.nostra13.universalimageloader.core.assist.QueueProcessingType; +import com.nostra13.universalimageloader.core.assist.deque.LIFOLinkedBlockingDeque; + +/** + * {@link ImageLoader} engine which responsible for {@linkplain LoadAndDisplayImageTask display task} execution. + * + * @author Sergey Tarasevich + * @created 01.02.2013 + */ +class ImageLoaderEngine { + + final ImageLoaderConfiguration configuration; + + private ExecutorService imageLoadingExecutor; + private ExecutorService cachedImageLoadingExecutor; + private ExecutorService taskDistributor; + + private final Map cacheKeysForImageViews = Collections.synchronizedMap(new HashMap()); + private final Map uriLocks = new WeakHashMap(); + + private final AtomicBoolean paused = new AtomicBoolean(false); + + ImageLoaderEngine(ImageLoaderConfiguration configuration) { + this.configuration = configuration; + } + + /** Submits display task to execution pool */ + void submit(final LoadAndDisplayImageTask task) { + initExecutorsIfNeed(); + taskDistributor.submit(new Runnable() { + @Override + public void run() { + boolean isImageCachedOnDisc = configuration.discCache.get(task.getLoadingUri()).exists(); + if (isImageCachedOnDisc) { + cachedImageLoadingExecutor.submit(task); + } else { + imageLoadingExecutor.submit(task); + } + } + }); + } + + private void initExecutorsIfNeed() { + if (imageLoadingExecutor == null || imageLoadingExecutor.isShutdown()) { + imageLoadingExecutor = createTaskExecutor(); + } + if (cachedImageLoadingExecutor == null || cachedImageLoadingExecutor.isShutdown()) { + cachedImageLoadingExecutor = createTaskExecutor(); + } + if (taskDistributor == null || taskDistributor.isShutdown()) { + taskDistributor = Executors.newCachedThreadPool(); + } + } + + private ExecutorService createTaskExecutor() { + boolean lifo = configuration.tasksProcessingType == QueueProcessingType.LIFO; + BlockingQueue taskQueue = lifo ? new LIFOLinkedBlockingDeque() : new LinkedBlockingQueue(); + return new ThreadPoolExecutor(configuration.threadPoolSize, configuration.threadPoolSize, 0L, TimeUnit.MILLISECONDS, taskQueue, + configuration.displayImageThreadFactory); + } + + /** Returns URI of image which is loading at this moment into passed {@link ImageView} */ + String getLoadingUriForView(ImageView imageView) { + return cacheKeysForImageViews.get(imageView.hashCode()); + } + + /** + * Associates memoryCacheKey with imageView. Then it helps to define image URI is loaded into + * ImageView at exact moment. + */ + void prepareDisplayTaskFor(ImageView imageView, String memoryCacheKey) { + cacheKeysForImageViews.put(imageView.hashCode(), memoryCacheKey); + } + + /** + * Cancels the task of loading and displaying image for incoming imageView. + * + * @param imageView {@link ImageView} for which display task will be cancelled + */ + void cancelDisplayTaskFor(ImageView imageView) { + cacheKeysForImageViews.remove(imageView.hashCode()); + } + + /** + * Pauses engine. All new "load&display" tasks won't be executed until ImageLoader is {@link #resume() resumed}.
+ * Already running tasks are not paused. + */ + void pause() { + paused.set(true); + } + + /** Resumes engine work. Paused "load&display" tasks will continue its work. */ + void resume() { + synchronized (paused) { + paused.set(false); + paused.notifyAll(); + } + } + + /** Stops all running display image tasks, discards all other scheduled tasks */ + void stop() { + if (imageLoadingExecutor != null) { + imageLoadingExecutor.shutdownNow(); + } + if (cachedImageLoadingExecutor != null) { + cachedImageLoadingExecutor.shutdownNow(); + } + if (taskDistributor != null) { + taskDistributor.shutdownNow(); + } + } + + ReentrantLock getLockForUri(String uri) { + ReentrantLock lock = uriLocks.get(uri); + if (lock == null) { + lock = new ReentrantLock(); + uriLocks.put(uri, lock); + } + return lock; + } + + AtomicBoolean getPause() { + return paused; + } +} diff --git a/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java b/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java index 701d24f67..bbc41432d 100644 --- a/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java +++ b/library/src/com/nostra13/universalimageloader/core/LoadAndDisplayImageTask.java @@ -52,11 +52,12 @@ final class LoadAndDisplayImageTask implements Runnable { private static final int ATTEMPT_COUNT_TO_DECODE_BITMAP = 3; private static final int BUFFER_SIZE = 8 * 1024; // 8 Kb - private final ImageLoaderConfiguration configuration; + private final ImageLoaderEngine engine; private final ImageLoadingInfo imageLoadingInfo; private final Handler handler; // Helper references + private final ImageLoaderConfiguration configuration; private final ImageDownloader downloader; private final boolean loggingEnabled; private final String uri; @@ -66,11 +67,12 @@ final class LoadAndDisplayImageTask implements Runnable { private final DisplayImageOptions options; private final ImageLoadingListener listener; - public LoadAndDisplayImageTask(ImageLoaderConfiguration configuration, ImageLoadingInfo imageLoadingInfo, Handler handler) { - this.configuration = configuration; + public LoadAndDisplayImageTask(ImageLoaderEngine engine, ImageLoadingInfo imageLoadingInfo, Handler handler) { + this.engine = engine; this.imageLoadingInfo = imageLoadingInfo; this.handler = handler; + configuration = engine.configuration; downloader = configuration.downloader; loggingEnabled = configuration.loggingEnabled; uri = imageLoadingInfo.uri; @@ -83,7 +85,7 @@ public LoadAndDisplayImageTask(ImageLoaderConfiguration configuration, ImageLoad @Override public void run() { - AtomicBoolean pause = ImageLoader.getInstance().getPause(); + AtomicBoolean pause = engine.getPause(); if (pause.get()) { synchronized (pause) { if (loggingEnabled) L.i(LOG_WAITING_FOR_RESUME, memoryCacheKey); @@ -123,7 +125,7 @@ public void run() { try { if (checkTaskIsNotActual()) return; - bmp = ImageLoader.getInstance().getMemoryCache().get(memoryCacheKey); + bmp = configuration.memoryCache.get(memoryCacheKey); if (bmp == null) { bmp = tryLoadBitmap(); if (bmp == null) return; @@ -144,7 +146,7 @@ public void run() { if (checkTaskIsNotActual() || checkTaskIsInterrupted()) return; - DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo); + DisplayBitmapTask displayBitmapTask = new DisplayBitmapTask(bmp, imageLoadingInfo, engine); displayBitmapTask.setLoggingEnabled(loggingEnabled); handler.post(displayBitmapTask); } @@ -154,7 +156,7 @@ public void run() { * moment and fire {@link ImageLoadingListener#onLoadingCancelled()} event if it doesn't. */ private boolean checkTaskIsNotActual() { - String currentCacheKey = ImageLoader.getInstance().getLoadingUriForView(imageView); + String currentCacheKey = engine.getLoadingUriForView(imageView); // Check whether memory cache key (image URI) for current ImageView is actual. // If ImageView is reused for another task then current task should be cancelled. boolean imageViewWasReused = !memoryCacheKey.equals(currentCacheKey); @@ -321,4 +323,8 @@ public void run() { }); } } + + String getLoadingUri() { + return uri; + } } diff --git a/library/src/com/nostra13/universalimageloader/core/assist/PauseOnScrollListener.java b/library/src/com/nostra13/universalimageloader/core/assist/PauseOnScrollListener.java index 0acd5573b..ee3ab2f7f 100644 --- a/library/src/com/nostra13/universalimageloader/core/assist/PauseOnScrollListener.java +++ b/library/src/com/nostra13/universalimageloader/core/assist/PauseOnScrollListener.java @@ -1,77 +1,82 @@ -package com.nostra13.universalimageloader.core.assist; - -import android.widget.AbsListView; -import android.widget.AbsListView.OnScrollListener; -import android.widget.GridView; -import android.widget.ListView; - -import com.nostra13.universalimageloader.core.ImageLoader; - -/** - * Listener-helper for {@linkplain AbsListView list views} ({@link ListView}, {@link GridView}) which can - * {@linkplain ImageLoader#pause() pause ImageLoader's tasks} while list view is scrolling (touch scrolling and/or - * fling). It prevents redundant loadings.
- * Set it to your list view's {@link AbsListView#setOnScrollListener(OnScrollListener) setOnScrollListener(...)}.
- * This listener can wrap your custom {@linkplain OnScrollListener listener}. - * - * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) - */ -public class PauseOnScrollListener implements OnScrollListener { - - private final boolean pauseOnScroll; - private final boolean pauseOnFling; - private final OnScrollListener externalListener; - - /** - * Constructor - * - * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling - * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling - */ - public PauseOnScrollListener(boolean pauseOnScroll, boolean pauseOnFling) { - this(pauseOnScroll, pauseOnFling, null); - } - - /** - * Constructor - * - * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling - * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling - * @param customListener Your custom {@link OnScrollListener} for {@linkplain AbsListView list view} which also will - * be get scroll events - */ - public PauseOnScrollListener(boolean pauseOnScroll, boolean pauseOnFling, OnScrollListener customListener) { - this.pauseOnScroll = pauseOnScroll; - this.pauseOnFling = pauseOnFling; - externalListener = customListener; - } - - @Override - public void onScrollStateChanged(AbsListView view, int scrollState) { - switch (scrollState) { - case OnScrollListener.SCROLL_STATE_IDLE: - ImageLoader.getInstance().resume(); - break; - case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: - if (pauseOnScroll) { - ImageLoader.getInstance().pause(); - } - break; - case OnScrollListener.SCROLL_STATE_FLING: - if (pauseOnFling) { - ImageLoader.getInstance().pause(); - } - break; - } - if (externalListener != null) { - externalListener.onScrollStateChanged(view, scrollState); - } - } - - @Override - public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { - if (externalListener != null) { - externalListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); - } - } -} +package com.nostra13.universalimageloader.core.assist; + +import android.widget.AbsListView; +import android.widget.AbsListView.OnScrollListener; +import android.widget.GridView; +import android.widget.ListView; + +import com.nostra13.universalimageloader.core.ImageLoader; + +/** + * Listener-helper for {@linkplain AbsListView list views} ({@link ListView}, {@link GridView}) which can + * {@linkplain ImageLoader#pause() pause ImageLoader's tasks} while list view is scrolling (touch scrolling and/or + * fling). It prevents redundant loadings.
+ * Set it to your list view's {@link AbsListView#setOnScrollListener(OnScrollListener) setOnScrollListener(...)}.
+ * This listener can wrap your custom {@linkplain OnScrollListener listener}. + * + * @author Sergey Tarasevich (nostra13[at]gmail[dot]com) + */ +public class PauseOnScrollListener implements OnScrollListener { + + private ImageLoader imageLoader; + + private final boolean pauseOnScroll; + private final boolean pauseOnFling; + private final OnScrollListener externalListener; + + /** + * Constructor + * + * @param imageLoader {@linkplain ImageLoader} instance for controlling + * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling + * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling + */ + public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling) { + this(imageLoader, pauseOnScroll, pauseOnFling, null); + } + + /** + * Constructor + * + * @param imageLoader {@linkplain ImageLoader} instance for controlling + * @param pauseOnScroll Whether {@linkplain ImageLoader#pause() pause ImageLoader} during touch scrolling + * @param pauseOnFling Whether {@linkplain ImageLoader#pause() pause ImageLoader} during fling + * @param customListener Your custom {@link OnScrollListener} for {@linkplain AbsListView list view} which also will + * be get scroll events + */ + public PauseOnScrollListener(ImageLoader imageLoader, boolean pauseOnScroll, boolean pauseOnFling, OnScrollListener customListener) { + this.imageLoader = imageLoader; + this.pauseOnScroll = pauseOnScroll; + this.pauseOnFling = pauseOnFling; + externalListener = customListener; + } + + @Override + public void onScrollStateChanged(AbsListView view, int scrollState) { + switch (scrollState) { + case OnScrollListener.SCROLL_STATE_IDLE: + imageLoader.resume(); + break; + case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: + if (pauseOnScroll) { + imageLoader.pause(); + } + break; + case OnScrollListener.SCROLL_STATE_FLING: + if (pauseOnFling) { + imageLoader.pause(); + } + break; + } + if (externalListener != null) { + externalListener.onScrollStateChanged(view, scrollState); + } + } + + @Override + public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { + if (externalListener != null) { + externalListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); + } + } +}