NetWorkImageView源码解析
/**
* 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 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;
/**
*
*从URL中获取图像以及相关请求的生命周期把控。
*这个控件在被从父控件detach的时候, 会自动取消网络请求的,即完全不用我们担心相关网络请求的生命周期问题,
*而且NetworkImageView还会根据你对图片设置的width和heigh自动压缩该图片不会产生多的内存,减少OOM
*还有NetworkImageView在列表中使用不会图片错位
*/
public class NetworkImageView extends ImageView {
/*网络加载的图片地址*/
private String mUrl;
/**
*
*在网络加载之前用作占位符的图像的资源ID,即默认图片
*/
private int mDefaultImageId;
/**
*
* 如果网络响应失败,将显示失败的图像资源ID。
*/
private int mErrorImageId;
/*本地Volley封装的图片加载类ImageLoader*/
private ImageLoader mImageLoader;
/*当前的图片容器,负责图片暂存和清除
private ImageContainer mImageContainer;
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);
}
/**
*
*设置要加载到该视图中的图像ur,注意:通过ImageLoader可立即设置缓存的图像(如果有)或默认的图像指定
* @param url 加载图片的地址
* @param imageLoader 处理加载图片的类
*/
public void setImageUrl(String url, ImageLoader imageLoader) {
mUrl = url;
mImageLoader = imageLoader;
// 如果网址发生改变,看看是否需要加载
loadImageIfNecessary(false);
}
/**
* 设置要用于此视图的默认图像资源ID,
*/
public void setDefaultImageResId(int defaultImage) {
mDefaultImageId = defaultImage;
}
/**
*网络加载失败后显示错误图像资源ID
*/
public void setErrorImageResId(int errorImage) {
mErrorImageId = errorImage;
}
/**
* 在没有赋值之前,进行图片布局尺判断(如宽、高)
* @param isInLayoutPass 如果这是一个真正的isinlayoutpass布局通过调用,否则为假。
*/
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;
// 如果视图的边界还不知道,而且不是一个内容包裹,关闭加载图像。
if (width == 0 && height == 0 && !isFullyWrapContent) {
return;
}
// 如果在此视图中加载的URL为空,请取消任何旧请求并清除当前加载的图像。
if (TextUtils.isEmpty(mUrl)) {
if (mImageContainer != null) {
mImageContainer.cancelRequest();
mImageContainer = null;
}
setDefaultImageOrNull();
return;
}
// 如果此视图中有旧请求,请检查是否需要取消。
if (mImageContainer != null && mImageContainer.getRequestUrl() != null) {
if (mImageContainer.getRequestUrl().equals(mUrl)) {
// 如果请求来自同一URL,返回空,亲测这里设计有些小bug。
return;
} else {
// 如果它正在获取一个不同的URL。取消预先存在的请求,重设视图,
mImageContainer.cancelRequest();
setDefaultImageOrNull();
}
}
// 此视图的预先存在的内容与当前URL不匹配。从网络加载新的图片。
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) {
//如果这是一个即时的反应,不设置图像立即就会引发requestLayout方法。相反,推迟返回到主线程设置图像。
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);
}
}
});
// 更新imagecontainer成为新的位图容器
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) {
// 从视图中取消并清除/输出图像。
mImageContainer.cancelRequest();
setImageBitmap(null);
// 如有必要, 清除容器,以便我们可以重新加载图像。
mImageContainer = null;
}
super.onDetachedFromWindow();
}
/**
*图片发生改变(宽高的属性等),立即更新视图
*/
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
invalidate();
}
}
NetWorkImageView具体使用:
布局文件中的xml中使用方法:
<com.android.volley.toolbox.networkimageview
android:id="@+id/image_view"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_marginTop="10dp"/>
MainActivity网络请求展示图片
public class MainActivity extends Activity {
private String path = "http://img.kaiyanapp.com/2729ed6cef6b267d456c9aacfad67b38.jpeg";
private NetworkImageView image_view;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//初始化控件
image_view(NetworkImageView)findViewById(R.id.image_view);
//图片加载
loadImageView(this, netWorkImageView, path);
}
/**
*
* @param mainActivity
*
*/
private void loadImageView(MainActivity mainActivity, NetworkImageView iv,String url) {
//实例化imageLoader
ImageLoader imageLoader = new ImageLoader(
Volley.newRequestQueue(mainActivity.getApplicationContext()), new ImageLoader.ImageCache() {
@Override
public Bitmap getBitmap(String s) {
return null;
}
@Override
public void putBitmap(String s, Bitmap bitmap) {
}
});
iv.setDefaultImageResId(android.R.drawable.ic_menu_camera);
iv.setErrorImageResId(android.R.drawable.ic_menu_delete);
iv.setImageUrl(url, imageLoader);
}
}
总结
- 丛源码解析中可以看出,volley自带的图片控件可以显示不同状态的视图。
- 通过setImageUrl(String url, ImageLoader imageLoader)即可实现视图的展示,注意两次请求的地址一样,会返回空,不显示视图。
- 这里没有具体对图片颜色、质量等的处理,所以倘若要封装一个考虑到这些的图片控件,还是需要做些处理。