您的位置:首页 > 其它

基于RecyclerView和Volley的瀑布流照片墙实现

2015-12-30 20:40 585 查看
博主最近在学习Volley框架,便想写个Demo练练手,于是这个基于RecyclerView和Volley的瀑布流照片墙应运而生= =。话不多说进入正题~

记得添加Gradle的dependencies,并且导入volley作为Lib。具体可以参考:http://blog.csdn.net/u010940300/article/details/44309405

RecyclerView的基本用法请参考:http://frank-zhu.github.io/android/2015/01/16/android-recyclerview-part-1/以及官方文档。

由于使用volley框架不能在获取图片之前获取具体的图片大小,因此在初始化时博主将一部分图片集体载入,之后在该部分图片全部加载完成之后再调用notifyDataSetChanged()更新RecyclerView。具体代码如下:

private void refreshLraCache(final int refreshNum) {
        //加载状态设置为未完成
        Log.d("WaterFall","refresh start");
        allLoaded=false;
        progressBar.setVisibility(View.VISIBLE);
        if(refreshNum==0){
            allLoaded=true;
            progressBar.setVisibility(View.GONE);
        }
        for(int i=itemCount;i<itemCount+refreshNum;i++){
            final int finalI = i;
            requestqueue.add(new ImageRequest(ImageURLs.imageUrls[i], new Response.Listener<Bitmap>() {
                @Override
                public void onResponse(Bitmap response) {
                    //将返回的Bitmap加入内存缓存
                    cache.put(ImageURLs.imageUrls[finalI],response);
                    ImageSize imageSize=new ImageSize(response.getWidth(),response.getHeight());
                    sizeHashMap.put(ImageURLs.imageUrls[finalI],imageSize);
                    //所有任务完成后将缓存传入Adapter并更新视图
                    taskCount++;
                    progress= (int) ((float)taskCount/(refreshNum-1)*100);
                    progressBar.setProgress(progress);
                    if(taskCount==refreshNum) {
                        adapter.setLruCache(cache);
                        adapter.setSizeHashMap(sizeHashMap);
                        //更新元素个数
                        itemCount=itemCount+refreshNum;
                        adapter.setItemCount(itemCount);
                        adapter.notifyDataSetChanged();
                        progressBar.setVisibility(View.GONE);
                        progress=0;
                        taskCount=0;
                        //加载状态设置为全部完成
                        allLoaded=true;
                        Log.d("WaterFall","refresh end");
                    }
                    Log.d("WaterFall","Task: "+finalI+" completed");
                    Log.d("WaterFall","remaining memorysize is "+(cacheSize-cache.size()));
                }
            },spanWidth,0, ImageView.ScaleType.CENTER_CROP,null,null));
        }
在触底加载更多时,依然调用该方法加载之后的图片。在图片加载之后,将Bitmap存入之前初始化的LruCache中,并将图片的大小信息封装为ImageSize类存入一个HashMap。当该部分所有图片加载完成时(即taskCount==refreshNum)将LruCache、itemCount、sizeHashMap传入adapter并调用notifyDataSetChanged()刷新,并将加载状态设置为完成(将用于触底加载的判断,当未完成加载时不会再次调用refreshCache()方法)。
private void setLruCache() {
        //获取最大缓存大小,单位M
        int maxCacheSize= (int) (Runtime.getRuntime().maxMemory()/1024);
        cacheSize=maxCacheSize/8;
        cache=new LruCache<String,Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                //重写LruCache中计算元素大小方法
                return value.getByteCount()/1024;
            }
        };
    }
public class ImageSize {
    private int imageWidth;
    private int imageHeight;
    public ImageSize(int imageWidth,int imageHeight){
        this.imageWidth=imageWidth;
        this.imageHeight=imageHeight;
    }
    public int getImageWidth(){
        return imageWidth;
    }
    public int getImageHeight(){
        return imageHeight;
    }
}
在adapter的onBindViewHolder()中,首先判断内存缓存是否存在。若存在直接显示图片,若不存在则先尝试获取图片尺寸并设置占位图片,然后再次发送volley请求下载图片。
@Override
    public void onBindViewHolder(final WaterFallVH holder, int position) {
        if(lruCache.get(ImageURLs.imageUrls[position])!=null){
            //内存缓存存在直接加载
            holder.imageView.setImageBitmap(lruCache.get(ImageURLs.imageUrls[position]));
        }else{
            //若尺寸信息缓存存在,则获取图片大小信息
            if(sizeHashMap.get(ImageURLs.imageUrls[position])!=null) {
                int[] size = getLruSize(position);
                Bitmap bitmap=resizedImage(BitmapFactory.decodeResource(context.getResources(), R.drawable.loading),size[0],size[1]);
                //加载占位图片
                holder.imageView.setImageBitmap(bitmap);
            }
            //内存缓存中不存在该元素,发送Volley请求
            sendVolleyRequest(holder,position);
        }
最后通过重写OnScrollListener中的onScrollStateChanged()方法判断是否触底以及是否开始加载更多图片。首先判断是否停止滑动,之后判断当前的加载任务是否完成,随后获取每列的最后一个可见View的位置编号,比较之后获得最大的视图编号。若编号与adapter当前的总元素个数相同,则判断已经滑动到最底部,最后判断待加载图片个数是否大于10来决定传入refreshLruCache()中的参数。当剩余图片不足10时,更新图片之后将标志位noMore设置为true,若再触底将不再调用refreshLruCache()并提示没有更多图片。

recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                //判断是否停止滚动
                if(newState==RecyclerView.SCROLL_STATE_IDLE) {
                        //判断当前加载是否完成
                        if (allLoaded == true) {
                            //得到每一列最后一个可见的元素的Position
                            int[] lastvisibalItem = layoutManager.findLastVisibleItemPositions(null);
                            int lastposition = 0;
                            if (columsCount != 1) {
                                lastposition = Math.max(lastvisibalItem[0], lastvisibalItem[1]);
                                for (int i = 2; i < columsCount; i++) {
                                    //获取整个视图可见元素中Position的最大值
                                    lastposition = Math.max(lastposition, lastvisibalItem[i]);
                                }
                            } else {
                                lastposition = lastvisibalItem[0];
                            }
                            if ((lastposition + 1) == itemCount) {
                                //当最后一个可见元素的Position与加载的元素总数相等时,判断滑到底部,更新缓存、加载更多
                                if ((lastposition + 11) <= ImageURLs.imageUrls.length) {
                                    //当还剩余十个以上元素待加载时,加载10个元素
                                    refreshLraCache(refreshSize);
                                    Toast.makeText(context, "Loading...", Toast.LENGTH_SHORT).show();
                                } else {
                                    if(!noMore) {
                                        //当剩余元素不足十个时,加载剩余元素并提示
                                        int remaining = ImageURLs.imageUrls.length - lastposition - 1;
                                        refreshLraCache(remaining);
                                        Toast.makeText(context, "Loading...", Toast.LENGTH_SHORT).show();
                                        //没有更多图片
                                        noMore = true;
                                    }else {
                                        //没有更多图片时提示
                                        Toast.makeText(context, "No more pictrues", Toast.LENGTH_SHORT).show();
                                    }
                                }
                            }
                        }
                }
大概思路就是这样了。什么?你说只有内存缓存太low了。其实volley自带diskCache,不过默认的大小只有5M,还没有内存缓存大= =。不过volley的强大之处就在于其高度的扩展性,于是博主修改了一下volley默认初始化RequestQueue类所传入的diskCache,将其改为了50M。

RequestQueue queue = new RequestQueue(new DiskBasedCache(cacheDir,diskCacheSize), network);
就这么一句就解决了,不得不服啊~volley的设计模式真的十分强大。想要了解更多:http://a.codekk.com/blogs/detail/54cfab086c4761e5001b2542

下面是效果图:





感觉和郭神的一样,我可没有盗图= =。只是我用的他DEMO中的图片地址。

下面附上DEMO地址:https://github.com/xiehaochn/WaterfallDemo

恭迎各路大神指正
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: