提交 b8963a02 编写于 作者: S Sam Judd

Pull out request builders into separate classes

上级 217da78d
package com.bumptech.glide;
import android.content.Context;
import android.graphics.Bitmap;
import android.view.View;
import android.widget.ImageView;
import com.bumptech.glide.loader.bitmap.ImageVideoBitmapLoadFactory;
import com.bumptech.glide.loader.bitmap.ResourceBitmapLoadFactory;
import com.bumptech.glide.loader.bitmap.model.ModelLoader;
import com.bumptech.glide.loader.bitmap.model.ModelLoaderFactory;
import com.bumptech.glide.resize.Priority;
import com.bumptech.glide.resize.load.BitmapDecoder;
import com.bumptech.glide.resize.load.Downsampler;
import com.bumptech.glide.resize.load.MultiTransformation;
import com.bumptech.glide.resize.load.Transformation;
import com.bumptech.glide.resize.load.VideoBitmapDecoder;
import com.bumptech.glide.resize.request.BitmapRequestBuilder;
import com.bumptech.glide.resize.request.Request;
import com.bumptech.glide.resize.request.ThumbnailRequestCoordinator;
import com.bumptech.glide.resize.target.ImageViewTarget;
import com.bumptech.glide.resize.target.Target;
import java.util.ArrayList;
import java.util.List;
/**
* A generic class that can handle loading a bitmap either from an image or as a thumbnail from a video given
* models loaders to translate a model into generic resources for either an image or a video and decoders that can
* decode those resources into bitmaps.
*
* @param <ModelType> The type of model representing the image or video.
* @param <ImageResourceType> The resource type that the image {@link ModelLoader} will provide that can be decoded
* by the image {@link BitmapDecoder}.
* @param <VideoResourceType> The resource type that the video {@link ModelLoader} will provide that can be decoded
* by the video {@link BitmapDecoder}.
*/
public class GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> {
private Context context;
private ModelLoaderFactory<ModelType, ImageResourceType> imageModelLoaderFactory;
private final List<Transformation> transformations = new ArrayList<Transformation>();
private final ModelLoaderFactory<ModelType, VideoResourceType> videoModelLoaderFactory;
private final ModelType model;
private int animationId;
private int placeholderId;
private int errorId;
private RequestListener<ModelType> requestListener;
private BitmapDecoder<ImageResourceType> imageDecoder;
private BitmapDecoder<VideoResourceType> videoDecoder;
private Float thumbSizeMultiplier;
private GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> thumbnailRequestBuilder;
private Float sizeMultiplier = 1f;
GenericRequestBuilder(Context context, ModelType model,
ModelLoaderFactory<ModelType, ImageResourceType> imageFactory,
ModelLoaderFactory<ModelType, VideoResourceType> videoFactory) {
if (context == null) {
throw new NullPointerException("Context can't be null");
}
this.context = context;
if (model == null ) {
throw new NullPointerException("Model can't be null");
}
this.model = model;
if (imageFactory == null && videoFactory == null) {
throw new NullPointerException("No ModelLoaderFactorys registered for either image or video type,"
+ " class=" + model.getClass());
}
this.imageModelLoaderFactory = imageFactory;
this.videoModelLoaderFactory = videoFactory;
}
/**
* Loads and displays the image retrieved by the given thumbnail request if it finishes before this request.
* Best used for loading thumbnail images that are smaller and will be loaded more quickly than the fullsize
* image. There are no guarantees about the order in which the requests will actually finish. However, if the
* thumb request completes after the full request, the thumb image will never replace the full image.
*
* @see #thumbnail(float)
*
* <p>
* Note - Any options on the main request will not be passed on to the thumbnail request. For example, if
* you want an animation to occur when either the full image loads or the thumbnail loads, you need to call
* {@link #animate(int)} on both the thumb and the full request. For a simpler thumbnail option, see
* {@link #thumbnail(float)}.
* </p>
*
* <p>
* Only the thumbnail call on the main request will be obeyed.
* </p>
*
* @param thumbnailRequest The request to use to load the thumbnail.
* @return This builder object.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> thumbnail(
GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> thumbnailRequest) {
this.thumbnailRequestBuilder = thumbnailRequest;
return this;
}
/**
* Loads an image in an identical manner to this request except with the dimensions of the target multiplied
* by the given size multiplier. If the thumbnail load completes before the fullsize load, the thumbnail will
* be shown. If the thumbnail load completes afer the fullsize load, the thumbnail will not be shown.
*
* <p>
* Note - The thumbnail image will be smaller than the size requested so the target (or {@link ImageView})
* must be able to scale the thumbnail appropriately. See {@link ImageView.ScaleType}.
* </p>
*
* <p>
* Almost all options will be copied from the original load, including the {@link ModelLoader},
* {@link BitmapDecoder}, and {@link Transformation}s. However, {@link #placeholder(int)} and
* {@link #error(int)}, and {@link #listener(RequestListener)} will only be used on the fullsize load and
* will not be copied for the thumbnail load.
* </p>
*
* <p>
* Only the thumbnail call on the main request will be obeyed.
* </p>
*
* @param sizeMultiplier The multiplier to apply to the {@link Target}'s dimensions when loading the thumbnail.
* @return This builder object.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> thumbnail(float sizeMultiplier) {
if (sizeMultiplier < 0f || sizeMultiplier > 1f) {
throw new IllegalArgumentException("sizeMultiplier must be between 0 and 1");
}
this.thumbSizeMultiplier = sizeMultiplier;
return this;
}
/**
* Applies a multiplier to the {@link Target}'s size before loading the image. Useful for loading thumbnails
* or trying to avoid loading huge bitmaps on devices with overly dense screens.
*
* @param sizeMultiplier The multiplier to apply to the {@link Target}'s dimensions when loading the image.
* @return This builder object.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> sizeMultiplier(
float sizeMultiplier) {
if (sizeMultiplier < 0f || sizeMultiplier > 1f) {
throw new IllegalArgumentException("sizeMultiplier must be between 0 and 1");
}
this.sizeMultiplier = sizeMultiplier;
return this;
}
/**
* Loads the image from the given resource type into an {@link Bitmap} using the given {@link BitmapDecoder}.
*
* <p>
* Will be ignored if the data represented by the given model is not an image.
* </p>
*
* @see Downsampler
*
* @param decoder The {@link BitmapDecoder} to use to decode the image resource.
* @return This RequestBuilder.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> imageDecoder(
BitmapDecoder<ImageResourceType> decoder) {
this.imageDecoder = decoder;
return this;
}
/**
* Loads the video from the given resource type into an {@link Bitmap} using the given {@link BitmapDecoder}.
*
* <p>
* Will be ignored if the data represented by the given model is not a video.
* </p>
*
* @see VideoBitmapDecoder
*
* @param decoder The {@link BitmapDecoder} to use to decode the video resource.
* @return This request.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> videoDecoder(
BitmapDecoder<VideoResourceType> decoder) {
this.videoDecoder = decoder;
return this;
}
/**
* Transform images using {@link Transformation#CENTER_CROP}.
*
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> centerCrop() {
return transform(Transformation.CENTER_CROP);
}
/**
* Transform images using {@link Transformation#FIT_CENTER}.
*
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> fitCenter() {
return transform(Transformation.FIT_CENTER);
}
/**
* Transform images with the given {@link Transformation}. Appends this transformation onto any existing
* transformations
*
* @param transformation the transformation to apply.
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> transform(
Transformation transformation) {
transformations.add(transformation);
return this;
}
/**
* Sets an animation to run on the wrapped target when an image load finishes. Will only be run if the image
* was loaded asynchronously (ie was not in the memory cache)
*
* @param animationId The resource id of the animation to run
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> animate(int animationId) {
this.animationId = animationId;
return this;
}
/**
* Sets a resource to display while an image is loading
*
* @param resourceId The id of the resource to use as a placeholder
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> placeholder(int resourceId) {
this.placeholderId = resourceId;
return this;
}
/**
* Sets a resource to display if a load fails
*
* @param resourceId The id of the resource to use as a placeholder
* @return This request
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> error(int resourceId) {
this.errorId = resourceId;
return this;
}
/**
* Sets a RequestBuilder listener to monitor the image load. It's best to create a single instance of an
* exception handler per type of request (usually activity/fragment) rather than pass one in per request to
* avoid some redundant object allocation.
*
* @param requestListener The request listener to use
* @return This request
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> listener(
RequestListener<ModelType> requestListener) {
this.requestListener = requestListener;
return this;
}
/**
* Set the target the image will be loaded into.
*
* @param target The target to load te image for
* @return The given target.
*/
public <Y extends Target> Y into(Y target) {
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
}
Request request = buildRequest(target);
target.setRequest(request);
request.run();
return target;
}
/**
* Sets the {@link ImageView} the image will be loaded into, cancels any existing loads into the view, and frees
* any resources Glide has loaded into the view so they may be reused.
*
* @see Glide#clear(View)
*
* @param view The view to cancel previous loads for and load the new image into.
* @return The {@link ImageViewTarget} used to wrap the given {@link ImageView}.
*/
public ImageViewTarget into(ImageView view) {
return into(new ImageViewTarget(view));
}
private <Y extends Target> Request buildRequest(Y target) {
final Request result;
if (thumbnailRequestBuilder != null) {
ThumbnailRequestCoordinator requestCoordinator = new ThumbnailRequestCoordinator();
Request fullRequest = buildBitmapRequest(target)
.setRequestCoordinator(requestCoordinator)
.build();
if (thumbnailRequestBuilder.animationId == 0 && animationId != 0) {
thumbnailRequestBuilder.animationId = animationId;
}
if (thumbnailRequestBuilder.requestListener == null && requestListener != null) {
thumbnailRequestBuilder.requestListener = requestListener;
}
Request thumbnailRequest = thumbnailRequestBuilder.buildBitmapRequest(target)
.setRequestCoordinator(requestCoordinator)
.build();
requestCoordinator.setRequests(fullRequest, thumbnailRequest);
result = requestCoordinator;
} else if (thumbSizeMultiplier != null) {
ThumbnailRequestCoordinator requestCoordinator = new ThumbnailRequestCoordinator();
Request fullRequest = buildBitmapRequest(target)
.setRequestCoordinator(requestCoordinator)
.build();
Request thumbnailRequest = buildBitmapRequest(target)
.setRequestCoordinator(requestCoordinator)
.setSizeMultiplier(thumbSizeMultiplier)
.build();
requestCoordinator.setRequests(fullRequest, thumbnailRequest);
result = requestCoordinator;
} else {
result = buildBitmapRequest(target).build();
}
return result;
}
private <Y extends Target> BitmapRequestBuilder<ModelType> buildBitmapRequest(Y target) {
ModelLoader<ModelType, ImageResourceType> imageModelLoader = null;
if (imageModelLoaderFactory != null) {
imageModelLoader = imageModelLoaderFactory.build(context, Glide.get(context).getLoaderFactory());
}
ModelLoader<ModelType, VideoResourceType> videoModelLoader = null;
if (videoModelLoaderFactory != null) {
videoModelLoader = videoModelLoaderFactory.build(context, Glide.get(context).getLoaderFactory());
}
final Transformation transformation = getFinalTransformation();
return new BitmapRequestBuilder<ModelType>()
.setContext(context)
.setPriority(Priority.NORMAL)
.setImageManager(Glide.get(context).getImageManager())
.setModel(model)
.setTarget(target)
.setBitmapLoadFactory(
new ImageVideoBitmapLoadFactory<ModelType, ImageResourceType, VideoResourceType>(
imageModelLoader != null && imageDecoder != null ?
new ResourceBitmapLoadFactory<ModelType, ImageResourceType>(
imageModelLoader, imageDecoder) : null,
videoModelLoader != null && videoDecoder != null ?
new ResourceBitmapLoadFactory<ModelType, VideoResourceType>(
videoModelLoader, videoDecoder) : null,
transformation))
.setAnimation(animationId)
.setRequestListener(requestListener)
.setPlaceholderResource(placeholderId)
.setErrorResource(errorId)
.setSizeMultiplier(sizeMultiplier);
}
private Transformation getFinalTransformation() {
switch (transformations.size()) {
case 0:
return Transformation.NONE;
case 1:
return transformations.get(0);
default:
return new MultiTransformation(transformations);
}
}
}
......@@ -6,10 +6,7 @@ import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.view.View;
import android.widget.ImageView;
import com.android.volley.RequestQueue;
import com.bumptech.glide.loader.bitmap.ImageVideoBitmapLoadFactory;
import com.bumptech.glide.loader.bitmap.ResourceBitmapLoadFactory;
import com.bumptech.glide.loader.bitmap.model.GenericLoaderFactory;
import com.bumptech.glide.loader.bitmap.model.ModelLoader;
import com.bumptech.glide.loader.bitmap.model.ModelLoaderFactory;
......@@ -25,19 +22,10 @@ import com.bumptech.glide.loader.bitmap.model.stream.StreamResourceLoader;
import com.bumptech.glide.loader.bitmap.model.stream.StreamStringLoader;
import com.bumptech.glide.loader.bitmap.model.stream.StreamUriLoader;
import com.bumptech.glide.resize.ImageManager;
import com.bumptech.glide.resize.Priority;
import com.bumptech.glide.resize.bitmap_recycle.BitmapPool;
import com.bumptech.glide.resize.cache.DiskCache;
import com.bumptech.glide.resize.cache.MemoryCache;
import com.bumptech.glide.resize.load.BitmapDecoder;
import com.bumptech.glide.resize.load.Downsampler;
import com.bumptech.glide.resize.load.MultiTransformation;
import com.bumptech.glide.resize.load.Transformation;
import com.bumptech.glide.resize.load.VideoBitmapDecoder;
import com.bumptech.glide.resize.request.BitmapRequestBuilder;
import com.bumptech.glide.resize.request.Request;
import com.bumptech.glide.resize.request.ThumbnailRequestCoordinator;
import com.bumptech.glide.resize.target.ImageViewTarget;
import com.bumptech.glide.resize.target.Target;
import com.bumptech.glide.resize.target.ViewTarget;
import com.bumptech.glide.volley.VolleyUrlLoader;
......@@ -45,8 +33,6 @@ import com.bumptech.glide.volley.VolleyUrlLoader;
import java.io.File;
import java.io.InputStream;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
/**
......@@ -63,52 +49,6 @@ public class Glide {
private final RequestQueue requestQueue;
private final ImageManager imageManager;
/**
* A class for monitoring the status of a request while images load.
*
* @param <T> The type of the model being loaded
*/
public interface RequestListener<T> {
/**
* Called when an exception occurs during a load. Will only be called if we currently want to display an image
* for the given model in the given target. It is recommended to create a single instance per activity/fragment
* rather than instantiate a new object for each call to {@code Glide.load()} to avoid object churn.
*
* <p>
* It is safe to reload this or a different model or change what is displayed in the target at this point.
* For example:
* <pre>
* <code>
* public void onException(Exception e, ModelType model, Target target) {
* target.setPlaceholder(R.drawable.a_specific_error_for_my_exception);
* Glide.load(model).into(target);
* }
* </code>
* </pre>
* </p>
*
* <p>
* Note - if you want to reload this or any other model after an exception, you will need to include all
* relevant builder calls (like centerCrop, placeholder etc).
* </p>
*
* @param e The exception, or null
* @param model The model we were trying to load when the exception occurred
* @param target The {@link Target} we were trying to load the image into
*/
public abstract void onException(Exception e, T model, Target target);
/**
* Called when a load completes successfully, immediately after
* {@link Target#onImageReady(android.graphics.Bitmap)}.
*
* @param model The specific model that was used to load the image.
* @param target The target the model was loaded into.
*/
public abstract void onImageReady(T model, Target target, boolean isFromMemoryCache, boolean isAnyImageSet);
}
/**
* Get the singleton.
*
......@@ -177,6 +117,37 @@ public class Glide {
return requestQueue;
}
/**
* Cancel any pending loads Glide may have for the target and free any resources (such as {@link Bitmap}s) that may
* have been loaded for the target so they may be reused.
*
* @param target The Target to cancel loads for.
*/
public static void clear(Target target) {
Request request = target.getRequest();
if (request!= null) {
request.clear();
}
}
/**
* Cancel any pending loads Glide may have for the view and free any resources that may have been loaded for the
* view.
*
* <p>
* Note that this will only work if {@link View#setTag(Object)} is not called on this view outside of Glide.
* </p>
*
* @see #clear(Target).
*
* @param view The view to cancel loads and free resources for.
* @throws IllegalArgumentException if an object other than Glide's metadata is set as the view's tag.
*/
public static void clear(View view) {
Target viewTarget = new ClearTarget(view);
clear(viewTarget);
}
/**
* Use the given factory to build a {@link ModelLoader} for models of the given class. Generally the best use of
* this method is to replace one of the default factories or add an implementation for other similar low level
......@@ -211,11 +182,6 @@ public class Glide {
}
}
@SuppressWarnings("unchecked")
private <T, Y> ModelLoaderFactory<T, Y> getFactory(T model, Class<Y> resourceClass) {
return loaderFactory.getFactory((Class<T>) model.getClass(), resourceClass);
}
/**
* Build a {@link ModelLoader} for the given model class using a registered factory.
*
......@@ -251,37 +217,6 @@ public class Glide {
return buildModelLoader(modelClass, ParcelFileDescriptor.class, context);
}
/**
* Cancel any pending loads Glide may have for the target and free any resources (such as {@link Bitmap}s) that may
* have been loaded for the target so they may be reused.
*
* @param target The Target to cancel loads for.
*/
public static void clear(Target target) {
Request request = target.getRequest();
if (request!= null) {
request.clear();
}
}
/**
* Cancel any pending loads Glide may have for the view and free any resources that may have been loaded for the
* view.
*
* <p>
* Note that this will only work if {@link View#setTag(Object)} is not called on this view outside of Glide.
* </p>
*
* @see #clear(Target).
*
* @param view The view to cancel loads and free resources for.
* @throws IllegalArgumentException if an object other than Glide's metadata is set as the view's tag.
*/
public static void clear(View view) {
Target viewTarget = new CancelTarget(view);
clear(viewTarget);
}
/**
* Begin a load with Glide by passing in a context.
*
......@@ -292,6 +227,33 @@ public class Glide {
return new ModelRequest(context);
}
@SuppressWarnings("unchecked")
<T, Y> ModelLoaderFactory<T, Y> getFactory(T model, Class<Y> resourceClass) {
return loaderFactory.getFactory((Class<T>) model.getClass(), resourceClass);
}
GenericLoaderFactory getLoaderFactory() {
return loaderFactory;
}
private static <T, Y> ModelLoaderFactory<T, Y> modelLoaderToFactory(final ModelLoader<T, Y> modelLoader) {
return new ModelLoaderFactory<T, Y>() {
@Override
public ModelLoader<T, Y> build(Context context, GenericLoaderFactory factories) {
return modelLoader;
}
@SuppressWarnings("unchecked")
@Override
public Class<? extends ModelLoader<T, Y>> loaderClass() {
return (Class<ModelLoader<T, Y>>) modelLoader.getClass();
}
@Override
public void teardown() { }
};
}
/**
* A {@link RequestBuilder} builder that returns a request for a model that represents an image.
*/
......@@ -507,519 +469,8 @@ public class Glide {
}
}
/**
* A class for creating a request to load a bitmap for an image or from a video. Sets a variety of type independent
* options including resizing, animations, and placeholders.
*
* @param <ModelType> The type of model that will be loaded into the target.
*/
@SuppressWarnings("unused") //public api
public static class RequestBuilder<ModelType> extends
GenericRequestBuilder<ModelType, InputStream, ParcelFileDescriptor> {
private RequestBuilder(Context context, ModelType model) {
this(context, model, Glide.get(context).getFactory(model, InputStream.class),
Glide.get(context).getFactory(model, ParcelFileDescriptor.class));
}
private RequestBuilder(Context context, ModelType model,
ModelLoaderFactory<ModelType, InputStream> imageFactory,
ModelLoaderFactory<ModelType, ParcelFileDescriptor> videoFactory) {
super(context, model, imageFactory, videoFactory);
approximate().videoDecoder(new VideoBitmapDecoder());
}
/**
* Load images at a size near the size of the target using {@link Downsampler#AT_LEAST}.
*
* @see #downsample(com.bumptech.glide.resize.load.Downsampler)
*
* @return This RequestBuilder
*/
public RequestBuilder<ModelType> approximate() {
return downsample(Downsampler.AT_LEAST);
}
/**
* Load images at their original size using {@link Downsampler#NONE}.
*
* @see #downsample(com.bumptech.glide.resize.load.Downsampler)
*
* @return This RequestBuilder
*/
public RequestBuilder<ModelType> asIs() {
return downsample(Downsampler.NONE);
}
/**
* Load images using the given {@link Downsampler}. Replaces any existing image decoder. Defaults to
* {@link Downsampler#AT_LEAST}. Will be ignored if the data represented by the model is a video.
*
* @see #imageDecoder
* @see #videoDecoder(BitmapDecoder)
*
* @param downsampler The downsampler
* @return This RequestBuilder
*/
public RequestBuilder<ModelType> downsample(Downsampler downsampler) {
super.imageDecoder(downsampler);
return this;
}
public RequestBuilder<ModelType> thumbnail(float sizeMultiplier) {
super.thumbnail(sizeMultiplier);
return this;
}
public RequestBuilder<ModelType> thumbnail(RequestBuilder<ModelType> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
public RequestBuilder<ModelType> sizeMultiplier(float sizeMultiplier) {
super.sizeMultiplier(sizeMultiplier);
return this;
}
@Override
public RequestBuilder<ModelType> imageDecoder(BitmapDecoder<InputStream> decoder) {
super.imageDecoder(decoder);
return this;
}
@Override
public RequestBuilder<ModelType> videoDecoder(BitmapDecoder<ParcelFileDescriptor> decoder) {
super.videoDecoder(decoder);
return this;
}
@Override
public RequestBuilder<ModelType> centerCrop() {
super.centerCrop();
return this;
}
@Override
public RequestBuilder<ModelType> fitCenter() {
super.fitCenter();
return this;
}
@Override
public RequestBuilder<ModelType> transform(Transformation transformation) {
super.transform(transformation);
return this;
}
@Override
public RequestBuilder<ModelType> animate(int animationId) {
super.animate(animationId);
return this;
}
@Override
public RequestBuilder<ModelType> placeholder(int resourceId) {
super.placeholder(resourceId);
return this;
}
@Override
public RequestBuilder<ModelType> error(int resourceId) {
super.error(resourceId);
return this;
}
@Override
public RequestBuilder<ModelType> listener(RequestListener<ModelType> requestListener) {
super.listener(requestListener);
return this;
}
}
/**
* A generic class that can handle loading a bitmap either from an image or as a thumbnail from a video given
* models loaders to translate a model into generic resources for either an image or a video and decoders that can
* decode those resources into bitmaps.
*
* @param <ModelType> The type of model representing the image or video.
* @param <ImageResourceType> The resource type that the image {@link ModelLoader} will provide that can be decoded
* by the image {@link BitmapDecoder}.
* @param <VideoResourceType> The resource type that the video {@link ModelLoader} will provide that can be decoded
* by the video {@link BitmapDecoder}.
*/
private static class GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> {
private Context context;
private ModelLoaderFactory<ModelType, ImageResourceType> imageModelLoaderFactory;
private final List<Transformation> transformations = new ArrayList<Transformation>();
private final ModelLoaderFactory<ModelType, VideoResourceType> videoModelLoaderFactory;
private final ModelType model;
private int animationId;
private int placeholderId;
private int errorId;
private RequestListener<ModelType> requestListener;
private BitmapDecoder<ImageResourceType> imageDecoder;
private BitmapDecoder<VideoResourceType> videoDecoder;
private Float thumbSizeMultiplier;
private GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> thumbnailRequestBuilder;
private Float sizeMultiplier = 1f;
private GenericRequestBuilder(Context context, ModelType model,
ModelLoaderFactory<ModelType, ImageResourceType> imageFactory,
ModelLoaderFactory<ModelType, VideoResourceType> videoFactory) {
if (context == null) {
throw new NullPointerException("Context can't be null");
}
this.context = context;
if (model == null ) {
throw new NullPointerException("Model can't be null");
}
this.model = model;
if (imageFactory == null && videoFactory == null) {
throw new NullPointerException("No ModelLoaderFactorys registered for either image or video type,"
+ " class=" + model.getClass());
}
this.imageModelLoaderFactory = imageFactory;
this.videoModelLoaderFactory = videoFactory;
}
/**
* Loads and displays the image retrieved by the given thumbnail request if it finishes before this request.
* Best used for loading thumbnail images that are smaller and will be loaded more quickly than the fullsize
* image. There are no guarantees about the order in which the requests will actually finish. However, if the
* thumb request completes after the full request, the thumb image will never replace the full image.
*
* @see #thumbnail(float)
*
* <p>
* Note - Any options on the main request will not be passed on to the thumbnail request. For example, if
* you want an animation to occur when either the full image loads or the thumbnail loads, you need to call
* {@link #animate(int)} on both the thumb and the full request. For a simpler thumbnail option, see
* {@link #thumbnail(float)}.
* </p>
*
* <p>
* Only the thumbnail call on the main request will be obeyed.
* </p>
*
* @param thumbnailRequest The request to use to load the thumbnail.
* @return This builder object.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> thumbnail(
GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> thumbnailRequest) {
this.thumbnailRequestBuilder = thumbnailRequest;
return this;
}
/**
* Loads an image in an identical manner to this request except with the dimensions of the target multiplied
* by the given size multiplier. If the thumbnail load completes before the fullsize load, the thumbnail will
* be shown. If the thumbnail load completes afer the fullsize load, the thumbnail will not be shown.
*
* <p>
* Note - The thumbnail image will be smaller than the size requested so the target (or {@link ImageView})
* must be able to scale the thumbnail appropriately. See {@link ImageView.ScaleType}.
* </p>
*
* <p>
* Almost all options will be copied from the original load, including the {@link ModelLoader},
* {@link BitmapDecoder}, and {@link Transformation}s. However, {@link #placeholder(int)} and
* {@link #error(int)}, and {@link #listener(RequestListener)} will only be used on the fullsize load and
* will not be copied for the thumbnail load.
* </p>
*
* <p>
* Only the thumbnail call on the main request will be obeyed.
* </p>
*
* @param sizeMultiplier The multiplier to apply to the {@link Target}'s dimensions when loading the thumbnail.
* @return This builder object.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> thumbnail(float sizeMultiplier) {
if (sizeMultiplier < 0f || sizeMultiplier > 1f) {
throw new IllegalArgumentException("sizeMultiplier must be between 0 and 1");
}
this.thumbSizeMultiplier = sizeMultiplier;
return this;
}
/**
* Applies a multiplier to the {@link Target}'s size before loading the image. Useful for loading thumbnails
* or trying to avoid loading huge bitmaps on devices with overly dense screens.
*
* @param sizeMultiplier The multiplier to apply to the {@link Target}'s dimensions when loading the image.
* @return This builder object.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> sizeMultiplier(
float sizeMultiplier) {
if (sizeMultiplier < 0f || sizeMultiplier > 1f) {
throw new IllegalArgumentException("sizeMultiplier must be between 0 and 1");
}
this.sizeMultiplier = sizeMultiplier;
return this;
}
/**
* Loads the image from the given resource type into an {@link Bitmap} using the given {@link BitmapDecoder}.
*
* <p>
* Will be ignored if the data represented by the given model is not an image.
* </p>
*
* @see Downsampler
*
* @param decoder The {@link BitmapDecoder} to use to decode the image resource.
* @return This RequestBuilder.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> imageDecoder(
BitmapDecoder<ImageResourceType> decoder) {
this.imageDecoder = decoder;
return this;
}
/**
* Loads the video from the given resource type into an {@link Bitmap} using the given {@link BitmapDecoder}.
*
* <p>
* Will be ignored if the data represented by the given model is not a video.
* </p>
*
* @see VideoBitmapDecoder
*
* @param decoder The {@link BitmapDecoder} to use to decode the video resource.
* @return This request.
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> videoDecoder(
BitmapDecoder<VideoResourceType> decoder) {
this.videoDecoder = decoder;
return this;
}
/**
* Transform images using {@link Transformation#CENTER_CROP}.
*
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> centerCrop() {
return transform(Transformation.CENTER_CROP);
}
/**
* Transform images using {@link Transformation#FIT_CENTER}.
*
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> fitCenter() {
return transform(Transformation.FIT_CENTER);
}
/**
* Transform images with the given {@link Transformation}. Appends this transformation onto any existing
* transformations
*
* @param transformation the transformation to apply.
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> transform(
Transformation transformation) {
transformations.add(transformation);
return this;
}
/**
* Sets an animation to run on the wrapped target when an image load finishes. Will only be run if the image
* was loaded asynchronously (ie was not in the memory cache)
*
* @param animationId The resource id of the animation to run
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> animate(int animationId) {
this.animationId = animationId;
return this;
}
/**
* Sets a resource to display while an image is loading
*
* @param resourceId The id of the resource to use as a placeholder
* @return This RequestBuilder
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> placeholder(int resourceId) {
this.placeholderId = resourceId;
return this;
}
/**
* Sets a resource to display if a load fails
*
* @param resourceId The id of the resource to use as a placeholder
* @return This request
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> error(int resourceId) {
this.errorId = resourceId;
return this;
}
/**
* Sets a RequestBuilder listener to monitor the image load. It's best to create a single instance of an
* exception handler per type of request (usually activity/fragment) rather than pass one in per request to
* avoid some redundant object allocation.
*
* @param requestListener The request listener to use
* @return This request
*/
public GenericRequestBuilder<ModelType, ImageResourceType, VideoResourceType> listener(
RequestListener<ModelType> requestListener) {
this.requestListener = requestListener;
return this;
}
/**
* Set the target the image will be loaded into.
*
* @param target The target to load te image for
* @return The given target.
*/
public <Y extends Target> Y into(Y target) {
Request previous = target.getRequest();
if (previous != null) {
previous.clear();
}
Request request = buildRequest(target);
target.setRequest(request);
request.run();
return target;
}
/**
* Sets the {@link ImageView} the image will be loaded into, cancels any existing loads into the view, and frees
* any resources Glide has loaded into the view so they may be reused.
*
* @see #clear(View)
*
* @param view The view to cancel previous loads for and load the new image into.
* @return The {@link ImageViewTarget} used to wrap the given {@link ImageView}.
*/
public ImageViewTarget into(ImageView view) {
return into(new ImageViewTarget(view));
}
private <Y extends Target> Request buildRequest(Y target) {
final Request result;
if (thumbnailRequestBuilder != null) {
ThumbnailRequestCoordinator requestCoordinator = new ThumbnailRequestCoordinator();
Request fullRequest = buildBitmapRequest(target)
.setRequestCoordinator(requestCoordinator)
.build();
if (thumbnailRequestBuilder.animationId == 0 && animationId != 0) {
thumbnailRequestBuilder.animationId = animationId;
}
if (thumbnailRequestBuilder.requestListener == null && requestListener != null) {
thumbnailRequestBuilder.requestListener = requestListener;
}
Request thumbnailRequest = thumbnailRequestBuilder.buildBitmapRequest(target)
.setRequestCoordinator(requestCoordinator)
.build();
requestCoordinator.setRequests(fullRequest, thumbnailRequest);
result = requestCoordinator;
} else if (thumbSizeMultiplier != null) {
ThumbnailRequestCoordinator requestCoordinator = new ThumbnailRequestCoordinator();
Request fullRequest = buildBitmapRequest(target)
.setRequestCoordinator(requestCoordinator)
.build();
Request thumbnailRequest = buildBitmapRequest(target)
.setRequestCoordinator(requestCoordinator)
.setSizeMultiplier(thumbSizeMultiplier)
.build();
requestCoordinator.setRequests(fullRequest, thumbnailRequest);
result = requestCoordinator;
} else {
result = buildBitmapRequest(target).build();
}
return result;
}
private <Y extends Target> BitmapRequestBuilder<ModelType> buildBitmapRequest(Y target) {
ModelLoader<ModelType, ImageResourceType> imageModelLoader = null;
if (imageModelLoaderFactory != null) {
imageModelLoader = imageModelLoaderFactory.build(context, Glide.get(context).loaderFactory);
}
ModelLoader<ModelType, VideoResourceType> videoModelLoader = null;
if (videoModelLoaderFactory != null) {
videoModelLoader = videoModelLoaderFactory.build(context, Glide.get(context).loaderFactory);
}
final Transformation transformation = getFinalTransformation();
return new BitmapRequestBuilder<ModelType>()
.setContext(context)
.setPriority(Priority.NORMAL)
.setImageManager(Glide.get(context).imageManager)
.setModel(model)
.setTarget(target)
.setBitmapLoadFactory(
new ImageVideoBitmapLoadFactory<ModelType, ImageResourceType, VideoResourceType>(
imageModelLoader != null && imageDecoder != null ?
new ResourceBitmapLoadFactory<ModelType, ImageResourceType>(
imageModelLoader, imageDecoder) : null,
videoModelLoader != null && videoDecoder != null ?
new ResourceBitmapLoadFactory<ModelType, VideoResourceType>(
videoModelLoader, videoDecoder) : null,
transformation))
.setAnimation(animationId)
.setRequestListener(requestListener)
.setPlaceholderResource(placeholderId)
.setErrorResource(errorId)
.setSizeMultiplier(sizeMultiplier);
}
private Transformation getFinalTransformation() {
switch (transformations.size()) {
case 0:
return Transformation.NONE;
case 1:
return transformations.get(0);
default:
return new MultiTransformation(transformations);
}
}
}
private static <T, Y> ModelLoaderFactory<T, Y> modelLoaderToFactory(final ModelLoader<T, Y> modelLoader) {
return new ModelLoaderFactory<T, Y>() {
@Override
public ModelLoader<T, Y> build(Context context, GenericLoaderFactory factories) {
return modelLoader;
}
@SuppressWarnings("unchecked")
@Override
public Class<? extends ModelLoader<T, Y>> loaderClass() {
return (Class<ModelLoader<T, Y>>) modelLoader.getClass();
}
@Override
public void teardown() { }
};
}
private static class CancelTarget extends ViewTarget<View> {
public CancelTarget(View view) {
private static class ClearTarget extends ViewTarget<View> {
public ClearTarget(View view) {
super(view);
}
......
......@@ -83,9 +83,9 @@ public abstract class ListPreloader<T> implements AbsListView.OnScrollListener {
* target and context will be provided by the preloader.
*
* @param item The model to load.
* @return A non null {@link Glide.RequestBuilder}.
* @return A non null {@link RequestBuilder}.
*/
protected abstract Glide.RequestBuilder getRequest(T item);
protected abstract RequestBuilder getRequestBuilder(T item);
private void preload(int start, boolean increasing) {
if (isIncreasing != increasing) {
......@@ -130,7 +130,7 @@ public abstract class ListPreloader<T> implements AbsListView.OnScrollListener {
final T item = items.get(position);
final int[] dimensions = getDimensions(item);
if (dimensions != null) {
getRequest(item).into(preloadTargetQueue.next(dimensions[0], dimensions[1]));
getRequestBuilder(item).into(preloadTargetQueue.next(dimensions[0], dimensions[1]));
}
}
......
package com.bumptech.glide;
import android.content.Context;
import android.os.ParcelFileDescriptor;
import com.bumptech.glide.loader.bitmap.model.ModelLoaderFactory;
import com.bumptech.glide.resize.load.BitmapDecoder;
import com.bumptech.glide.resize.load.Downsampler;
import com.bumptech.glide.resize.load.Transformation;
import com.bumptech.glide.resize.load.VideoBitmapDecoder;
import java.io.InputStream;
/**
* A class for creating a request to load a bitmap for an image or from a video. Sets a variety of type independent
* options including resizing, animations, and placeholders.
*
* @param <ModelType> The type of model that will be loaded into the target.
*/
@SuppressWarnings("unused") //public api
public class RequestBuilder<ModelType> extends GenericRequestBuilder<ModelType, InputStream, ParcelFileDescriptor> {
RequestBuilder(Context context, ModelType model) {
this(context, model, Glide.get(context).getFactory(model, InputStream.class),
Glide.get(context).getFactory(model, ParcelFileDescriptor.class));
}
RequestBuilder(Context context, ModelType model, ModelLoaderFactory<ModelType, InputStream> imageFactory,
ModelLoaderFactory<ModelType, ParcelFileDescriptor> videoFactory) {
super(context, model, imageFactory, videoFactory);
approximate().videoDecoder(new VideoBitmapDecoder());
}
/**
* Load images at a size near the size of the target using {@link Downsampler#AT_LEAST}.
*
* @see #downsample(com.bumptech.glide.resize.load.Downsampler)
*
* @return This RequestBuilder
*/
public RequestBuilder<ModelType> approximate() {
return downsample(Downsampler.AT_LEAST);
}
/**
* Load images at their original size using {@link Downsampler#NONE}.
*
* @see #downsample(com.bumptech.glide.resize.load.Downsampler)
*
* @return This RequestBuilder
*/
public RequestBuilder<ModelType> asIs() {
return downsample(Downsampler.NONE);
}
/**
* Load images using the given {@link Downsampler}. Replaces any existing image decoder. Defaults to
* {@link Downsampler#AT_LEAST}. Will be ignored if the data represented by the model is a video.
*
* @see #imageDecoder
* @see #videoDecoder(BitmapDecoder)
*
* @param downsampler The downsampler
* @return This RequestBuilder
*/
public RequestBuilder<ModelType> downsample(Downsampler downsampler) {
super.imageDecoder(downsampler);
return this;
}
public RequestBuilder<ModelType> thumbnail(float sizeMultiplier) {
super.thumbnail(sizeMultiplier);
return this;
}
public RequestBuilder<ModelType> thumbnail(RequestBuilder<ModelType> thumbnailRequest) {
super.thumbnail(thumbnailRequest);
return this;
}
public RequestBuilder<ModelType> sizeMultiplier(float sizeMultiplier) {
super.sizeMultiplier(sizeMultiplier);
return this;
}
@Override
public RequestBuilder<ModelType> imageDecoder(BitmapDecoder<InputStream> decoder) {
super.imageDecoder(decoder);
return this;
}
@Override
public RequestBuilder<ModelType> videoDecoder(BitmapDecoder<ParcelFileDescriptor> decoder) {
super.videoDecoder(decoder);
return this;
}
@Override
public RequestBuilder<ModelType> centerCrop() {
super.centerCrop();
return this;
}
@Override
public RequestBuilder<ModelType> fitCenter() {
super.fitCenter();
return this;
}
@Override
public RequestBuilder<ModelType> transform(Transformation transformation) {
super.transform(transformation);
return this;
}
@Override
public RequestBuilder<ModelType> animate(int animationId) {
super.animate(animationId);
return this;
}
@Override
public RequestBuilder<ModelType> placeholder(int resourceId) {
super.placeholder(resourceId);
return this;
}
@Override
public RequestBuilder<ModelType> error(int resourceId) {
super.error(resourceId);
return this;
}
@Override
public RequestBuilder<ModelType> listener(RequestListener<ModelType> requestListener) {
super.listener(requestListener);
return this;
}
}
package com.bumptech.glide;
import com.bumptech.glide.resize.target.Target;
/**
* A class for monitoring the status of a request while images load.
*
* @param <T> The type of the model being loaded
*/
public interface RequestListener<T> {
/**
* Called when an exception occurs during a load. Will only be called if we currently want to display an image
* for the given model in the given target. It is recommended to create a single instance per activity/fragment
* rather than instantiate a new object for each call to {@code Glide.load()} to avoid object churn.
*
* <p>
* It is safe to reload this or a different model or change what is displayed in the target at this point.
* For example:
* <pre>
* <code>
* public void onException(Exception e, ModelType model, Target target) {
* target.setPlaceholder(R.drawable.a_specific_error_for_my_exception);
* Glide.load(model).into(target);
* }
* </code>
* </pre>
* </p>
*
* <p>
* Note - if you want to reload this or any other model after an exception, you will need to include all
* relevant builder calls (like centerCrop, placeholder etc).
* </p>
*
* @param e The exception, or null
* @param model The model we were trying to load when the exception occurred
* @param target The {@link Target} we were trying to load the image into
*/
public abstract void onException(Exception e, T model, Target target);
/**
* Called when a load completes successfully, immediately after
* {@link Target#onImageReady(android.graphics.Bitmap)}.
*
* @param model The specific model that was used to load the image.
* @param target The target the model was loaded into.
*/
public abstract void onImageReady(T model, Target target, boolean isFromMemoryCache, boolean isAnyImageSet);
}
......@@ -6,7 +6,7 @@ import android.graphics.drawable.Drawable;
import android.util.Log;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestListener;
import com.bumptech.glide.loader.bitmap.BitmapLoadFactory;
import com.bumptech.glide.resize.ImageManager;
import com.bumptech.glide.resize.Metadata;
......@@ -33,7 +33,7 @@ public class BitmapRequest<T> implements Request, ImageManager.LoadedCallback, T
private final ImageManager imageManager;
private final Priority priority;
private final Target target;
private final Glide.RequestListener<T> requestListener;
private final RequestListener<T> requestListener;
private final float sizeMultiplier;
private Drawable placeholderDrawable;
......
......@@ -4,7 +4,7 @@ import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.view.animation.Animation;
import com.bumptech.glide.Glide;
import com.bumptech.glide.RequestListener;
import com.bumptech.glide.loader.bitmap.BitmapLoadFactory;
import com.bumptech.glide.resize.ImageManager;
import com.bumptech.glide.resize.Priority;
......@@ -24,7 +24,7 @@ public class BitmapRequestBuilder<T> {
float sizeMultiplier;
Drawable placeholderDrawable;
Drawable errorDrawable;
Glide.RequestListener<T> requestListener;
RequestListener<T> requestListener;
Animation animation;
int placeholderResourceId;
int errorResourceId;
......@@ -67,7 +67,7 @@ public class BitmapRequestBuilder<T> {
return this;
}
public BitmapRequestBuilder<T> setRequestListener(Glide.RequestListener<T> requestListener) {
public BitmapRequestBuilder<T> setRequestListener(RequestListener<T> requestListener) {
this.requestListener = requestListener;
return this;
}
......
......@@ -11,6 +11,7 @@ import android.widget.ImageView;
import com.actionbarsherlock.app.SherlockFragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.ListPreloader;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.loader.bitmap.model.Cache;
import com.bumptech.glide.samples.flickr.api.Photo;
......@@ -81,7 +82,7 @@ public class FlickrPhotoGrid extends SherlockFragment implements PhotoViewer {
}
@Override
protected Glide.RequestBuilder<Photo> getRequest(Photo item) {
protected RequestBuilder<Photo> getRequestBuilder(Photo item) {
return Glide.with(context)
.using(new FlickrModelLoader(getActivity(), urlCache))
.load(item)
......
......@@ -12,6 +12,7 @@ import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.ListPreloader;
import com.bumptech.glide.RequestBuilder;
import com.bumptech.glide.loader.bitmap.model.Cache;
import com.bumptech.glide.samples.flickr.api.Photo;
......@@ -91,7 +92,7 @@ public class FlickrPhotoList extends SherlockFragment implements PhotoViewer {
}
@Override
protected Glide.RequestBuilder getRequest(Photo item) {
protected RequestBuilder getRequestBuilder(Photo item) {
return Glide.with(context)
.using(new FlickrModelLoader(getActivity(), urlCache))
.load(item)
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册