Android 学习笔记之Volley(八)实现网络图片的数据加载

时间:2023-03-09 03:09:26
Android 学习笔记之Volley(八)实现网络图片的数据加载

PS:最后一篇关于Volley框架的博客...

学习内容:

1.使用ImageRequest.java实现网络图片加载

2.使用ImageLoader.java实现网络图片加载

3.使用NetWorkImageView.java实现网络图片加载

  Volley的第三个作用就是实现网络图片的加载,图片加载在Volley中有三种不同的方式,各有各的用法,只是后两个还算是有相关联系的,因为NetWorkImageView是基于ImageLoader的..内部的图片加载还是使用ImageLoader,只是在后续的过程中有一些不同...

1.ImageRequest.java

  使用ImageRequest加载图片数据,ImageRequest没有什么特殊的,也需要建立一个请求队列,向请求队列当中添加这次请求就可以完成图片数据的加载...

package com.android.volley.toolbox;

import com.android.volley.DefaultRetryPolicy;
import com.android.volley.NetworkResponse;
import com.android.volley.ParseError;
import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyLog; import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory; public class ImageRequest extends Request<Bitmap> {
/** Socket timeout in milliseconds for image requests */
private static final int IMAGE_TIMEOUT_MS = 1000; //超时时间的定义.. /** Default number of retries for image requests */
private static final int IMAGE_MAX_RETRIES = 2; //一次最多重试的图片数量... /** Default backoff multiplier for image requests */
private static final float IMAGE_BACKOFF_MULT = 2f; //后退请求的数量.. private final Response.Listener<Bitmap> mListener; //成功监听..
private final Config mDecodeConfig; //属性编码...
private final int mMaxWidth; //最大宽度...
private final int mMaxHeight; //最大高度... /** Decoding lock so that we don't decode more than one image at a time (to avoid OOM's) */
private static final Object sDecodeLock = new Object(); //一个编码锁..目的是一次只能对一个图片进行编码,加载,避免OOM的发生...
//指定url来创建一个ImageRequest请求...
public ImageRequest(String url, Response.Listener<Bitmap> listener, int maxWidth, int maxHeight,
Config decodeConfig, Response.ErrorListener errorListener) {
super(Method.GET, url, errorListener);
setRetryPolicy(
new DefaultRetryPolicy(IMAGE_TIMEOUT_MS, IMAGE_MAX_RETRIES, IMAGE_BACKOFF_MULT));
mListener = listener;
mDecodeConfig = decodeConfig;
mMaxWidth = maxWidth;
mMaxHeight = maxHeight;
}
//获取优先级...图片加载的优先级一般是最低的..
@Override
public Priority getPriority() {
return Priority.LOW;
} //重新设置图片的大小...其实为了缩小图片...
private static int getResizedDimension(int maxPrimary, int maxSecondary, int actualPrimary,
int actualSecondary) {
// If no dominant value at all, just return the actual.
if (maxPrimary == 0 && maxSecondary == 0) {
return actualPrimary;
} // If primary is unspecified, scale primary to match secondary's scaling ratio.
if (maxPrimary == 0) {
double ratio = (double) maxSecondary / (double) actualSecondary;
return (int) (actualPrimary * ratio);
} if (maxSecondary == 0) {
return maxPrimary;
} double ratio = (double) actualSecondary / (double) actualPrimary;
int resized = maxPrimary;
if (resized * ratio > maxSecondary) {
resized = (int) (maxSecondary / ratio);
}
return resized;
}
//解析响应...
@Override
protected Response<Bitmap> parseNetworkResponse(NetworkResponse response) {
// Serialize all decode on a global lock to reduce concurrent heap usage.
synchronized (sDecodeLock) {
try {
return doParse(response); //调用doParse函数..
} catch (OutOfMemoryError e) {
VolleyLog.e("Caught OOM for %d byte image, url=%s", response.data.length, getUrl());
return Response.error(new ParseError(e));
}
}
} /**
* The real guts of parseNetworkResponse. Broken out for readability.
*/
//获取图片数据的过程...
private Response<Bitmap> doParse(NetworkResponse response) {
byte[] data = response.data; //图片数据...
BitmapFactory.Options decodeOptions = new BitmapFactory.Options(); //定义一个位图选项对象..
Bitmap bitmap = null;
if (mMaxWidth == 0 && mMaxHeight == 0) {//这里表示如果为0,那么表示图片按照图片本身的大小进行展现..不用进行其他操作...
decodeOptions.inPreferredConfig = mDecodeConfig;
bitmap = BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);
} else {
// If we have to resize this image, first get the natural bounds.
decodeOptions.inJustDecodeBounds = true; //表示图片需要缩放..
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);//这里并不是完全对图片进行编码,而是获取图片的基本参数..
int actualWidth = decodeOptions.outWidth; //获取图片的宽度...
int actualHeight = decodeOptions.outHeight; //获取图片的高度.. // Then compute the dimensions we would ideally like to decode to.
int desiredWidth = getResizedDimension(mMaxWidth, mMaxHeight,
actualWidth, actualHeight); //对宽度进行缩放...
int desiredHeight = getResizedDimension(mMaxHeight, mMaxWidth,
actualHeight, actualWidth); //对高度进行缩放... // Decode to the nearest power of two scaling factor.
decodeOptions.inJustDecodeBounds = false; //缩放后,表示图片不需要进行缩放了...
// TODO(ficus): Do we need this or is it okay since API 8 doesn't support it?
// decodeOptions.inPreferQualityOverSpeed = PREFER_QUALITY_OVER_SPEED;
decodeOptions.inSampleSize =
findBestSampleSize(actualWidth, actualHeight, desiredWidth, desiredHeight);//根据实际大小和所需大小去找到一个最合适的大小...
Bitmap tempBitmap =
BitmapFactory.decodeByteArray(data, 0, data.length, decodeOptions);//对图片编码,转化成比特流的形式... // If necessary, scale down to the maximal acceptable size.
if (tempBitmap != null && (tempBitmap.getWidth() > desiredWidth ||
tempBitmap.getHeight() > desiredHeight)) {
//这里表示如果通过上述缩放后图片的大小仍然比所需大小要大,那么按照所需大小进一步进行缩放...
bitmap = Bitmap.createScaledBitmap(tempBitmap,
desiredWidth, desiredHeight, true);
tempBitmap.recycle();
} else {
bitmap = tempBitmap; //否则直接为当前临时图片...
}
} if (bitmap == null) {
return Response.error(new ParseError(response)); //返回错误数据..
} else {
return Response.success(bitmap, HttpHeaderParser.parseCacheHeaders(response)); //返回成功数据...
}
}
//对响应分发...
@Override
protected void deliverResponse(Bitmap response) {
mListener.onResponse(response);
} //需找最优大小..
static int findBestSampleSize(
int actualWidth, int actualHeight, int desiredWidth, int desiredHeight) {
double wr = (double) actualWidth / desiredWidth;
double hr = (double) actualHeight / desiredHeight;
double ratio = Math.min(wr, hr);
float n = 1.0f;
while ((n * 2) <= ratio) {
n *= 2;
} return (int) n;
}
}

  源码看起来其实并不是非常的困难,还是比较好理解的..如果预先对ImageLoader加载图片非常熟悉的话,那么这个理解起来就不是非常的费劲...源码就分析到这,还是看看调用过程..这里的构造函数需要传递六个参数...

  public ImageRequest(url,Listener,maxWidth,maxHeight,Config,errorListener);

  传递的参数分别是url,成功监听,最大宽度和高度,图片使用的颜色属性,以及失败后的监听...这里宽度和高度如果指定成自定义的大小,那么图图片就会按照指定大小进行缩放,如果都指定成0,那么图片按照原本大小进行显示,不进行缩放设置...

package com.example.oop;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.ImageLoader.ImageCache;
import com.android.volley.toolbox.NetworkImageView;
import com.android.volley.toolbox.Volley; import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener; public class MainActivity extends Activity implements OnClickListener { String url="http://192.168.199.172:8080/JSP/imageview.jpg";
ImageView iv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
iv=(ImageView)findViewById(R.id.iv);
init();
} public void init(){
RequestQueue queue=Volley.newRequestQueue(MainActivity.this);
queue.add(new ImageRequest("http://192.168.199.172:8080/JSP/imageview.jpg",new Listener<Bitmap>(){
@Override
public void onResponse(Bitmap response){
//自定义了一个imageview...图片资源就是下载后的Bitmap
iv.setImageBitmap(response);
}
},0,0,Config.ARGB_8888, new ErrorListener(){
@Override
public void onErrorResponse(VolleyError error){
System.out.println(error.toString());
}
}));
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub } }

  这样就成功的使用ImageRequest加载了网络上的图片数据...但是使用ImageRequest加载网络图片的缺点是没有缓存机制,只能频繁的发送网络请求,而没有缓存请求,这样会增加服务器的负担...因此在这点上ImageRequest并不是非常的完善...

2.ImageLoader.java

  ImageLoader算是对ImageRequest的一个强化,这个类内部提供了缓存机制,这样我们可以更好的去处理请求,由于存在缓存,那么相同的请求可以通过从缓存中直接获取相关的数据,从而能够减少一些不必要的请求,这样会减少网络请求,减少服务器的负担...

package com.android.volley.toolbox;

import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.os.Handler;
import android.os.Looper;
import android.widget.ImageView; import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response.ErrorListener;
import com.android.volley.Response.Listener;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.ImageRequest; import java.util.HashMap;
import java.util.LinkedList; public class ImageLoader {
/** RequestQueue for dispatching ImageRequests onto. */
private final RequestQueue mRequestQueue; //请求队列.. /** Amount of time to wait after first response arrives before delivering all responses. */
private int mBatchResponseDelayMs = 100; //响应到达的延时... /** The cache implementation to be used as an L1 cache before calling into volley. */
private final ImageCache mCache; //图片缓存对象... private final HashMap<String, BatchedImageRequest> mInFlightRequests =
new HashMap<String, BatchedImageRequest>(); //表示正在处理的请求集合... /** HashMap of the currently pending responses (waiting to be delivered). */
private final HashMap<String, BatchedImageRequest> mBatchedResponses =
new HashMap<String, BatchedImageRequest>(); //批处理响应的集合.. /** Handler to the main thread. */
private final Handler mHandler = new Handler(Looper.getMainLooper()); /** Runnable for in-flight response delivery. */
private Runnable mRunnable; //用于分发响应...
//缓存对象构造...
public interface ImageCache {
public Bitmap getBitmap(String url);
public void putBitmap(String url, Bitmap bitmap);
} //通过一个请求队列和缓存机制来构造一个ImageLoader对象...
public ImageLoader(RequestQueue queue, ImageCache imageCache) {
mRequestQueue = queue;
mCache = imageCache;
} //获取图片监听...
public static ImageListener getImageListener(final ImageView view,
final int defaultImageResId, final int errorImageResId) {
return new ImageListener() { //失败的监听...
@Override
public void onErrorResponse(VolleyError error) {
if (errorImageResId != 0) {
view.setImageResource(errorImageResId); //如果加载失败,使用默认图片来显示...
}
} @Override
public void onResponse(ImageContainer response, boolean isImmediate) {
if (response.getBitmap() != null) {
view.setImageBitmap(response.getBitmap());//如果响应数据不为空,那么显示网络图片...
} else if (defaultImageResId != 0) {
view.setImageResource(defaultImageResId);//失败显示默认图片...
}
}
};
} //失败监听的一个接口...
public interface ImageListener extends ErrorListener { public void onResponse(ImageContainer response, boolean isImmediate);
} //如果请求允许缓存,那么保存缓存数据...
public boolean isCached(String requestUrl, int maxWidth, int maxHeight) {
throwIfNotOnMainThread(); String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight);
return mCache.getBitmap(cacheKey) != null;
} //调用下面的函数...
public ImageContainer get(String requestUrl, final ImageListener listener) {
return get(requestUrl, listener, 0, 0);
} //获取图片的过程...
public ImageContainer get(String requestUrl, ImageListener imageListener,
int maxWidth, int maxHeight) {
// only fulfill requests that were initiated from the main thread.
throwIfNotOnMainThread();
//如果缓存中存在,那么从缓存当中取出数据...
final String cacheKey = getCacheKey(requestUrl, maxWidth, maxHeight); Bitmap cachedBitmap = mCache.getBitmap(cacheKey);
if (cachedBitmap != null) { //缓存存在...
// Return the cached bitmap.
ImageContainer container = new ImageContainer(cachedBitmap, requestUrl, null, null);//将缓存数据的图片,以及url进行封装,返回...
imageListener.onResponse(container, true);
return container;
} ImageContainer imageContainer =
new ImageContainer(null, requestUrl, cacheKey, imageListener); //缓存不存在,新建立一个ImageContainer对象... // Update the caller to let them know that they should use the default bitmap.
//回调函数,使用默认图片...
imageListener.onResponse(imageContainer, true); // Check to see if a request is already in-flight.
BatchedImageRequest request = mInFlightRequests.get(cacheKey); //查看当前执行队列中是否有与之相同的请求..
if (request != null) {
// If it is, add this request to the list of listeners.
request.addContainer(imageContainer); //如果有,那么加入到批处理请求队列当中,由这个请求队列去处理这些相同的请求..
return imageContainer;
} //如果没有,那么新建立一个请求...
Request<?> newRequest =
new ImageRequest(requestUrl, new Listener<Bitmap>() {
@Override
public void onResponse(Bitmap response) {
onGetImageSuccess(cacheKey, response);
}
}, maxWidth, maxHeight,
Config.RGB_565, new ErrorListener() {
@Override
public void onErrorResponse(VolleyError error) {
onGetImageError(cacheKey, error);
}
});
//添加新请求到请求队列当中
mRequestQueue.add(newRequest);
//向当前请求队列中加入这次请求的键和值...
mInFlightRequests.put(cacheKey,
new BatchedImageRequest(newRequest, imageContainer));
return imageContainer;
} //设置批处理响应的延时...
public void setBatchedResponseDelay(int newBatchedResponseDelayMs) {
mBatchResponseDelayMs = newBatchedResponseDelayMs;
} //成功获取图片...
private void onGetImageSuccess(String cacheKey, Bitmap response) {
// cache the image that was fetched.
mCache.putBitmap(cacheKey, response); //向缓存中放入数据... // remove the request from the list of in-flight requests.
BatchedImageRequest request = mInFlightRequests.remove(cacheKey); //请求成功,那么表示所有与之相同的请求都获取到了相关数据,那么就将请求移出队列... if (request != null) {
// Update the response bitmap.
request.mResponseBitmap = response; // Send the batched response
batchResponse(cacheKey, request);//发送相同请求的响应...
}
} //获取图片失败的时候和成功时执行的操作基本是相同的...
private void onGetImageError(String cacheKey, VolleyError error) {
// Notify the requesters that something failed via a null result.
// Remove this request from the list of in-flight requests.
BatchedImageRequest request = mInFlightRequests.remove(cacheKey); // Set the error for this request
request.setError(error); if (request != null) {
// Send the batched response
batchResponse(cacheKey, request);
}
} //ImageContainer类..
public class ImageContainer { private Bitmap mBitmap; //图片对象.. private final ImageListener mListener; //成功监听.. /** The cache key that was associated with the request */
private final String mCacheKey; //缓存键值 /** The request URL that was specified */
private final String mRequestUrl; //url地址... //将图片,url,缓存键值,以及监听的一个封装...
public ImageContainer(Bitmap bitmap, String requestUrl,
String cacheKey, ImageListener listener) {
mBitmap = bitmap;
mRequestUrl = requestUrl;
mCacheKey = cacheKey;
mListener = listener;
} //是否中断了请求...
public void cancelRequest() {
if (mListener == null) {
return;
} BatchedImageRequest request = mInFlightRequests.get(mCacheKey);
if (request != null) {
boolean canceled = request.removeContainerAndCancelIfNecessary(this); //请求如果中断,那么判断是否有必要中断请求...
if (canceled) {
mInFlightRequests.remove(mCacheKey); //如果有必要,直接中断处理...
}
} else {
// check to see if it is already batched for delivery.
request = mBatchedResponses.get(mCacheKey); //判断请求的响应是否已经被分发...
if (request != null) {
request.removeContainerAndCancelIfNecessary(this);//判断是否有必要移除所有与之相同的请求并中断..
if (request.mContainers.size() == 0) { //如果同一种请求已经空了,那么就从批处理请求中移除这个键值..
mBatchedResponses.remove(mCacheKey);
}
}
}
}
//获取图片数据...
public Bitmap getBitmap() {
return mBitmap;
} //获取url...
public String getRequestUrl() {
return mRequestUrl;
}
} private class BatchedImageRequest {
/** The request being tracked */
private final Request<?> mRequest; //请求对象.. /** The result of the request being tracked by this item */
private Bitmap mResponseBitmap; //位图对象... /** Error if one occurred for this response */
private VolleyError mError; //错误对象... private final LinkedList<ImageContainer> mContainers = new LinkedList<ImageContainer>(); //用于保存相同的键值对应的请求... //构造函数...
public BatchedImageRequest(Request<?> request, ImageContainer container) {
mRequest = request;
mContainers.add(container);
} /**
* Set the error for this response
*/
public void setError(VolleyError error) {
mError = error;
} /**
* Get the error for this response
*/
public VolleyError getError() {
return mError;
} //将每一个不同的请求加入队列中
public void addContainer(ImageContainer container) {
mContainers.add(container);
} //是否有必要中断请求...
public boolean removeContainerAndCancelIfNecessary(ImageContainer container) {
mContainers.remove(container);
if (mContainers.size() == 0) {
mRequest.cancel();
return true;
}
return false;
}
} //批处理响应...
private void batchResponse(String cacheKey, BatchedImageRequest request) {
mBatchedResponses.put(cacheKey, request);//放入响应...
//开启一个线程分发响应的过程...
if (mRunnable == null) {
mRunnable = new Runnable() {
@Override
public void run() {
for (BatchedImageRequest bir : mBatchedResponses.values()) {
for (ImageContainer container : bir.mContainers) {
// If one of the callers in the batched request canceled the request
// after the response was received but before it was delivered,
// skip them.
if (container.mListener == null) {
continue;
}
if (bir.getError() == null) {
container.mBitmap = bir.mResponseBitmap;
container.mListener.onResponse(container, false);
} else {
container.mListener.onErrorResponse(bir.getError());
}
}
}
mBatchedResponses.clear();
mRunnable = null;
} };
// Post the runnable.
mHandler.postDelayed(mRunnable, mBatchResponseDelayMs);
}
} private void throwIfNotOnMainThread() {
if (Looper.myLooper() != Looper.getMainLooper()) {
throw new IllegalStateException("ImageLoader must be invoked from the main thread.");
}
}
//获取缓存中的键值..
private static String getCacheKey(String url, int maxWidth, int maxHeight) {
return new StringBuilder(url.length() + 12).append("#W").append(maxWidth)
.append("#H").append(maxHeight).append(url).toString();
}
}

  这个源码相对就比较多,但是其中的过程并不是非常的难理解...还是看一下实际中是如何使用的...

package com.example.oop;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.ImageLoader.ImageCache;
import com.android.volley.toolbox.NetworkImageView;
import com.android.volley.toolbox.Volley; import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener; public class MainActivity extends Activity implements OnClickListener { String url="http://192.168.199.172:8080/JSP/imageview.jpg";
ImageView iv;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView=(ImageView) findViewById(R.id.iv);
init();
} public void init(){
RequestQueue queue=Volley.newRequestQueue(MainActivity.this);
ImageLoader imageLoader=new ImageLoader(queue, new ImageCache() { LruCache<String,Bitmap>cache=new LruCache<String, Bitmap>(((int) Runtime.getRuntime().maxMemory())/8){ @Override
protected int sizeOf(String key,Bitmap bitmap){
return bitmap.getRowBytes()*bitmap.getHeight();
}
}; @Override
public void putBitmap(String url, Bitmap bitmap) {
// TODO Auto-generated method stub
cache.put(url, bitmap);
} @Override
public Bitmap getBitmap(String url) {
// TODO Auto-generated method stub
return cache.get(url);
}
});
//为ImageLoader设置监听...成功则iv显示成功图片,没加载完显示默认图片,加载失败显示失败图片..
ImageListener imagelistener=imageLoader.getImageListener(iv,default_image,error_image);
imageloader.get(url,imagelistener);
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub } }

imageloader.get(url,imagelistener);获取图片的最终过程...这里get函数首先判断图片是否有缓存,通过url的形式来判断是否存在缓存,缓存内部存在,那么直接从缓存中取出,如果缓存中不存在,那么首先看本次的请求是否在批处理图像请求队列中,如果队列中存在这类请求,那么就直接再次添加相同的请求,就没有必要重新建立一个新的请求了...如果这个请求在批处理队列中并不存在,那么就创建一个新的请求来完成...然后放入RequestQueue中和批处理请求队列中...这样就完成了使用ImageLoader来加载图片数据...

  这里使用了LruCache()最近最久未使用算法设立的缓存机制,如果缓存的容量发生了不足,那么直接清除最近最久未被使用的图片缓存...同时每一个Key对应的请求仅仅被放入请求队列一次,对于相同key值的请求,直接从缓存中取出即可,如果缓存内部没有数据,那么通过判断当前是否有这类的请求...如果有,直接进行赋值,如果没有定义一个新的请求...

3.NetWorkImageLoader.java

  第三种加载图片的方式...它基于ImageLoader,加载图片也是通过ImageLoader来实现的,只是在一些地方有一定的差异...

package com.android.volley.toolbox;

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.ImageContainer;
import com.android.volley.toolbox.ImageLoader.ImageListener; public class NetworkImageView extends ImageView {
/** The URL of the network image to load */
private String mUrl; //图片的url /**
* Resource ID of the image to be used as a placeholder until the network image is loaded.
*/
private int mDefaultImageId; //默认图片的ID.. /**
* 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; //ImageContainer对象.. public NetworkImageView(Context context) {
this(context, null);
} public NetworkImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public NetworkImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
} //设置图片的url...
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);
} //设置默认图片...
public void setDefaultImageResId(int defaultImage) {
mDefaultImageId = defaultImage;
} //设置错误图片...
public void setErrorImageResId(int errorImage) {
mErrorImageId = errorImage;
} //这个函数在OnLauout调用时才被调用...用于加载图片...
private void loadImageIfNecessary(final boolean isInLayoutPass) {
//获取长度和宽度...
int width = getWidth();
int height = getHeight(); boolean isFullyWrapContent = getLayoutParams() != null
&& getLayoutParams().height == LayoutParams.WRAP_CONTENT
&& getLayoutParams().width == LayoutParams.WRAP_CONTENT;
//如果view控件的大小界限是不确定的,并且还不是wrap_content属性,那么放弃加载...
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.
//如果这个url被加载后,view是空的,那么就撤销掉与之相同的所有请求...
if (TextUtils.isEmpty(mUrl)) {
if (mImageContainer != null) {
mImageContainer.cancelRequest();
mImageContainer = null;
}
setImageBitmap(null);
return;
} // if there was an old request in this view, check if it needs to be canceled.
//如果请求是相同的url,那么直接return,数据的获取由缓存处理...
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();
setImageBitmap(null);
}
} // The pre-existing content of this view didn't match the current URL. Load the new image
// from the network.
//如果不满足上述情况,那么就建立一个新的ImageContainer来保存...
ImageContainer newContainer = mImageLoader.get(mUrl,
new ImageListener() {
@Override
public void onErrorResponse(VolleyError error) {
if (mErrorImageId != 0) {
setImageResource(mErrorImageId);
}
} @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());
} else if (mDefaultImageId != 0) {
setImageResource(mDefaultImageId);
}
}
}); // update the ImageContainer to be the new bitmap container.
mImageContainer = newContainer;
}
//布局函数调用...
@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();
}
}

  这是源码的实现过程,有些细节上的问题就不进行分析了...我们还是来看一下实例...

package com.example.oop;

import com.android.volley.RequestQueue;
import com.android.volley.toolbox.ImageLoader;
import com.android.volley.toolbox.ImageLoader.ImageCache;
import com.android.volley.toolbox.NetworkImageView;
import com.android.volley.toolbox.Volley; import android.os.Bundle;
import android.app.Activity;
import android.graphics.Bitmap;
import android.support.v4.util.LruCache;
import android.view.Menu;
import android.view.View;
import android.view.View.OnClickListener; public class MainActivity extends Activity implements OnClickListener { String url="http://192.168.199.172:8080/JSP/imageview.jpg";
NetworkImageView imageView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
imageView=(NetworkImageView) findViewById(R.id.network);
init();
} public void init(){
RequestQueue queue=Volley.newRequestQueue(MainActivity.this);
ImageLoader imageLoader=new ImageLoader(queue, new ImageCache() { LruCache<String,Bitmap>cache=new LruCache<String, Bitmap>(((int) Runtime.getRuntime().maxMemory())/8){ @Override
protected int sizeOf(String key,Bitmap bitmap){
return bitmap.getRowBytes()*bitmap.getHeight();
}
}; @Override
public void putBitmap(String url, Bitmap bitmap) {
// TODO Auto-generated method stub
cache.put(url, bitmap);
} @Override
public Bitmap getBitmap(String url) {
// TODO Auto-generated method stub
return cache.get(url);
}
});
imageView.setDefaultImageResId(R.drawable.default_image); //这里需要手动设置默认图片和错误显示图片...
imageView.setErrorImageResId(R.drawable.no_image);
//加载获取图片的过程...
imageView.setImageUrl(url, imageLoader);
} @Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
} @Override
public void onClick(View v) {
// TODO Auto-generated method stub } }

 需要说的是,布局文件中我们需要这样进行书写去定义一个NetWorkImageView对象...通过获取ID,来进行数据信息的加载...

<com.android.volley.toolbox.NetworkImageView
android:id="@+id/network"
android:layout_height="100dip"
android:layout_width="100dip">
</com.android.volley.toolbox.NetworkImageView>