# Volley之NetWorkImageView的源码解析与用法:

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)即可实现视图的展示,注意两次请求的地址一样,会返回空,不显示视图。
  • 这里没有具体对图片颜色、质量等的处理,所以倘若要封装一个考虑到这些的图片控件,还是需要做些处理。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值