Android Open Source - RZAndroidBaseUtils Base Async Drawable Task






From Project

Back to project page RZAndroidBaseUtils.

License

The source code is released under:

MIT License

If you think the Android project RZAndroidBaseUtils listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

package com.raizlabs.graphics.drawable.async;
/*from  w  w  w.  ja  v a2  s .  com*/
import java.lang.ref.WeakReference;

import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.view.View;
import android.widget.ImageView;

import com.raizlabs.baseutils.ThreadingUtils;
import com.raizlabs.events.EventListener;

/**
 * Base class which can be used for easy implementation of an
 * {@link AsyncDrawableTask}. Allows for different {@link Drawable}s
 * to be shown for the cancelled and loading states.
 * 
 * @author Dylan James
 *
 * @param <T> The type of the key used to identify an {@link AsyncDrawableTask}
 * and whether it is trying to accomplish the same thing as another instance.
 */
public abstract class BaseAsyncDrawableTask<T> implements AsyncDrawableTask<T> {
  
  private EventListener<Drawable> completedListener;
  /**
   * Sets an {@link EventListener} to be called when the execution of this task
   * is complete.
   * @param listener The listener to call
   */
  public void setCompletedListener(EventListener<Drawable> listener) {
    this.completedListener = listener;
  }
  
  private boolean cancelled = false;
  @Override
  public boolean isCancelled() {
    synchronized (this) {
      return cancelled;
    }
  }

  private boolean completed = false;
  @Override
  public boolean isCompleted() {
    synchronized (this) {
      return completed;
    }
  }
  
  @Override
  public void cancel() {
    synchronized (this) {
      cancelled = true;
      // If this task isn't finished, update the drawable
      // to the cancelled drawable
      if (!completed && wrapper.getView() != null) {
        wrapper.update(getCancelledDrawable(), true);
      }
    }
    onCancel();
  }
  
  /**
   * Wrapper for our current view to abstract the type
   */
  private ViewWrapper wrapper;
  protected View getView() { return wrapper.getView(); }
  
  /**
   * Constructs a {@link BaseAsyncDrawableTask} which binds to the given
   * {@link ImageView}.
   * @param imageView The image view to bind to.
   */
  public BaseAsyncDrawableTask(ImageView imageView) {
    // Wrap the view up with a wrapper which sets the image
    wrapper = new ImageWrapper(imageView);
  }
  
  /**
   * Constructs a {@link BaseAsyncDrawableTask} which binds to the given
   * {@link View}
   * @param view The view to bind to.
   */
  public BaseAsyncDrawableTask(View view) {
    // Wrap the view up with a wrapper which sets the background
    wrapper = new BackgroundWrapper(view);
  }
  
  @Override
  public boolean bind() {
    // Attempt to cancel the existing work
    boolean cancelled = wrapper.cancelExistingWork();
    // If we cancelled the work, set the loading drawable
    if (cancelled) {
      // Default to the bind drawable
      Drawable drawable = onBind();
      // If no drawable was given, fall back to the loading drawable
      // and we are not done
      if (drawable == null) {
        drawable = getLoadingDrawable();
        completed = false;
      } else {
        // We're done early!
        completed = true;
      }
      // Update the view
      wrapper.update(drawable, false);
    }
    return cancelled;
  }
  
  @Override
  public void execute() {
    // If we're already completed, break early
    if (isCompleted()) return;
    // If our view is emptied, we can't do anything, abort
    if (wrapper.getView() == null) return;
    Drawable drawable = null;
    // If we aren't bound to the view, abort
    if (!wrapper.isBoundToView()) return;
    // If we aren't cancelled, start up the work 
    if (!isCancelled()) {
      drawable = doExecute();
      // Work has completed...
      synchronized (this) {
        // If we've been cancelled during the work, set the cancelled
        // drawable
        if (cancelled) {
          drawable = getCancelledDrawable();
        } else {
          // Otherwise we completed successfully
          completed = true;
        }
      }
    } else {
      // We were cancelled, set the cancelled drawable
      drawable = getCancelledDrawable();
    }
    
    // Set our new drawable
    setDrawable(drawable);
    
    if (completedListener != null) completedListener.onEvent(this, drawable);
  }

  /**
   * Call this when a {@link Drawable} is loaded as the target image. This will set
   * the {@link Drawable} if we are still bound and haven't been cancelled yet.
   * @param drawable The {@link Drawable} to set.
   */
  protected void onDrawableLoaded(Drawable drawable) {
    synchronized (this) {
      if (!isCancelled()) {
        completed = true;
        setDrawable(drawable);
        
        if (completedListener != null) completedListener.onEvent(this, drawable);
      }
    }
  }
  
  /**
   * Sets the current image to the given {@link Drawable} if we are still bound
   * @param drawable The {@link Drawable} to set
   */
  protected void setDrawable(Drawable drawable) {
    // Push our new drawable through the wrapper, and ensure we are still bound
    wrapper.update(drawable, true);
  }
  
  /**
   * Interface which allows us to update different views polymorphically.
   * Wraps views of different types and interfaces with them appropriately.
   */
  private interface ViewWrapper {
    /**
     * Updates the wrapped view to the given {@link Drawable}.
     * @param drawable The Drawable to update the view to show.
     * @param checkIfBound True to double check that the view is still
     * bound to our task
     */
    void update(Drawable drawable, boolean checkIfBound);
    /**
     * Attempts to cancel the existing work associated with the view
     * @see AsyncDrawableUtils#cancelExistingWork(AsyncDrawableTask, ImageView)
     * @see AsyncDrawableUtils#cancelExistingWork(AsyncDrawableTask, View)
     * @return True if there was no work or the work was cancelled, false if
     * the work is already assigned to another task
     */
    boolean cancelExistingWork();
    /**
     * @return The view being wrapped by this wrapper.
     */
    View getView();
    /**
     * @return True if this task is still associated with the wrapped view.
     */
    boolean isBoundToView();
  }
  
  /**
   * {@link ViewWrapper} implementation which interfaces with the background
   * of a given view.
   */
  private class BackgroundWrapper implements ViewWrapper {
    WeakReference<View> viewRef;
    /**
     * Constructs a {@link BackgroundWrapper} which wraps the given View.
     * @param view The View to wrap.
     */
    public BackgroundWrapper(View view) {
      viewRef = new WeakReference<View>(view);
    }
    
    @Override
    public View getView() {
      return viewRef.get();
    }
    
    @Override
    public boolean cancelExistingWork() {
      final View view = getView();
      if (view == null) return false;
      return AsyncDrawableUtils.cancelExistingWork(BaseAsyncDrawableTask.this, view);
    }
    
    @SuppressWarnings("deprecation")
    @SuppressLint("NewApi")
    @Override
    public void update(Drawable drawable, final boolean checkIfBound) {
      final View view = viewRef.get();
      if (view != null) {
        // If we were told to check, only proceed if we are bound to the view
        if (!checkIfBound || isBoundToView(view)) {
          // Wrap the desired drawable in a wrapper that contains the information
          // about this task
          final AsyncDrawableWrapper<T> newDrawable = 
              new AsyncDrawableWrapper<T>(drawable, BaseAsyncDrawableTask.this);
          // Change the drawable on the UI thread
          ThreadingUtils.runOnUIThread(new Runnable() {
            @Override
            public void run() {
              // Double check that we're still the bound task
              if (!checkIfBound || isBoundToView(view)) {
                // JellyBean+ deprecates setBackgroundDrawable
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                  view.setBackground(newDrawable);
                } else {
                  view.setBackgroundDrawable(newDrawable);
                }
                onDrawableUpdated(newDrawable.getDrawble());
              }
            }
          });
        }
      }
    }

    @Override
    public boolean isBoundToView() {
      final View view = getView();
      if (view == null) return false;
      return isBoundToView(view);
    }
    
    boolean isBoundToView(View view) {
      // We're bound if our task is the associated task
      return BaseAsyncDrawableTask.this.equals(AsyncDrawableUtils.getTask(view));
    }
  }
  
  /**
   * {@link ViewWrapper} implementation which interfaces with the image
   * drawable of a given ImageView.
   */
  private class ImageWrapper implements ViewWrapper {
    WeakReference<ImageView> viewRef;
    /**
     * Constructs a {@link BackgroundWrapper} which wraps the given ImageView.
     * @param imageView The ImageView to wrap.
     */
    public ImageWrapper(ImageView imageView) {
      viewRef = new WeakReference<ImageView>(imageView);
    }
    
    @Override
    public View getView() {
      return viewRef.get();
    }
    
    @Override
    public boolean cancelExistingWork() {
      ImageView view = viewRef.get();
      if (view == null) return false;
      return AsyncDrawableUtils.cancelExistingWork(BaseAsyncDrawableTask.this, view);
    }
    
    @Override
    public void update(Drawable drawable, final boolean checkIfBound) {
      final ImageView view = viewRef.get();
      if (view != null) {
        // If we were told to check, only proceed if we are bound to the view
        if (!checkIfBound || isBoundToView(view)) {
          // Wrap the desired drawable in a wrapper that contains the information
          // about this task
          final AsyncDrawableWrapper<T> newDrawable = 
              new AsyncDrawableWrapper<T>(drawable, BaseAsyncDrawableTask.this);
          // Change the drawable on the UI thread
          ThreadingUtils.runOnUIThread(new Runnable() {
            @Override
            public void run() {
              // Double check that we're still the bound task
              if (!checkIfBound ||isBoundToView(view)) {
                view.setImageDrawable(newDrawable);
                onDrawableUpdated(newDrawable.getDrawble());
              }
            }
          });
        }
      }
    }
    
    @Override
    public boolean isBoundToView() {
      ImageView view = viewRef.get();
      if (view == null) return false;
      return isBoundToView(view);
    }
    
    boolean isBoundToView(ImageView imageView) {
      // We're bound if our task is the associated task
      return BaseAsyncDrawableTask.this.equals(AsyncDrawableUtils.getTask(imageView));
    }
  }
  
  /**
   * Called when the drawable for the view is updated. This will be called on the UI thread.
   * @param drawable The drawable which was committed.
   */
  protected void onDrawableUpdated(Drawable drawable) { }
  
  /**
   * Called when this task is bound to a view. You may pass a Drawable back
   * to complete early, though this is run from the UI thread. A good case
   * for this is if an image is already cached in memory or something simliar.
   * @return The completed drawable if it can be obtained quickly, otherwise
   * null.
   */
  protected abstract Drawable onBind();
  /**
   * Called to do long running work and obtain the desired Drawable.
   * @return The resulting Drawable.
   */
  protected abstract Drawable doExecute();
  /**
   * Called to get a Drawable to display while the task is running.
   * @return The Drawable to display.
   */
  protected abstract Drawable getLoadingDrawable();
  /**
   * Called to get a Drawable to display when the task is cancelled.
   * @return The Drawable to display.
   */
  protected abstract Drawable getCancelledDrawable();
  /**
   * Called when the task is cancelled.
   */
  protected abstract void onCancel();
}




Java Source Code List

com.raizlabs.baseutils.CompatibilityUtils.java
com.raizlabs.baseutils.IOUtils.java
com.raizlabs.baseutils.Logger.java
com.raizlabs.baseutils.Math.java
com.raizlabs.baseutils.StringUtils.java
com.raizlabs.baseutils.ThreadingUtils.java
com.raizlabs.baseutils.Wrapper.java
com.raizlabs.baseutils.examples.MainActivity.java
com.raizlabs.baseutils.examples.asyncdrawable.AsyncDrawableExampleActivity.java
com.raizlabs.baseutils.examples.asyncdrawable.AsyncDrawableListExampleActivity.java
com.raizlabs.baseutils.examples.simplegenericadapter.SimpleGenericAdapterExampleActivity.java
com.raizlabs.baseutils.examples.viewgroupadapter.ViewGroupAdapterExampleActivity.java
com.raizlabs.baseutils.examples.viewholderstrategy.SimpleViewHolderStrategyExampleActivity.java
com.raizlabs.collections.ListUtils.java
com.raizlabs.collections.MappableSet.java
com.raizlabs.collections.TransactionalHashSet.java
com.raizlabs.concurrent.BasePrioritizedRunnable.java
com.raizlabs.concurrent.ConcurrencyUtils.java
com.raizlabs.concurrent.PrioritizedRunnable.java
com.raizlabs.concurrent.Prioritized.java
com.raizlabs.content.sharing.SharingUtils.java
com.raizlabs.database.CursorIterable.java
com.raizlabs.database.CursorIterator.java
com.raizlabs.events.EventListener.java
com.raizlabs.events.Event.java
com.raizlabs.events.ProgressListener.java
com.raizlabs.events.SimpleEventListener.java
com.raizlabs.functions.Delegate.java
com.raizlabs.functions.Predicate.java
com.raizlabs.functions.Provider.java
com.raizlabs.graphics.ImageFactory.java
com.raizlabs.graphics.drawable.async.AsyncDrawableTask.java
com.raizlabs.graphics.drawable.async.AsyncDrawableUtils.java
com.raizlabs.graphics.drawable.async.AsyncDrawableWrapper.java
com.raizlabs.graphics.drawable.async.AsyncDrawable.java
com.raizlabs.graphics.drawable.async.BaseAsyncDrawableTask.java
com.raizlabs.imagecaching.ImageCache.java
com.raizlabs.imagecaching.PrefixedImageCacheAdapter.java
com.raizlabs.imagecaching.StubImageCache.java
com.raizlabs.json.JSONArrayParserDelegate.java
com.raizlabs.json.JSONHelper.java
com.raizlabs.synchronization.OneShotLock.java
com.raizlabs.tasks.RZAsyncTaskEvent.java
com.raizlabs.tasks.RZAsyncTaskListener.java
com.raizlabs.tasks.RZAsyncTask.java
com.raizlabs.util.observable.ObservableData.java
com.raizlabs.util.observable.ObservableListAdapter.java
com.raizlabs.util.observable.ObservableList.java
com.raizlabs.view.ViewCompatibility.java
com.raizlabs.view.animation.AnimationListenerWrapper.java
com.raizlabs.view.animation.RelativeLayoutParamsAnimation.java
com.raizlabs.view.animation.ResizeAnimation.java
com.raizlabs.widget.EvenLinearLayout.java
com.raizlabs.widget.ImageMixView.java
com.raizlabs.widget.SlideRevealLayout.java
com.raizlabs.widget.ViewUtils.java
com.raizlabs.widget.adapters.ListBasedAdapter.java
com.raizlabs.widget.adapters.SimpleGenericAdapter.java
com.raizlabs.widget.adapters.ViewGroupAdapter.java
com.raizlabs.widget.adapters.ViewHolderStrategyAdapter.java
com.raizlabs.widget.utils.SimpleViewHolderStrategy.java
com.raizlabs.widget.utils.ViewHolderStrategyConverter.java
com.raizlabs.widget.utils.ViewHolderStrategyUtils.java
com.raizlabs.widget.utils.ViewHolderStrategy.java