Android 异步图片加载+RecylerView瀑布流

效果图



工程目录



依赖库



Android Studio 项目:http://pan.baidu.com/s/1hr8KZTq


MainActivity.java

import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.ImageView;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import okhttp3.Call;
import okhttp3.Callback;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;

public class MainActivity extends Activity {
    private final int COLNUM = 2;   //列数

    private RecyclerView recyclerView;

    private ArrayList<String> imgList = new ArrayList<>();      //图片的url
    private ArrayList<Integer> heightList = new ArrayList<>();  //item高度
    private MyImageLoader imageLoader;
    private Handler mHandler = new Handler(){
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            //为ImageView设置下载好的图片
            if (MyImageLoader.WHAT == msg.what){
                MyImageLoader.ImageBean bean = (MyImageLoader.ImageBean) msg.obj;
                if (null != bean){
                    imageLoader.addBitmapToCache(bean.url, bean.bitmap);
                    //防止图片错位
                    if (bean.url.equals(bean.imageView.getTag())) {
                        bean.imageView.setImageBitmap(bean.bitmap);
                    }
                }
            }
            //图片路径抓取完成,设置适配器
            else if (1 == msg.what){
                recyclerView.setAdapter(new MyAdapter(imgList));
            }
        }
    };

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        recyclerView = (RecyclerView) findViewById(R.id.recyclerView);

        imageLoader = new MyImageLoader(mHandler){
            @Override
            public Bitmap loadBitmap(ImageBean bean) {
                Bitmap bitmap = null;
                try {
                    //从URL流读取图片
                    URL url = new URL(bean.url);
                    bitmap = BitmapFactory.decodeStream(url.openStream());
                }catch (Exception e){
                    e.printStackTrace();
                }
                return bitmap;
            }
        };

        //抓取图片
        OkHttpClient client = new OkHttpClient();
        Request request = new Request.Builder()
                .url("http://image.baidu.com/search/index?tn=baiduimage&ct=201326592&lm=-1&cl=2&ie=gbk&word=%C3%C0%C5%AE&ala=1&fr=ala&alatpl=cover&pos=0#z=0&pn=&ic=0&st=-1&face=0&s=0&lm=-1")
                .build();
        Call call = client.newCall(request);
        call.enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {

            }

            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String html = response.body().string();

                //用正则表达式匹配网页中图片的地址
                Pattern pattern = Pattern.compile("hoverURL\":\"[^\"]+");
                Matcher matcher = pattern.matcher(html);
                while (matcher.find()) {
                    String s = matcher.group();
                    imgList.add(s.substring(s.indexOf("http"), s.length()));
                }

                //匹配图片宽度
                pattern = Pattern.compile("\"width\":[0-9]+");
                matcher = pattern.matcher(html);
                String matcherStr = "\"width\":";
                int[] widths = new int[imgList.size()];
                int i = 0;
                while (matcher.find()){
                    String s = matcher.group();
                    try {
                        int w = Integer.valueOf(s.substring(matcherStr.length(), s.length()));
                        widths[i++] = w;
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }

                //匹配图片高度
                pattern = Pattern.compile("\"height\":[0-9]+");
                matcher = pattern.matcher(html);
                matcherStr = "\"height\":";
                int[] heights = new int[imgList.size()];
                i = 0;
                while (matcher.find()){
                    String s = matcher.group();
                    try {
                        int h = Integer.valueOf(s.substring(matcherStr.length(), s.length()));
                        heights[i++] = h;
                    }catch (Exception e){
                        e.printStackTrace();
                    }
                }

                //计算缩放item的实际高度
                int size = widths.length;
                double screenWidth = ((WindowManager) getSystemService(WINDOW_SERVICE)).getDefaultDisplay().getWidth();
                double itemWidth = screenWidth / COLNUM;
                for (i = 0; i < size; i++){
                    //计算宽高比例
                    double scale = heights[i] / (double) widths[i];
                    heightList.add((int) (scale * itemWidth));
                }

                mHandler.sendEmptyMessage(1);
            }
        });

        recyclerView.setLayoutManager(new StaggeredGridLayoutManager(COLNUM, StaggeredGridLayoutManager.VERTICAL));
    }

    private class MyAdapter extends RecyclerView.Adapter<MyAdapter.MyViewHolder>{

        private ArrayList<String> dataList;

        public MyAdapter(ArrayList<String> dataList){
            this.dataList = dataList;
        }

        @Override
        public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(MainActivity.this).inflate(R.layout.item, parent, false);
            MyViewHolder holder = new MyViewHolder(view);
            return holder;
        }

        @Override
        public void onBindViewHolder(MyViewHolder holder, int position) {
            //设置图片大小
            ViewGroup.LayoutParams params = holder.itemView.getLayoutParams();
            params.height = heightList.get(position);
            holder.itemView.setLayoutParams(params);

            holder.iv.setImageBitmap(null);
            //防止图片错位
            holder.iv.setTag(dataList.get(position));
            imageLoader.add(new MyImageLoader.ImageBean(holder.iv, dataList.get(position)));
        }

        @Override
        public int getItemCount() {
            return dataList.size();
        }

        class MyViewHolder extends RecyclerView.ViewHolder{
            public ImageView iv;

            public MyViewHolder(View itemView) {
                super(itemView);
                iv = (ImageView) itemView.findViewById(R.id.iv);
            }
        }
    }
 }

页面的抓取用了okhttp库

图片的链接和对应的宽高都是从百度图片根据“美女”关键字搜索得出的页面中抓取的

在onBindViewHolder方法中根据heightList来设置itemView的高度来实现瀑布流效果


MyImageLoader.java

import android.graphics.Bitmap;
import android.os.Handler;
import android.os.Message;
import android.support.v4.util.LruCache;
import android.widget.ImageView;

import java.util.ArrayDeque;
import java.util.Deque;

/**
 * 异步图片加载
 */
public abstract class MyImageLoader{
    public static final int WHAT = 98791981;                    //msg.what
    private final int CACHE_PART = 8;                           //把可用内存的 1/CACHE_PART 用于图片缓存

    private final Deque<ImageBean> deque = new ArrayDeque<>();  //等待队列
    private Handler handler;                                    //UI线程的Handler
    private Type loadType = Type.LIFO;                          //加载方式,默认为后进先出
    private LruCache<String, Bitmap> imgCache;                  //图片缓存
    private final Thread mThread = new Thread(){                //加载线程
        @Override
        public void run() {
            super.run();
            while (true){
                while(!deque.isEmpty()){
                    ImageBean bean;
                    synchronized (deque) {
                        //先进先出,取出队列的尾部
                        if (Type.FIFO == loadType){
                            bean = deque.removeLast();
                        }
                        //后进先出,取出队列的首部
                        else {
                            bean = deque.removeFirst();
                        }
                    }
                    //加载Bitmap
                    Bitmap bitmap = loadBitmap(bean);

                    //通知UI线程更新图片
                    if (null != bitmap) {
                        Message msg = new Message();
                        msg.what = WHAT;
                        bean.bitmap = bitmap;
                        msg.obj = bean;
                        handler.sendMessage(msg);
                    }
                }

                //没有任务则让线程休眠
                synchronized (this){
                    try {
                        wait();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        }
    };

    /**
     * @param handler UI线程的Handler
     */
    public MyImageLoader(Handler handler){
        this.handler = handler;
        //设置图片缓存
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cacheSize = maxMemory / CACHE_PART;
        imgCache = new LruCache<String, Bitmap>(cacheSize){
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount() / 1024;
            }
        };

        //开始加载线程
        mThread.start();
    }

    /**
     * 添加任务
     */
    public void add(ImageBean bean){
        Bitmap bitmap = getBitmapFromCache(bean.url);
        if (null != bitmap){
            Message msg = new Message();
            msg.what = WHAT;
            bean.bitmap = bitmap;
            msg.obj = bean;
            handler.sendMessage(msg);
        }

        synchronized (deque){
            deque.addFirst(bean);
        }

        if (Thread.State.WAITING == mThread.getState()){
            synchronized (mThread) {
                mThread.notify();
            }
        }
    }

    /**
     * 设置加载方式
     * @param type
     */
    public void setLoadType(Type type){
        if (null != type) {
            this.loadType = type;
        }
    }

    /**
     * 添加到缓存
     */
    public void addBitmapToCache(String key, Bitmap bitmap){
        if (null != key && null != bitmap){
            imgCache.put(key, bitmap);
        }
    }

    /**
     * 从缓存中取出
     */
    public Bitmap getBitmapFromCache(String key){
        return imgCache.get(key);
    }

    /**
     * 加载Bitmap的方法
     */
    public abstract Bitmap loadBitmap(ImageBean bean);

    public static class ImageBean{
        public ImageView imageView;
        public String url;
        public Bitmap bitmap;

        public ImageBean(ImageView imageView, String url) {
            this.imageView = imageView;
            this.url = url;
        }

    }

    public enum Type{
        FIFO,   //先进先出
        LIFO    //后进先出
    }
}

用Thread+Handler实现图片的异步加载,并且使用了LruCache来缓存图片


activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</LinearLayout>

item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="1px"
    android:background="#8A8A8A"
    android:gravity="center">

    <ImageView
        android:id="@+id/iv"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

</LinearLayout>



  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值