zoukankan      html  css  js  c++  java
  • 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>
  • 相关阅读:
    Java实现 洛谷 P1064 金明的预算方案
    (Java实现) 洛谷 P1605 迷宫
    (Java实现) 洛谷 P1031 均分纸牌
    Java实现 洛谷 P1060 开心的金明
    三代名将却三代悲剧(绝对不要轻易招惹“关系户”,要学会适应规则)
    simulate windows postmessage or keydown
    qt 拖拽 修改大小(使用了nativeEvent和winEvent)
    QT下资源使用和资源占用…(可以动态加载资源文件,这样不占内存)
    qt实现头像上传功能(写了4个类,朝十晚八的博客,非常好)
    自定义ModelValidatorProvider
  • 原文地址:https://www.cnblogs.com/RGogoing/p/4909598.html
Copyright © 2011-2022 走看看