【第三篇】Volley图片加载之NetworkImageView代码分析
1,通过几个构造方法,可见NetworkImageView并没有添加自己的自定义属性,而是继承自Imageview的自定义属性。
2,然后是setImageUrl(String url,ImageLoader imageLoader),第一个参数为网络图片url,第二个参数为Imageloader对象,一般我们在开发过程中会如下使用NetWorkImageView。
1 load_img =(NetworkImageView) findViewById(R.id.load_img); 2 lruImageCache =LruImageCache.getInstance(); 3 requestQueue =Volley.newRequestQueue(this); 4 imageLoader =newImageLoader(requestQueue, lruImageCache); 5 load_img.setImageUrl(IMGURL, imageLoader);
其中setImageUrl方法中会去调用loadImageIfNecessary(false)方法;那就着重看下这个方法:
第 73行,74行,获取当前NetWorkImageView的组件宽高;
第91行到98行,如何传进来的url为null,就会取消图片加载请求,并设置默认的图片给NetWorkImageView;
调用155-166行代码,mDefaultImageId是通过setDefaultImageResId(int defaultImage)方法传递进来的,(由此可以可以看出,有必要设置一个默认的图片在NetWorkImageView中,防止在图片url为null的情况下的不友好的界面显示效果),否则调用ImagView的setImageBitmap(null)代码;
100-111行代码是判断是否该NetworkImageView重复调用过该图片url,如果重复调用return掉,如果现在调用的图片url和以前的图片url不同,cannel掉以前的图片加载,并设置默认的图片给该NetworkImageView。
mImageLoader.get方法其实要穿进去组件的宽高,里面有两个回调函数,分别处理加载成功和加载失败情况下的问题处理,如果失败调用onErrorResponse,否则调用onResponse,表示获取图片的相关信息了,然后看失败里面调用的是setErrorImageResId(int errorImage),其实这个图片也是可以从外面通过参数设置在加载图片失败的情况下的图片显示。
143-140行,如果获取到图片,按照尺寸加载图片,否则按照尺寸加载默认图片。
171-181行代码是表示当屏幕划出, cannel前面的请求。
1 public class NetworkImageView extends ImageView { 2 /** The URL of the network image to load */ 3 private String mUrl; 4 5 /** 6 * Resource ID of the image to be used as a placeholder until the network image is loaded. 7 */ 8 private int mDefaultImageId; 9 10 /** 11 * Resource ID of the image to be used if the network response fails. 12 */ 13 private int mErrorImageId; 14 15 /** Local copy of the ImageLoader. */ 16 private ImageLoader mImageLoader; 17 18 /** Current ImageContainer. (either in-flight or finished) */ 19 private ImageContainer mImageContainer; 20 21 public NetworkImageView(Context context) { 22 this(context, null); 23 } 24 25 public NetworkImageView(Context context, AttributeSet attrs) { 26 this(context, attrs, 0); 27 } 28 29 public NetworkImageView(Context context, AttributeSet attrs, int defStyle) { 30 super(context, attrs, defStyle); 31 } 32 33 /** 34 * Sets URL of the image that should be loaded into this view. Note that calling this will 35 * immediately either set the cached image (if available) or the default image specified by 36 * {@link NetworkImageView#setDefaultImageResId(int)} on the view. 37 * 38 * NOTE: If applicable, {@link NetworkImageView#setDefaultImageResId(int)} and 39 * {@link NetworkImageView#setErrorImageResId(int)} should be called prior to calling 40 * this function. 41 * 42 * @param url The URL that should be loaded into this ImageView. 43 * @param imageLoader ImageLoader that will be used to make the request. 44 */ 45 public void setImageUrl(String url, ImageLoader imageLoader) { 46 mUrl = url; 47 mImageLoader = imageLoader; 48 // The URL has potentially changed. See if we need to load it. 49 loadImageIfNecessary(false); 50 } 51 52 /** 53 * Sets the default image resource ID to be used for this view until the attempt to load it 54 * completes. 55 */ 56 public void setDefaultImageResId(int defaultImage) { 57 mDefaultImageId = defaultImage; 58 } 59 60 /** 61 * Sets the error image resource ID to be used for this view in the event that the image 62 * requested fails to load. 63 */ 64 public void setErrorImageResId(int errorImage) { 65 mErrorImageId = errorImage; 66 } 67 68 /** 69 * Loads the image for the view if it isn't already loaded. 70 * @param isInLayoutPass True if this was invoked from a layout pass, false otherwise. 71 */ 72 void loadImageIfNecessary(final boolean isInLayoutPass) { 73 int width = getWidth(); 74 int height = getHeight(); 75 76 boolean wrapWidth = false, wrapHeight = false; 77 if (getLayoutParams() != null) { 78 wrapWidth = getLayoutParams().width == LayoutParams.WRAP_CONTENT; 79 wrapHeight = getLayoutParams().height == LayoutParams.WRAP_CONTENT; 80 } 81 82 // if the view's bounds aren't known yet, and this is not a wrap-content/wrap-content 83 // view, hold off on loading the image. 84 boolean isFullyWrapContent = wrapWidth && wrapHeight; 85 if (width == 0 && height == 0 && !isFullyWrapContent) { 86 return; 87 } 88 89 // if the URL to be loaded in this view is empty, cancel any old requests and clear the 90 // currently loaded image. 91 if (TextUtils.isEmpty(mUrl)) { 92 if (mImageContainer != null) { 93 mImageContainer.cancelRequest(); 94 mImageContainer = null; 95 } 96 setDefaultImageOrNull(); 97 return; 98 } 99 100 // if there was an old request in this view, check if it needs to be canceled. 101 if (mImageContainer != null && mImageContainer.getRequestUrl() != null) { 102 if (mImageContainer.getRequestUrl().equals(mUrl)) { 103 // if the request is from the same URL, return. 104 return; 105 } else { 106 // if there is a pre-existing request, cancel it if it's fetching a different URL. 107 mImageContainer.cancelRequest(); 108 setDefaultImageOrNull(); 109 } 110 } 111 112 // Calculate the max image width / height to use while ignoring WRAP_CONTENT dimens. 113 int maxWidth = wrapWidth ? 0 : width; 114 int maxHeight = wrapHeight ? 0 : height; 115 116 // The pre-existing content of this view didn't match the current URL. Load the new image 117 // from the network. 118 ImageContainer newContainer = mImageLoader.get(mUrl, 119 new ImageListener() { 120 @Override 121 public void onErrorResponse(VolleyError error) { 122 if (mErrorImageId != 0) { 123 setImageResource(mErrorImageId); 124 } 125 } 126 127 @Override 128 public void onResponse(final ImageContainer response, boolean isImmediate) { 129 // If this was an immediate response that was delivered inside of a layout 130 // pass do not set the image immediately as it will trigger a requestLayout 131 // inside of a layout. Instead, defer setting the image by posting back to 132 // the main thread. 133 if (isImmediate && isInLayoutPass) { 134 post(new Runnable() { 135 @Override 136 public void run() { 137 onResponse(response, false); 138 } 139 }); 140 return; 141 } 142 143 if (response.getBitmap() != null) { 144 setImageBitmap(response.getBitmap()); 145 } else if (mDefaultImageId != 0) { 146 setImageResource(mDefaultImageId); 147 } 148 } 149 }, maxWidth, maxHeight); 150 151 // update the ImageContainer to be the new bitmap container. 152 mImageContainer = newContainer; 153 } 154 155 private void setDefaultImageOrNull() { 156 if(mDefaultImageId != 0) { 157 setImageResource(mDefaultImageId); 158 } 159 else { 160 setImageBitmap(null); 161 } 162 } 163 164 @Override 165 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 166 super.onLayout(changed, left, top, right, bottom); 167 loadImageIfNecessary(true); 168 } 169 170 @Override 171 protected void onDetachedFromWindow() { 172 if (mImageContainer != null) { 173 // If the view was bound to an image request, cancel it and clear 174 // out the image from the view. 175 mImageContainer.cancelRequest(); 176 setImageBitmap(null); 177 // also clear out the container so we can reload the image if necessary. 178 mImageContainer = null; 179 } 180 super.onDetachedFromWindow(); 181 } 182 183 @Override 184 protected void drawableStateChanged() { 185 super.drawableStateChanged(); 186 invalidate(); 187 } 188 }