recyclerview实现瀑布流

 

瀑布流类似小红书App的界面  如下:

原理(知识点):

图片宽度相同,但是图片的高度不同,如果后台上传的图片规定了尺寸的话就直接添加到imageview就可以了,如果没有的话就需要自己对图片进行等比压缩,压缩成宽度是屏幕的一半

方法如下:

 /**
     * 图片等比例压缩,按指定宽度压缩
     *
     * @param is
     * @param trgetWidth 期望的宽
     * @param out 用于磁盘缓存的输出流                  
     * @return
     */
    public static boolean compressBitemap(InputStream is, int trgetWidth,OutputStream out) {

        byte[] datas = getBytesFromStream(is);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(datas, 0, datas.length, options);
        //int outHeigh=options.outHeight;
        int outWidth = options.outWidth;
        //计算比例(图片原始高度和目标高度的比例)
        int blw = Math.round(outWidth / trgetWidth);
        //使用比较大的比例
        int bl = blw;
        //如果比例小于等于0,表示图片不进行压缩
        if (bl <= 0) {
            bl = 1;
        }
        //压缩比例
        options.inSampleSize = bl;
        options.inJustDecodeBounds = false;
        //压缩
        Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length, options);
        if (bitmap!= null){
            //计算拉伸比例
            float i = ((float) trgetWidth / bitmap.getWidth());//拉伸比例
            Matrix matrix = new Matrix();
            matrix.postScale(i, i);
            //缩放成指定宽度的图片(newBmp就是压缩后宽度为屏幕一半的图片)
            Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
            /**存储到磁盘===============*/
            //bitmap转换成InputStream
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //无损拉伸/缩放
            newBmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            InputStream isBm = new ByteArrayInputStream(baos.toByteArray());
            BufferedInputStream bis=null;
            BufferedOutputStream bos=null;
            try{
                bis=new BufferedInputStream(isBm);
                bos=new BufferedOutputStream(out);
                int len;
                while ((len=bis.read())!=-1){
                    bos.write(len);
                }
                bos.flush();//刷新缓存
                return true;

            }catch (Exception e){
                e.printStackTrace();
            }
        }else{
            Log.i(tag,"图片为空");
        }
        return false;
    }

 

 

其他: 

剩下的就没什么说的了,基本和在recyclerview显示图片一样

我写的时候就2用到2个知识点

1是图片的压缩

2是图片的缓存在这里我用了内存缓存和磁盘缓存,防止oom

完整代码 

Activity代码:

 

 

 

public class Test6Activity extends Activity {
    private Context mContext;

    private RecyclerView rv_falls;
    //图片链接
    private String[] mDatas={
            "https://ss3.bdstatic.com/70cFv8Sh_Q1YnxGkpoWK1HF6hhy/it/u=2056059545,3075726884&fm=27&gp=0.jpg",
            "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1240426408,3396216424&fm=27&gp=0.jpg",
            "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1765208127,2618259413&fm=27&gp=0.jpg",
            "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1447507835,3654535229&fm=27&gp=0.jpg",
            "https://ss2.bdstatic.com/70cFvnSh_Q1YnxGkpoWK1HF6hhy/it/u=2716881984,3272848008&fm=27&gp=0.jpg",
            "https://ss1.bdstatic.com/70cFvXSh_Q1YnxGkpoWK1HF6hhy/it/u=4056436047,3626959226&fm=27&gp=0.jpg",
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1519994047809&di=5b646d6c9d8fca47ff0749aeebf46fba&imgtype=0&src=http%3A%2F%2Fimg4.duitang.com%2Fuploads%2Fitem%2F201407%2F20%2F20140720201056_HmZ4d.jpeg",
            "https://ss0.bdstatic.com/70cFuHSh_Q1YnxGkpoWK1HF6hhy/it/u=1286656969,4109636359&fm=27&gp=0.jpg",
            "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=1928187901,3300785708&fm=27&gp=0.jpg",
            "https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3941443566,3889552161&fm=27&gp=0.jpg",
            "https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1519994047808&di=af68834c33967cd6a82bd047fbf8f809&imgtype=0&src=http%3A%2F%2Fimg5.duitang.com%2Fuploads%2Fitem%2F201310%2F23%2F20131023105257_zNeA3.jpeg",
            "https://ss0.bdstatic.com/70cFvHSh_Q1YnxGkpoWK1HF6hhy/it/u=1820318310,3796454650&fm=27&gp=0.jpg",
            "http://img07.tooopen.com/images/20170316/tooopen_sy_201956178977.jpg",
            "http://img.zcool.cn/community/01638059302785a8012193a36096b8.jpg@2o.jpg",
            "http://pic71.nipic.com/file/20150610/13549908_104823135000_2.jpg",
            "http://pic2.nipic.com/20090424/1242397_110033072_2.jpg",
            "http://pic2.ooopic.com/12/22/95/08bOOOPICe2_1024.jpg",
            "http://img05.tooopen.com/images/20140326/sy_57640132134.jpg",
            "http://www.taopic.com/uploads/allimg/140421/318743-140421213T910.jpg",
            "http://news.cnhubei.com/ctjb/ctjbsgk/ctjb40/200808/W020080822221006461534.jpg",
            "http://img3.redocn.com/tupian/20150430/mantenghuawenmodianshiliangbeijing_3924704.jpg",
            "http://d.hiphotos.baidu.com/zhidao/pic/item/72f082025aafa40fe871b36bad64034f79f019d4.jpg",
            "http://pic40.nipic.com/20140424/13846002_113008517141_2.jpg",
            "http://i9.download.fd.pchome.net/t_960x600/g1/M00/0B/10/oYYBAFQlOmuIZDQRAALvMZ8mYRIAAB9HAKQEtcAAu9J875.jpg",
            "http://img2.imgtn.bdimg.com/it/u=834235734,679217072&fm=27&gp=0.jpg"};
    private My6Adapter mAdapter;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_6);
        mContext=this;
        initView();
        initData();
    }

    private void initView() {
        rv_falls = (RecyclerView) findViewById(R.id.rv_falls);
    }

    private void initData() {
        LinearLayoutManager manager = new LinearLayoutManager(mContext);
        //设置网格布局,实现瀑布流
        rv_falls.setLayoutManager(new StaggeredGridLayoutManager(2,  StaggeredGridLayoutManager.VERTICAL));
        mAdapter = new My6Adapter(mContext,mDatas,rv_falls);
        rv_falls.setAdapter(mAdapter);
    }
    //在Activity关闭的时候清除任务栈
    @Override
    protected void onDestroy() {
        super.onDestroy();
        mAdapter.clearAsync();
    }
}

Activity布局: 

<?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/rv_falls"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>
</LinearLayout>

Adapter代码: 

public class My6Adapter extends RecyclerView.Adapter {

    private Context mContext;
    private String tag = "My6Adapter";
    private String[] mDatas;
    private LayoutInflater mInflater;
    private RecyclerView mRlv;
    //内存缓存
    private LruCache<String, Bitmap> mLruCache;
    //图片本地磁盘缓存
    private DiskLruCache mDiskLruCache;
    //管理异步任务
    private Set<BitmapAsync> taskSet;

    //屏幕宽度
    private int width;

    public My6Adapter(Context context, String[] datas, RecyclerView mRlv) {
        mContext = context;
        mDatas = datas;
        mInflater = LayoutInflater.from(context);
        this.mRlv = mRlv;
        width = ((Activity) mContext).getWindowManager().getDefaultDisplay().getWidth();
        //内存缓存初始化
        int maxMemory = (int) (Runtime.getRuntime().maxMemory() / 1024);
        int cache = maxMemory / 8;
        mLruCache = new LruCache<String, Bitmap>(cache) {
            @Override
            protected int sizeOf(String key, Bitmap value) {
                return value.getByteCount() / 1024;
            }
        };
        //磁盘存储
        File file = DiskUtils.getDiskCacheDir(context, "bitmap");
        try {
            mDiskLruCache = DiskLruCache.open(file, DiskUtils.getAppVersion(context), 1, 10 * 1024 * 1024);
        } catch (IOException e) {
            e.printStackTrace();
        }
        taskSet = new HashSet<BitmapAsync>();
    }

    //添加,添加前先判断是否存在,不存在则添加
    public void addBitmapForLruCache(String key, Bitmap bitmap) {
        if (getLruCacheBitmap(key) == null) {
            //
            //Log.i(tag,"写入内存缓存");
            mLruCache.put(key, bitmap);
        }
    }

    //获取内存缓存图片
    private Bitmap getLruCacheBitmap(String url) {
        return mLruCache.get(url);
    }

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
        View view = mInflater.inflate(R.layout.item6, parent, false);
        return new My6ViewHolder(view);
    }

    @Override
    public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
        String url = mDatas[position];
        //设置tag
        ((My6ViewHolder) holder).iv_6.setTag(url);
        Bitmap bitmap = mLruCache.get(url);
        ((My6ViewHolder) holder).tv_count.setText(""+position);
        if (bitmap == null) {
            BitmapAsync async = new BitmapAsync();
            async.executeOnExecutor(AsyncTask.SERIAL_EXECUTOR, url, width / 2, position);
            taskSet.add(async);
        } else {
            Log.i(tag, "从内存中获取图片");
            if (((My6ViewHolder) holder).iv_6.getTag().equals(url)) {
                ((My6ViewHolder) holder).iv_6.setImageBitmap(bitmap);
            }
        }

    }

    @Override
    public int getItemCount() {
        return mDatas.length;
    }

    //清空任务
    public void clearAsync() {
        if (taskSet != null) {
            for (BitmapAsync b : taskSet) {
                b.cancel(false);
            }
        }
    }

    public void finishCache() {
        if (mDiskLruCache != null) {
            try {
                mDiskLruCache.flush();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    private class My6ViewHolder extends RecyclerView.ViewHolder {
        private ImageView iv_6;
        private TextView tv_count;

        My6ViewHolder(View itemView) {
            super(itemView);
            iv_6 = (ImageView) itemView.findViewById(R.id.iv_6);
            tv_count = (TextView) itemView.findViewById(R.id.tv_count);

        }
    }

    /**
     * 异步任务
     */
    private class BitmapAsync extends AsyncTask<Object, Void, Bitmap> {
        private String url;
        private int width;

        @Override
        protected Bitmap doInBackground(Object... params) {
            FileInputStream fileInputStream = null;
            DiskLruCache.Snapshot snapshot;
            FileDescriptor fd = null;
            url = (String) params[0];
            //期望的宽度
            width = (int) params[1];

            // 存储到磁盘缓存
            try {
                //从本地缓存中取图片,如果没有存储snapshot为空
                //对url进行加密
                String key = DiskUtils.hashKeyForDisk(url);
                snapshot = mDiskLruCache.get(key);
                if (snapshot == null) {
                    //未空则去网络上加载
                    DiskLruCache.Editor editor = mDiskLruCache.edit(key);
                    if (editor != null) {
                        //网络加载图片
                        Log.i(tag, "从网络下载");
                        OutputStream outputStream = editor.newOutputStream(0);
                        if (BitmapUtils.loadImg(url, width, outputStream)) {
                            editor.commit();//提交
                        } else {
                            editor.abort();//终止
                        }
                    }
                    //经过上面的获取,已经存储到本地了
                    snapshot = mDiskLruCache.get(key);
                }
                if (snapshot != null) {
                    Log.i(tag, "从磁盘加载,position:" + params[2]);
                    fileInputStream = (FileInputStream) snapshot.getInputStream(0);
                    fd = fileInputStream.getFD();
                }

                Bitmap bitmap = null;
                if (fd != null) {
                    //内存没释放导致
                    bitmap = BitmapFactory.decodeFileDescriptor(fd);
                    //存到内存
                    if (bitmap != null) {
                        addBitmapForLruCache(url, bitmap);
                    }
                }
                return bitmap;
            } catch (IOException e) {
                e.printStackTrace();
            } finally {
                if (fileInputStream != null && fd != null) {
                    try {
                        fileInputStream.close();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            ImageView imageView = (ImageView) mRlv.findViewWithTag(url);
            if (imageView != null && bitmap != null) {
                imageView.setImageBitmap(bitmap);
            } else {
                taskSet.remove(this);
            }
        }
    }
}

 

item布局:  

 

<?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:orientation="vertical">
    <ImageView
        android:id="@+id/iv_6"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/tv_count"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:textSize="20sp"
        android:background="#ccc"/>
</LinearLayout>

 

工具类1: 

 

public class BitmapUtils {

    private static String tag = "BitmapUtils";
    /**
     * 图片等比例压缩,按指定宽度压缩
     * 一开始就压缩一次是因为百度上的图片可能太大,导致OOM,第二次才是将图片压缩成合适的宽高
     * @param is
     * @param trgetWidth 期望的宽
     * @param out 用于磁盘缓存的输出流
     * @return
     */
    public static boolean compressBitemap(InputStream is, int trgetWidth,OutputStream out) {

        byte[] datas = getBytesFromStream(is);
        BitmapFactory.Options options = new BitmapFactory.Options();
        options.inJustDecodeBounds = true;
        BitmapFactory.decodeByteArray(datas, 0, datas.length, options);
        //int outHeigh=options.outHeight;
        int outWidth = options.outWidth;
        //计算比例(图片原始高度和目标高度的比例)
        int blw = Math.round(outWidth / trgetWidth);
        //使用比较大的比例
        int bl = blw;
        //如果比例小于等于0,表示图片不进行压缩
        if (bl <= 0) {
            bl = 1;
        }
        options.inSampleSize = bl;
        options.inJustDecodeBounds = false;
        //等比例压缩
        Bitmap bitmap = BitmapFactory.decodeByteArray(datas, 0, datas.length, options);
        if (bitmap!= null){
            //计算拉伸比例
            float i = ((float) trgetWidth / bitmap.getWidth());//拉伸比例
            Log.i(tag, "缩放比例i:" + i);
            Matrix matrix = new Matrix();
            matrix.postScale(i, i);
            //缩放成指定宽度的图片(newBmp就是压缩后宽度为屏幕一半的图片)
            Bitmap newBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmap.getWidth(), bitmap.getHeight(), matrix, false);
            /**存储到磁盘===============*/
            //bitmap转换成InputStream
            ByteArrayOutputStream baos = new ByteArrayOutputStream();
            //无损压缩
            newBmp.compress(Bitmap.CompressFormat.JPEG, 100, baos);
            InputStream isBm = new ByteArrayInputStream(baos.toByteArray());
            BufferedInputStream bis=null;
            BufferedOutputStream bos=null;
            try{
                bis=new BufferedInputStream(isBm);
                bos=new BufferedOutputStream(out);
                int len;
                while ((len=bis.read())!=-1){
                    bos.write(len);
                }
                bos.flush();//刷新缓存
                return true;
            }catch (Exception e){
                e.printStackTrace();
            }
        }else{
            Log.i(tag,"图片为空");
        }
        return false;
    }

    /**
     * 将输入流转换为字节数组
     *
     * @param is
     * @return
     */
    public static byte[] getBytesFromStream(InputStream is) {
        byte[] datas = null;
        ByteArrayOutputStream os = new ByteArrayOutputStream();
        byte[] buffer = new byte[1024];
        int len = 0;
        try {
            while ((len = is.read(buffer, 0, buffer.length)) != -1) {
                os.write(buffer, 0, len);
            }
            datas = os.toByteArray();
        } catch (Exception e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } finally {
            try {
                os.close();
                is.close();
            } catch (IOException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
        return datas;
    }
    /**
     * 从网络上加载载图片,并存储到缓存
     *
     * @param url
     * @param width 期望的宽
     */
    public static boolean loadImg(String url, final int width, OutputStream outputStream) {
        BufferedInputStream bis=null;
        BufferedOutputStream bos=null;
        try {

            URL url1 = new URL(url);
            HttpURLConnection conn = (HttpURLConnection) url1.openConnection();
            conn.setRequestMethod("GET");
            conn.setConnectTimeout(10 * 1000);
            int code = conn.getResponseCode();
            if (code == 200) {
                InputStream inputStream = conn.getInputStream();
//                bis=new BufferedInputStream(inputStream);
//                bos=new BufferedOutputStream(outputStream);
//                int len;
//                while ((len=bis.read())!=-1){
//                    bos.write(len);
//                }
//                bos.flush();//刷新缓存
                return compressBitemap(inputStream,width,outputStream);
            } else {
                Log.i(tag, "输入流为空");
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

}

工具类2: 

public class DiskUtils {
    //文件路径
    public static File getDiskCacheDir(Context context, String uniqueName) {
        String cachePath;
        if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())
                || !Environment.isExternalStorageRemovable()) {
            cachePath = context.getExternalCacheDir().getPath();
        } else {
            cachePath = context.getCacheDir().getPath();
        }
        return new File(cachePath + File.separator + uniqueName);
    }

    /**
     * 获取应用的版本号
     * @param context
     * @return
     */
    public static int getAppVersion(Context context) {
        try {
            PackageInfo info = context.getPackageManager().getPackageInfo(context.getPackageName(), 0);
            return info.versionCode;
        } catch (PackageManager.NameNotFoundException e) {
            e.printStackTrace();
        }
        return 1;
    }
    /**
     * 用md5算法对字符串加密
     * @param key
     * @return
     */
    public static String hashKeyForDisk(String key){
        String cacheKey;
        try {
            MessageDigest md5 = MessageDigest.getInstance("MD5");
            md5.update(key.getBytes());
            cacheKey=bytesToHexString(md5.digest());
        } catch (NoSuchAlgorithmException e) {
            e.printStackTrace();
            cacheKey=String.valueOf(key.hashCode());
        }
        return cacheKey;
    }

    /**
     * 将bytes数组转换为string类型
     * @param bytes
     * @return
     */
    public static String bytesToHexString(byte[] bytes){
        StringBuilder sb=new StringBuilder();
        for (int i=0;i<bytes.length;i++){
            String hex=Integer.toHexString(0xFF & bytes[i]);
            if (hex.length()==1){//
                sb.append("0");
            }
            sb.append(hex);
        }
        return sb.toString();
    }
}

效果图 

 

其中的DiskLrucache类下载地址(郭霖大佬上传的):
点击下载 

图片都是我直接从百度找的,样式啥的都没改,所以有点丑

代码全贴出来了,就不上传项目了。公司电脑加密了,上传了下载下来也是乱码 ,

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值