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

Bring back special handling for views using setTag

上级 214d5d49
......@@ -2,11 +2,14 @@ package com.bumptech.glide;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import com.bumptech.glide.loader.bitmap.BaseBitmapLoadFactory;
import com.bumptech.glide.loader.bitmap.model.file_descriptor.FileDescriptorFileLoader;
import com.bumptech.glide.loader.bitmap.model.file_descriptor.FileDescriptorModelLoader;
......@@ -30,6 +33,8 @@ import com.bumptech.glide.loader.bitmap.transformation.None;
import com.bumptech.glide.loader.bitmap.transformation.TransformationLoader;
import com.bumptech.glide.presenter.ImagePresenter;
import com.bumptech.glide.presenter.target.Target;
import com.bumptech.glide.presenter.target.ImageViewTarget;
import com.bumptech.glide.presenter.target.ViewTarget;
import com.bumptech.glide.resize.ImageManager;
import com.bumptech.glide.resize.load.BitmapDecoder;
import com.bumptech.glide.resize.load.Downsampler;
......@@ -219,6 +224,11 @@ 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.
*
......@@ -253,26 +263,36 @@ public class Glide {
return buildModelLoader(modelClass, ParcelFileDescriptor.class, context);
}
@SuppressWarnings("unchecked")
private <T, Y> ModelLoaderFactory<T, Y> getFactory(T model, Class<Y> resourceClass) {
return loaderFactory.getFactory((Class<T>) model.getClass(), resourceClass);
}
/**
* Cancel any pending loads Glide may have for the target. After the load is cancelled Glide will not load
* a placeholder or bitmap into the target so it is safe to do so yourself until you start another load.
* 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
* @return True iff Glide had ever been asked to load an image for this target
* @param target The Target to cancel loads for.
*/
public static boolean cancel(Target target) {
ImagePresenter current = target.getImagePresenter();
final boolean cancelled = current != null;
if (cancelled) {
current.clear();
public static void clear(Target target) {
ImagePresenter imagePresenter = target.getImagePresenter();
if (imagePresenter != null) {
imagePresenter.clear();
}
}
return cancelled;
/**
* 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);
}
/**
......@@ -317,7 +337,6 @@ public class Glide {
*/
public <T> VideoModelRequest<T> using(final FileDescriptorModelLoader<T> modelLoader) {
return new VideoModelRequest<T>(context, modelLoaderToFactory(modelLoader));
}
/**
......@@ -775,27 +794,31 @@ public class Glide {
* 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) {
return finish(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> Y finish(Y target) {
ImagePresenter<ModelType, Y> imagePresenter = getImagePresenter(target);
ImagePresenter<ModelType, Y> imagePresenter = buildImagePresenter(target);
imagePresenter.setModel(model);
return target;
}
@SuppressWarnings("unchecked")
private <Y extends Target> ImagePresenter<ModelType, Y> getImagePresenter(Y target) {
final ImagePresenter<ModelType, Y> current = target.getImagePresenter();
if (current != null) {
current.clear();
}
return buildImagePresenter(target);
}
private <Y extends Target> ImagePresenter<ModelType, Y> buildImagePresenter(final Y target) {
TransformationLoader<ModelType> transformationLoader = getFinalTransformationLoader();
......@@ -885,4 +908,16 @@ public class Glide {
public void teardown() { }
};
}
private static class CancelTarget extends ViewTarget<View> {
public CancelTarget(View view) {
super(view);
}
@Override
public void onImageReady(Bitmap bitmap) { }
@Override
public void setPlaceholder(Drawable placeholder) { }
}
}
......@@ -4,7 +4,7 @@ import android.annotation.TargetApi;
import android.graphics.Bitmap;
import android.os.Build;
import android.widget.AbsListView;
import com.bumptech.glide.presenter.target.SimpleTarget;
import com.bumptech.glide.presenter.target.BaseTarget;
import java.util.ArrayDeque;
import java.util.LinkedList;
......@@ -136,7 +136,7 @@ public abstract class ListPreloader<T> implements AbsListView.OnScrollListener {
private void cancelAll() {
for (int i = 0; i < maxPreload; i++) {
Glide.cancel(preloadTargetQueue.next(0, 0));
Glide.clear(preloadTargetQueue.next(0, 0));
}
}
......@@ -165,7 +165,7 @@ public abstract class ListPreloader<T> implements AbsListView.OnScrollListener {
}
}
private static class PreloadTarget extends SimpleTarget {
private static class PreloadTarget extends BaseTarget {
private int photoHeight;
private int photoWidth;
......@@ -173,13 +173,9 @@ public abstract class ListPreloader<T> implements AbsListView.OnScrollListener {
public void onImageReady(Bitmap bitmap) { }
@Override
protected int getWidth() {
return photoWidth;
public void getSize(SizeReadyCallback cb) {
cb.onSizeReady(photoWidth, photoHeight);
}
@Override
protected int getHeight() {
return photoHeight;
}
}
}
package com.bumptech.glide.presenter.target;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.view.View;
import android.view.animation.Animation;
import com.bumptech.glide.Glide;
import com.bumptech.glide.presenter.ImagePresenter;
/**
* A base {@link Target} for loading {@link Bitmap}s that provides basic or empty implementations for most methods.
*
* <p>
* For maximum efficiency, clear this target when you have finished using or displaying the {@link Bitmap} loaded
* into it using {@link Glide#clear(Target)}.
* </p>
*
* <p>
* For loading {@link Bitmap}s into {@link View}s, {@link ViewTarget} is preferable to this class.
* </p>
*/
public abstract class BaseTarget implements Target {
private ImagePresenter imagePresenter;
......@@ -15,4 +32,10 @@ public abstract class BaseTarget implements Target {
public ImagePresenter getImagePresenter() {
return imagePresenter;
}
@Override
public void setPlaceholder(Drawable placeholder) { }
@Override
public void startAnimation(Animation animation) { }
}
......@@ -18,154 +18,21 @@ import static android.view.ViewGroup.LayoutParams;
/**
* A target wrapping an ImageView. Obtains the runtime dimensions of the ImageView.
*/
public class ImageViewTarget extends BaseTarget {
private static final String TAG = "ImageViewTarget";
private final ImageView imageView;
private final SizeDeterminer sizeDeterminer;
public class ImageViewTarget extends ViewTarget<ImageView> {
private final ImageView view;
public ImageViewTarget(ImageView imageView) {
this.imageView = imageView;
this.sizeDeterminer = new SizeDeterminer(imageView);
}
public ImageView getView() {
return imageView;
public ImageViewTarget(ImageView view) {
super(view);
this.view = view;
}
@Override
public void onImageReady(Bitmap bitmap) {
imageView.setImageBitmap(bitmap);
view.setImageBitmap(bitmap);
}
@Override
public void setPlaceholder(Drawable placeholder) {
imageView.setImageDrawable(placeholder);
}
@Override
public void getSize(SizeReadyCallback cb) {
sizeDeterminer.getSize(cb);
}
@Override
public void startAnimation(Animation animation) {
imageView.clearAnimation();
imageView.startAnimation(animation);
}
@Override
public int hashCode() {
return imageView.hashCode();
}
@Override
public boolean equals(Object o) {
if (o == null) {
return false;
} else if (!(o instanceof ImageViewTarget)) {
return false;
}
ImageViewTarget other = (ImageViewTarget) o;
return imageView.equals(other.imageView);
}
private static class SizeDeterminer {
private final View view;
private SizeReadyCallback cb;
private SizeDeterminerLayoutListener layoutListener;
private void checkCurrentDimens() {
if (cb == null) return;
boolean calledCallback = true;
LayoutParams layoutParams = view.getLayoutParams();
if (isViewSizeValid()) {
cb.onSizeReady(view.getWidth(), view.getHeight());
} else if (isLayoutParamsSizeValid()) {
cb.onSizeReady(layoutParams.width, layoutParams.height);
} else {
calledCallback = false;
}
if (calledCallback) {
cb = null;
// Keep a reference to the layout listener and remove it here
// rather than having the observer remove itself because the observer
// we add the listener to will be almost immediately merged into
// another observer and will therefore never be alive. If we instead
// keep a reference to the listener and remove it here, we get the
// current view tree observer and should succeed.
ViewTreeObserver observer = view.getViewTreeObserver();
if (observer.isAlive()) {
observer.removeGlobalOnLayoutListener(layoutListener);
}
}
}
public SizeDeterminer(View view) {
this.view = view;
}
public void getSize(SizeReadyCallback cb) {
this.cb = null;
LayoutParams layoutParams = view.getLayoutParams();
if (isViewSizeValid()) {
cb.onSizeReady(view.getWidth(), view.getHeight());
} else if (isLayoutParamsSizeValid()) {
cb.onSizeReady(layoutParams.width, layoutParams.height);
} else if (isUsingWrapContent()) {
WindowManager windowManager =
(WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
final int width = display.getWidth();
final int height = display.getHeight();
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Trying to load image into ImageView using WRAP_CONTENT, defaulting to screen" +
" dimensions: [" + width + "x" + height + "]. Give the view an actual width and height " +
" for better performance.");
}
cb.onSizeReady(display.getWidth(), display.getHeight());
} else {
this.cb = cb;
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnGlobalLayoutListener(layoutListener);
}
}
private boolean isViewSizeValid() {
return view.getWidth() > 0 && view.getHeight() > 0;
}
private boolean isUsingWrapContent() {
final LayoutParams layoutParams = view.getLayoutParams();
return layoutParams != null && (layoutParams.width == LayoutParams.WRAP_CONTENT
|| layoutParams.height == LayoutParams.WRAP_CONTENT);
}
private boolean isLayoutParamsSizeValid() {
final LayoutParams layoutParams = view.getLayoutParams();
return layoutParams != null && (layoutParams.width > 0 && layoutParams.height > 0);
}
private static class SizeDeterminerLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private final WeakReference<SizeDeterminer> sizeDeterminerRef;
public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) {
sizeDeterminerRef = new WeakReference<SizeDeterminer>(sizeDeterminer);
}
@Override
public void onGlobalLayout() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "OnGlobalLayoutListener called listener=" + this);
}
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens();
}
}
}
view.setImageDrawable(placeholder);
}
}
package com.bumptech.glide.presenter.target;
import android.graphics.drawable.Drawable;
import android.view.animation.Animation;
import com.bumptech.glide.presenter.ImagePresenter;
/**
* A simpler interface for targets with default (usually noop) implementations of non essential methods.
* A simpler interface for targets with default (usually noop) implementations of non essential methods that allows the
* caller to specify an exact width/height.
*/
@SuppressWarnings("unused")
public abstract class SimpleTarget extends BaseTarget {
private final int width;
private final int height;
@Override
public void setPlaceholder(Drawable placeholder) { }
@Override
public void startAnimation(Animation animation) { }
public SimpleTarget(int width, int height) {
this.width = width;
this.height = height;
}
/**
* A default implementation that calls {@link com.bumptech.glide.presenter.target.Target.SizeReadyCallback}
* synchronously with {@link #getWidth()} and {@link #getHeight()}
*
* @param cb The callback that must be called when the size of the target has been determined
*/
@Override
public void getSize(SizeReadyCallback cb) {
cb.onSizeReady(getWidth(), getHeight());
public final void getSize(SizeReadyCallback cb) {
cb.onSizeReady(width, height);
}
/**
* @return The height of this target, which will be used to determine how to load and crop the Bitmap.
*/
protected abstract int getWidth();
/**
* @return The width of this target, which will be used to determine how to load and crop the Bitmap.
*/
protected abstract int getHeight();
}
package com.bumptech.glide.presenter.target;
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.widget.ListView;
import com.bumptech.glide.presenter.ImagePresenter;
import java.lang.ref.WeakReference;
/**
* A base {@link Target} for loading {@link Bitmap}s into {@link View}s that provides default implementations for most
* most methods and can determine the size of views using a {@link ViewTreeObserver.OnGlobalLayoutListener}.
*
* <p>
* To detect {@link View} reuse in {@link ListView} or any {@link ViewGroup} that reuses views, this class uses the
* {@link View#setTag(Object)} method to store some metadata so that if a view is reused, any previous loads or
* resources from previous loads can be cancelled or reused.
* </p>
*
* <p>
* Any calls to {@link View#setTag(Object)}} on a View given to this class will result in excessive allocations and
* and/or {@link IllegalArgumentException}s. If you must call {@link View#setTag(Object)} on a view, consider
* using {@link BaseTarget} or {@link SimpleTarget} instead.
* </p>
*
* @param <T> The specific subclass of view wrapped by this target.
*/
public abstract class ViewTarget<T extends View> implements Target {
private static final String TAG = "ViewTarget";
private final T view;
private final SizeDeterminer sizeDeterminer;
public ViewTarget(T view) {
this.view = view;
sizeDeterminer = new SizeDeterminer(view);
}
public T getView() {
return view;
}
@Override
public void getSize(SizeReadyCallback cb) {
sizeDeterminer.getSize(cb);
}
@Override
public void startAnimation(Animation animation) {
view.clearAnimation();
view.startAnimation(animation);
}
@Override
public void setImagePresenter(ImagePresenter imagePresenter) {
view.setTag(imagePresenter);
}
@Override
public ImagePresenter getImagePresenter() {
Object tag = view.getTag();
ImagePresenter presenter = null;
if (tag != null) {
if ((tag instanceof ImagePresenter)) {
presenter = (ImagePresenter) tag;
} else {
throw new IllegalArgumentException("You must not call setTag() on a view Glide is targeting");
}
}
return presenter;
}
private static class SizeDeterminer {
private final View view;
private SizeReadyCallback cb;
private SizeDeterminerLayoutListener layoutListener;
public SizeDeterminer(View view) {
this.view = view;
}
private void checkCurrentDimens() {
if (cb == null) return;
boolean calledCallback = true;
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
if (isViewSizeValid()) {
cb.onSizeReady(view.getWidth(), view.getHeight());
} else if (isLayoutParamsSizeValid()) {
cb.onSizeReady(layoutParams.width, layoutParams.height);
} else {
calledCallback = false;
}
if (calledCallback) {
cb = null;
// Keep a reference to the layout listener and remove it here
// rather than having the observer remove itself because the observer
// we add the listener to will be almost immediately merged into
// another observer and will therefore never be alive. If we instead
// keep a reference to the listener and remove it here, we get the
// current view tree observer and should succeed.
ViewTreeObserver observer = view.getViewTreeObserver();
if (observer.isAlive()) {
observer.removeGlobalOnLayoutListener(layoutListener);
}
}
}
public void getSize(SizeReadyCallback cb) {
this.cb = null;
ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
if (isViewSizeValid()) {
cb.onSizeReady(view.getWidth(), view.getHeight());
} else if (isLayoutParamsSizeValid()) {
cb.onSizeReady(layoutParams.width, layoutParams.height);
} else if (isUsingWrapContent()) {
WindowManager windowManager =
(WindowManager) view.getContext().getSystemService(Context.WINDOW_SERVICE);
Display display = windowManager.getDefaultDisplay();
final int width = display.getWidth();
final int height = display.getHeight();
if (Log.isLoggable(TAG, Log.WARN)) {
Log.w(TAG, "Trying to load image into ImageView using WRAP_CONTENT, defaulting to screen" +
" dimensions: [" + width + "x" + height + "]. Give the view an actual width and height " +
" for better performance.");
}
cb.onSizeReady(display.getWidth(), display.getHeight());
} else {
this.cb = cb;
final ViewTreeObserver observer = view.getViewTreeObserver();
layoutListener = new SizeDeterminerLayoutListener(this);
observer.addOnGlobalLayoutListener(layoutListener);
}
}
private boolean isViewSizeValid() {
return view.getWidth() > 0 && view.getHeight() > 0;
}
private boolean isUsingWrapContent() {
final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
return layoutParams != null && (layoutParams.width == ViewGroup.LayoutParams.WRAP_CONTENT
|| layoutParams.height == ViewGroup.LayoutParams.WRAP_CONTENT);
}
private boolean isLayoutParamsSizeValid() {
final ViewGroup.LayoutParams layoutParams = view.getLayoutParams();
return layoutParams != null && (layoutParams.width > 0 && layoutParams.height > 0);
}
private static class SizeDeterminerLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
private final WeakReference<SizeDeterminer> sizeDeterminerRef;
public SizeDeterminerLayoutListener(SizeDeterminer sizeDeterminer) {
sizeDeterminerRef = new WeakReference<SizeDeterminer>(sizeDeterminer);
}
@Override
public void onGlobalLayout() {
if (Log.isLoggable(TAG, Log.VERBOSE)) {
Log.v(TAG, "OnGlobalLayoutListener called listener=" + this);
}
SizeDeterminer sizeDeterminer = sizeDeterminerRef.get();
if (sizeDeterminer != null) {
sizeDeterminer.checkCurrentDimens();
}
}
}
}
}
......@@ -13,7 +13,6 @@ import com.actionbarsherlock.app.SherlockFragment;
import com.bumptech.glide.Glide;
import com.bumptech.glide.ListPreloader;
import com.bumptech.glide.loader.bitmap.model.Cache;
import com.bumptech.glide.presenter.target.ImageViewTarget;
import com.bumptech.glide.samples.flickr.api.Photo;
import java.net.URL;
......@@ -33,8 +32,9 @@ public class FlickrPhotoList extends SherlockFragment implements PhotoViewer {
@Override
public void onPhotosUpdated(List<Photo> photos) {
currentPhotos = photos;
if (adapter != null)
if (adapter != null) {
adapter.setPhotos(currentPhotos);
};
}
@Override
......@@ -45,18 +45,19 @@ public class FlickrPhotoList extends SherlockFragment implements PhotoViewer {
list.setAdapter(adapter);
preloader = new FlickrListPreloader(getActivity(), 5);
list.setOnScrollListener(preloader);
if (currentPhotos != null)
if (currentPhotos != null) {
adapter.setPhotos(currentPhotos);
}
return result;
}
private static class ViewHolder {
private final TextView titleText;
private final ImageViewTarget imageViewTarget;
private final ImageView imageView;
public ViewHolder(ImageView imageView, TextView titleText) {
this.imageView = imageView;
this.titleText = titleText;
this.imageViewTarget = new ImageViewTarget(imageView);
}
}
......@@ -153,7 +154,7 @@ public class FlickrPhotoList extends SherlockFragment implements PhotoViewer {
.load(current)
.centerCrop()
.animate(R.anim.fade_in)
.into(viewHolder.imageViewTarget);
.into(viewHolder.imageView);
viewHolder.titleText.setText(current.title);
return view;
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册