mirror of https://github.com/kurisufriend/Clover
parent
010a1c2cef
commit
197edd1d61
@ -0,0 +1,277 @@ |
|||||||
|
/** |
||||||
|
* Copyright (C) 2013 The Android Open Source Project |
||||||
|
* |
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License"); |
||||||
|
* you may not use this file except in compliance with the License. |
||||||
|
* You may obtain a copy of the License at |
||||||
|
* |
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
* |
||||||
|
* Unless required by applicable law or agreed to in writing, software |
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS, |
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
||||||
|
* See the License for the specific language governing permissions and |
||||||
|
* limitations under the License. |
||||||
|
*/ |
||||||
|
package org.floens.chan.ui.view; |
||||||
|
|
||||||
|
import android.content.Context; |
||||||
|
import android.text.TextUtils; |
||||||
|
import android.util.AttributeSet; |
||||||
|
import android.view.ViewGroup.LayoutParams; |
||||||
|
import android.widget.ImageView; |
||||||
|
|
||||||
|
import com.android.volley.VolleyError; |
||||||
|
import com.android.volley.toolbox.ImageLoader; |
||||||
|
import com.android.volley.toolbox.ImageLoader.ImageContainer; |
||||||
|
import com.android.volley.toolbox.ImageLoader.ImageListener; |
||||||
|
|
||||||
|
/** |
||||||
|
* Custom version of NetworkImageView |
||||||
|
* |
||||||
|
* Handles fetching an image from a URL as well as the life-cycle of the |
||||||
|
* associated request. |
||||||
|
*/ |
||||||
|
public class CustomNetworkImageView extends ImageView { |
||||||
|
/** The URL of the network image to load */ |
||||||
|
private String mUrl; |
||||||
|
|
||||||
|
/** |
||||||
|
* Resource ID of the image to be used as a placeholder until the network |
||||||
|
* image is loaded. |
||||||
|
*/ |
||||||
|
private int mDefaultImageId; |
||||||
|
|
||||||
|
/** |
||||||
|
* Resource ID of the image to be used if the network response fails. |
||||||
|
*/ |
||||||
|
private int mErrorImageId; |
||||||
|
|
||||||
|
/** Local copy of the ImageLoader. */ |
||||||
|
private ImageLoader mImageLoader; |
||||||
|
|
||||||
|
/** Current ImageContainer. (either in-flight or finished) */ |
||||||
|
private ImageContainer mImageContainer; |
||||||
|
|
||||||
|
/** |
||||||
|
* Max amount to scale the image inside the view |
||||||
|
*/ |
||||||
|
private float mMaxScale = 1; |
||||||
|
|
||||||
|
private int mFadeTime; |
||||||
|
|
||||||
|
public CustomNetworkImageView(Context context) { |
||||||
|
this(context, null); |
||||||
|
} |
||||||
|
|
||||||
|
public CustomNetworkImageView(Context context, AttributeSet attrs) { |
||||||
|
this(context, attrs, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public CustomNetworkImageView(Context context, AttributeSet attrs, int defStyle) { |
||||||
|
super(context, attrs, defStyle); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* How larger the inner bitmap is to the defined view size. |
||||||
|
* |
||||||
|
* @param amount |
||||||
|
*/ |
||||||
|
public void setMaxScale(float amount) { |
||||||
|
mMaxScale = amount; |
||||||
|
} |
||||||
|
|
||||||
|
public float getMaxScale() { |
||||||
|
return mMaxScale; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Animate the image fading in. |
||||||
|
* |
||||||
|
* @param duration |
||||||
|
* duration of the fade animation in milliseconds |
||||||
|
*/ |
||||||
|
public void setFadeIn(int time) { |
||||||
|
mFadeTime = time; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets URL of the image that should be loaded into this view. Note that |
||||||
|
* calling this will immediately either set the cached image (if available) |
||||||
|
* or the default image specified by |
||||||
|
* {@link CustomNetworkImageView#setDefaultImageResId(int)} on the view. |
||||||
|
* |
||||||
|
* NOTE: If applicable, |
||||||
|
* {@link CustomNetworkImageView#setDefaultImageResId(int)} and |
||||||
|
* {@link CustomNetworkImageView#setErrorImageResId(int)} should be called |
||||||
|
* prior to calling this function. |
||||||
|
* |
||||||
|
* @param url |
||||||
|
* The URL that should be loaded into this ImageView. |
||||||
|
* @param imageLoader |
||||||
|
* ImageLoader that will be used to make the request. |
||||||
|
*/ |
||||||
|
public void setImageUrl(String url, ImageLoader imageLoader) { |
||||||
|
mUrl = url; |
||||||
|
mImageLoader = imageLoader; |
||||||
|
// The URL has potentially changed. See if we need to load it.
|
||||||
|
loadImageIfNecessary(false); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the default image resource ID to be used for this view until the |
||||||
|
* attempt to load it completes. |
||||||
|
*/ |
||||||
|
public void setDefaultImageResId(int defaultImage) { |
||||||
|
mDefaultImageId = defaultImage; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Sets the error image resource ID to be used for this view in the event |
||||||
|
* that the image requested fails to load. |
||||||
|
*/ |
||||||
|
public void setErrorImageResId(int errorImage) { |
||||||
|
mErrorImageId = errorImage; |
||||||
|
} |
||||||
|
|
||||||
|
public void onErrorResponse(VolleyError error) { |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* Loads the image for the view if it isn't already loaded. |
||||||
|
* |
||||||
|
* @param isInLayoutPass |
||||||
|
* True if this was invoked from a layout pass, false otherwise. |
||||||
|
*/ |
||||||
|
void loadImageIfNecessary(final boolean isInLayoutPass) { |
||||||
|
int width = getWidth(); |
||||||
|
int height = getHeight(); |
||||||
|
|
||||||
|
boolean wrapWidth = false, wrapHeight = false; |
||||||
|
if (getLayoutParams() != null) { |
||||||
|
wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT; |
||||||
|
wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT; |
||||||
|
} |
||||||
|
|
||||||
|
// if the view's bounds aren't known yet, and this is not a
|
||||||
|
// wrap-content/wrap-content
|
||||||
|
// view, hold off on loading the image.
|
||||||
|
boolean isFullyWrapContent = wrapWidth && wrapHeight; |
||||||
|
if (width == 0 && height == 0 && !isFullyWrapContent) { |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// if the URL to be loaded in this view is empty, cancel any old
|
||||||
|
// requests and clear the
|
||||||
|
// currently loaded image.
|
||||||
|
if (TextUtils.isEmpty(mUrl)) { |
||||||
|
if (mImageContainer != null) { |
||||||
|
mImageContainer.cancelRequest(); |
||||||
|
mImageContainer = null; |
||||||
|
} |
||||||
|
setDefaultImageOrNull(); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
// if there was an old request in this view, check if it needs to be
|
||||||
|
// canceled.
|
||||||
|
if (mImageContainer != null && mImageContainer.getRequestUrl() != null) { |
||||||
|
if (mImageContainer.getRequestUrl().equals(mUrl)) { |
||||||
|
// if the request is from the same URL, return.
|
||||||
|
return; |
||||||
|
} else { |
||||||
|
// if there is a pre-existing request, cancel it if it's
|
||||||
|
// fetching a different URL.
|
||||||
|
mImageContainer.cancelRequest(); |
||||||
|
setDefaultImageOrNull(); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Calculate the max image width / height to use while ignoring
|
||||||
|
// WRAP_CONTENT dimens.
|
||||||
|
int maxWidth = wrapWidth ? 0 : width; |
||||||
|
int maxHeight = wrapHeight ? 0 : height; |
||||||
|
|
||||||
|
// The pre-existing content of this view didn't match the current URL.
|
||||||
|
// Load the new image
|
||||||
|
// from the network.
|
||||||
|
ImageContainer newContainer = mImageLoader.get(mUrl, new ImageListener() { |
||||||
|
@Override |
||||||
|
public void onErrorResponse(VolleyError error) { |
||||||
|
if (mErrorImageId != 0) { |
||||||
|
setImageResource(mErrorImageId); |
||||||
|
} |
||||||
|
|
||||||
|
CustomNetworkImageView.this.onErrorResponse(error); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
public void onResponse(final ImageContainer response, boolean isImmediate) { |
||||||
|
// If this was an immediate response that was delivered inside
|
||||||
|
// of a layout
|
||||||
|
// pass do not set the image immediately as it will trigger a
|
||||||
|
// requestLayout
|
||||||
|
// inside of a layout. Instead, defer setting the image by
|
||||||
|
// posting back to
|
||||||
|
// the main thread.
|
||||||
|
if (isImmediate && isInLayoutPass) { |
||||||
|
post(new Runnable() { |
||||||
|
@Override |
||||||
|
public void run() { |
||||||
|
onResponse(response, false); |
||||||
|
} |
||||||
|
}); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
if (response.getBitmap() != null) { |
||||||
|
setImageBitmap(response.getBitmap()); |
||||||
|
|
||||||
|
if (mFadeTime > 0 && !isImmediate) { |
||||||
|
setAlpha(0f); |
||||||
|
animate().alpha(1f).setDuration(mFadeTime); |
||||||
|
} |
||||||
|
} else if (mDefaultImageId != 0) { |
||||||
|
setImageResource(mDefaultImageId); |
||||||
|
} |
||||||
|
} |
||||||
|
}, (int)(maxWidth * mMaxScale), (int)(maxHeight * mMaxScale)); |
||||||
|
|
||||||
|
// update the ImageContainer to be the new bitmap container.
|
||||||
|
mImageContainer = newContainer; |
||||||
|
} |
||||||
|
|
||||||
|
private void setDefaultImageOrNull() { |
||||||
|
if (mDefaultImageId != 0) { |
||||||
|
setImageResource(mDefaultImageId); |
||||||
|
} else { |
||||||
|
setImageBitmap(null); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onLayout(boolean changed, int left, int top, int right, int bottom) { |
||||||
|
super.onLayout(changed, left, top, right, bottom); |
||||||
|
loadImageIfNecessary(true); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void onDetachedFromWindow() { |
||||||
|
if (mImageContainer != null) { |
||||||
|
// If the view was bound to an image request, cancel it and clear
|
||||||
|
// out the image from the view.
|
||||||
|
mImageContainer.cancelRequest(); |
||||||
|
setImageBitmap(null); |
||||||
|
// also clear out the container so we can reload the image if
|
||||||
|
// necessary.
|
||||||
|
mImageContainer = null; |
||||||
|
} |
||||||
|
super.onDetachedFromWindow(); |
||||||
|
} |
||||||
|
|
||||||
|
@Override |
||||||
|
protected void drawableStateChanged() { |
||||||
|
super.drawableStateChanged(); |
||||||
|
invalidate(); |
||||||
|
} |
||||||
|
} |
Loading…
Reference in new issue