Android瀑布流,解决oom

这是一个Android瀑布流的实现demo。

瀑布流我的实现是定义三个linearlayout,然后向里面addView(),如果多了会出现oom异常,所以做了一些处理。

1.lrucache缓存

2.只显示当前屏的图片

3.滑动过程中不加载图片

4.大图缩放成小图

直接看代码:

PhotoFallScrollView.java主类 自定义的ScrollView.

package com.pangzaifei.falls;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;

/**
 * 瀑布流类
 *
 * 原理: 1:创建3个linearlayout,设置他们的宽度,将获得的图片压缩成和3个linearlayout一样的宽度,
 * 然后根据3个linearlayout的高度来判断,将bitmap添加到哪一个linearlayout中
 * 2:翻页处理,根据手势抬起的位置和滑动的末尾处来进行翻页
 *
 * @author pangzf
 * @date 2014年7月15日 上午10:33:05
 */
public class PhotoFallScrollView extends ScrollView implements OnTouchListener {
    /**
     * 页数
     */
    private static int page;
    /**
     * 每页显示多少张
     */
    private static final int PAGE_SIZE = 8;
    private Context mContext;
    /**
     * 数据源图片
     */
    private Images mImagesThoumb;
    /**
     * task请求集合
     */
    private Set<DownLoadTask> mTasks;
    boolean isFirstEntr = true;
    private LinearLayout mFirstColumn;
    private LinearLayout mSecondColumn;
    private LinearLayout mThirdColumn;
    private int mFirstColumnHeight;
    private int mSecondColumnHeight;
    private int mThirdColumnHeight;
    private int mClolumnWidth;

    private long mDelay = 5;
    /**
     * 上次滑动的最后位置
     */
    private static int lastScrollY = -1;

    /**
     * 是否已加载过一次layout,这里onLayout中的初始化只需加载一次
     */
    private boolean loadOnce;
    /**
     * 存放图片的集合
     */
    private List<ImageView> mImageViewList = new ArrayList<ImageView>();

    public PhotoFallScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.mContext = context;
        init();
    }

    public PhotoFallScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.mContext = context;
        init();
    }

    public PhotoFallScrollView(Context context) {
        super(context);
        this.mContext = context;
        init();
    }

    /**
     * 初始化
     */
    private void init() {
        mImagesThoumb = Images.getInstance();
        mTasks = new HashSet<DownLoadTask>();
        setOnTouchListener(this);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);

        // 第一次进入就加载第一页的图片
        if (changed && !loadOnce) {
            mScrollViewHeight = this.getHeight();
            mScrollLayout = this.getChildAt(0);
            mFirstColumn = (LinearLayout) findViewById(R.id.first_column);
            mSecondColumn = (LinearLayout) findViewById(R.id.second_column);
            mThirdColumn = (LinearLayout) findViewById(R.id.third_column);
            mClolumnWidth = mFirstColumn.getWidth();
            loadOnce = true;
            loadMoreImages();
        }
    }

    /**
     * 加载图片
     */
    private void loadMoreImages() {
        if (hashSdcard()) {

            // 根据页数加载图片
            int startIndex = page * PAGE_SIZE;
            int endIndex = page * PAGE_SIZE + PAGE_SIZE;

            if (startIndex < mImagesThoumb.imageThumbs.length) {
                if (endIndex > mImagesThoumb.imageThumbs.length) {
                    endIndex = mImagesThoumb.imageThumbs.length;
                }
                for (int i = startIndex; i < endIndex; i++) {
                    String imageUrl = mImagesThoumb.imageThumbs[i].toString();
                    if (imageUrl != null && !"".equals(imageUrl)) {
                        downLoadData(imageUrl);
                    }
                }
                page++;
            } else {
                Toast.makeText(mContext, "没有更多图片了", 0).show();
            }
        } else {
            Toast.makeText(mContext, "无sdcard", 0).show();
        }
    }

    /**
     * 下载
     *
     * @param imageUrl
     */
    private void downLoadData(String imageUrl) {
        DownLoadTask task = new DownLoadTask();
        mTasks.add(task);
        task.execute(imageUrl);
    }

    public class DownLoadTask extends AsyncTask<String, String, Bitmap> {

        private String mImageUrl;

        @Override
        protected Bitmap doInBackground(String... params) {
            try {
                mImageUrl = params[0];
                Bitmap bitmapFromMemory = mImagesThoumb
                        .getMemoryCache(mImageUrl);
                if (bitmapFromMemory != null) {
                    return bitmapFromMemory;
                }
                if (hashSdcard()) {
                    Bitmap bitmap = loadImage(mImageUrl);
                    return bitmap;
                } else {
                    Toast.makeText(mContext, "无sdcard,无法获取图片", 0).show();
                }

            } catch (Exception e) {
                e.printStackTrace();
            }
            return null;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            super.onPostExecute(bitmap);
            // 展示图片
            if (bitmap != null) {
                // 1.缩放图片
                // 2.新建ImageView
                // 3.找到需要的linerlayout添加imageView
                float width = bitmap.getWidth();
                float radio = width / mFirstColumn.getWidth();
                float scaleHeight = bitmap.getHeight() / radio;
                addImage(bitmap, mFirstColumn.getWidth(), scaleHeight);
            }
            mTasks.remove(this);
        }

        /**
         * 将图片添加到linearlayout中
         *
         * @param bitmap
         * @param scaleHeight
         */
        public void addImage(Bitmap bitmap, float width, float scaleHeight) {
            // 生成缩放的iv
            ImageView iv = new ImageView(mContext);
            android.view.ViewGroup.LayoutParams params = new LayoutParams(
                    (int) width, (int) scaleHeight);
            iv.setLayoutParams(params);
            if (bitmap != null) {
                // 解决默认图片有大有小的问题
                iv.setScaleType(ScaleType.FIT_XY);
                iv.setPadding(5, 5, 5, 5);

                iv.setImageBitmap(bitmap);
                iv.setTag(R.string.iamgurl, mImageUrl);
                findColumnToAdd(iv, (int) scaleHeight).addView(iv);
                mImageViewList.add(iv);
            }
        }

    }

    private Bitmap downLoad(String imageUrl) throws IOException {
        BufferedInputStream bis = null;
        FileOutputStream fos = null;
        BufferedOutputStream bos = null;
        HttpURLConnection conn = null;
        File imageFile = null;
        try {
            URL url = new URL(imageUrl);
            conn = (HttpURLConnection) url.openConnection();
            conn.setReadTimeout(10000);
            conn.setConnectTimeout(5000);
            conn.setDoInput(true);
            conn.setDoOutput(true);
            InputStream is = conn.getInputStream();
            imageFile = new File(getImagePath(imageUrl));
            bis = new BufferedInputStream(is);
            fos = new FileOutputStream(imageFile);
            bos = new BufferedOutputStream(fos);
            int len = 0;
            byte[] buffer = new byte[1024];
            while ((len = bis.read(buffer)) != -1) {
                bos.write(buffer, 0, len);
                bos.flush();
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (bis != null) {
                bis.close();
            }
            if (bos != null) {
                bos.close();
            }
            if (conn != null) {
                conn.disconnect();
            }
        }
        // 如果imageFile不为null,将图片添加到memory中
        if (imageFile != null) {
            Bitmap bitmap = BitmapFactory.decodeFile(imageFile.getPath());
            mImagesThoumb.addBitmapToMemoryCache(imageUrl, bitmap);
            return bitmap;
        }
        return null;

    }

    /**
     * 判断图片sdcard是否有图片,如果有就用,没有就下载
     *
     * @param mImageUrl
     * @return
     */
    public Bitmap loadImage(String mImageUrl) throws Exception {
        File file = new File(getImagePath(mImageUrl));
        if (!file.exists()) {
            downLoad(mImageUrl);
        }

        if (mImageUrl != null) {
            // 处理本地图片,设置大小防止oom
            Bitmap bitmap = mImagesThoumb.decodeSimpleBitMapFromResource(
                    file.getPath(), mClolumnWidth);
            // Bitmap bitmap = BitmapFactory.decodeFile(file.getPath());
            if (bitmap != null) {
                mImagesThoumb.addBitmapToMemoryCache(mImageUrl, bitmap);
                return bitmap;
            }
        }
        return null;
    }

    /**
     * 查找要添加的column
     *
     * @param iv
     */
    private LinearLayout findColumnToAdd(ImageView iv, int imageHeight) {
        if (mFirstColumnHeight <= mSecondColumnHeight) {
            if (mFirstColumnHeight <= mThirdColumnHeight) {
                iv.setTag(R.string.border_top, mFirstColumnHeight);
                mFirstColumnHeight += imageHeight;
                iv.setTag(R.string.border_bottom, mFirstColumnHeight);
                return mFirstColumn;
            }
            iv.setTag(R.string.border_top, mThirdColumnHeight);
            mThirdColumnHeight += imageHeight;
            iv.setTag(R.string.border_bottom, mThirdColumnHeight);
            return mThirdColumn;

        } else {
            if (mSecondColumnHeight <= mThirdColumnHeight) {
                iv.setTag(R.string.border_top, mSecondColumnHeight);
                mSecondColumnHeight += imageHeight;
                iv.setTag(R.string.border_bottom, mSecondColumnHeight);
                return mSecondColumn;
            }
            iv.setTag(R.string.border_top, mThirdColumnHeight);
            mThirdColumnHeight += imageHeight;
            iv.setTag(R.string.border_bottom, mThirdColumnHeight);
            return mThirdColumn;
        }
    }

    /**
     * 获得file地址
     *
     * @param imageUrl
     * @return
     */
    private String getImagePath(String imageUrl) {
        int lastIndexOf = imageUrl.lastIndexOf("/");
        String imageName = imageUrl.substring(lastIndexOf + 1);
        String imageDir = Environment.getExternalStorageDirectory().getPath()
                + "/pangzaifei/";
        File file = new File(imageDir);
        if (!file.exists()) {
            file.mkdir();
        }
        String imagePath = imageDir + imageName;
        return imagePath;
    }

    /**
     * 获得图片的名字
     *
     * @param imageUrl
     */

    private boolean hashSdcard() {
        if (Environment.getExternalStorageState().equals(
                Environment.MEDIA_MOUNTED)) {
            return true;
        }
        return false;
    }

    @Override
    /**
     * 当手势抬起时,开始每个5毫秒计算位置
     */
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            // 发送handler
            Message msg = mHandler.obtainMessage();
            msg.obj = this;
            mHandler.sendMessageDelayed(msg, mDelay);
        }
        return false;
    }

    private Handler mHandler = new Handler() {

        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            // 判断是否已经滑到了最低处,如果滑到了最低处,则加载更多页面,否则继续发送handler扫描
            PhotoFallScrollView scrollView = (PhotoFallScrollView) msg.obj;
            int scrollY = scrollView.getScrollY();
            if (scrollY == lastScrollY) {
                if (mScrollViewHeight + scrollY >= mScrollLayout.getHeight()
                        && mTasks.isEmpty()) {
                    scrollView.loadMoreImages();
                }
                scrollView.checkVisibile();
            } else {
                lastScrollY = scrollY;
                Message message = new Message();
                message.obj = scrollView;
                mHandler.sendMessageDelayed(message, mDelay);
            }
        }

    };
    private int mScrollViewHeight;
    private View mScrollLayout;

    /**
     * 想不可见的变为空图片
     */
    protected void checkVisibile() {
        if (mImageViewList != null && mImageViewList.size() > 0) {
            for (int i = 0; i < mImageViewList.size(); i++) {
                ImageView iv = mImageViewList.get(i);
                int borderTop = (int) iv.getTag(R.string.border_top);
                int borderBottom = (int) iv.getTag(R.string.border_bottom);
                if (borderBottom > getScrollY()
                        && borderTop < getScrollY() + mScrollViewHeight) {
                    String imageUrl = (String) iv.getTag(R.string.iamgurl);
                    if (imageUrl != null && !"".equals(imageUrl)) {
                        Bitmap bitmap = mImagesThoumb.getMemoryCache(imageUrl);
                        if (bitmap != null) {
                            iv.setImageBitmap(bitmap);
                        } else {
                            downLoadData(imageUrl);
                        }
                    }
                } else {
                    iv.setImageResource(R.drawable.empty_photo);
                }

            }
        }
    }
}

上面是主要的东西,思路和注释已经添加。其中imageView中的tag我要解释一下,主要保存了每一列的上边距和下边距和图片的url,这个方法,就是只显示当前页的图片。

布局文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.pangzaifei.falls.PhotoFallScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" >

        <LinearLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal" >

            <LinearLayout
                android:id="@+id/first_column"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:orientation="vertical" >
            </LinearLayout>

            <LinearLayout
                android:id="@+id/second_column"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:orientation="vertical" >
            </LinearLayout>

            <LinearLayout
                android:id="@+id/third_column"
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:orientation="vertical" >
            </LinearLayout>
        </LinearLayout>
    </com.pangzaifei.falls.PhotoFallScrollView>

</LinearLayout>

效果图:

源码地址:

http://download.csdn.net/detail/pangzaifei/7639423

Android瀑布流,解决oom

时间: 2024-10-11 00:04:46

Android瀑布流,解决oom的相关文章

转载—— android 瀑布流的实现详解,附源码

介绍 参考自:https://github.com/dodola/android_waterfall,因为原来的代码封装不好,所以,我根据源码的思路,重新写了一遍,所以有了现在这个项目:https://github.com/youxilua/waterfall4android 原作者表示: 试过在1万张可以流畅的滑动,不出现内存溢出情况 设计思路 之前的作者的自定义view 只有主滑动一层,其他的设置要在相应的活动设置,个人觉得,重用起来比较麻烦,所以决定封装一层.现在定义一个默认的瀑布流只需5

Android瀑布流照片墙实现,体验不规则排列的美感Demo

Android瀑布流照片墙实现,体验不规则排列的美感Demo.略有逼格~ 下载地址:http://www.devstore.cn/code/info/637.html

Android 瀑布流demo

原文:Android 瀑布流demo 源代码下载地址:http://www.zuidaima.com/share/1550463780342784.htm Android 中实现瀑布流列表

Android瀑布流StaggeredGridView学习研究

关于Android瀑布流控件,已经在江湖上,流传已久,超过两年时间了.网上也有很多相关学习资源,可以拿来研究学习.github上,就有两个资源,可以供学习者膜拜. 1.https://github.com/maurycyw/StaggeredGridView    此链接有图片加载功能,但功能相对简单些. 2.https://github.com/etsy/AndroidStaggeredGrid  提供的瀑布流功能强大,可以自定义瀑布流列数. 本篇博客,就讲解etsy的源码为主了.首先看效果图

Android瀑布流照片墙实现,体验不规则排列的美感

转载请注明出处:http://blog.csdn.net/guolin_blog/article/details/10470797 传统界面的布局方式总是行列分明.坐落有序的,这种布局已是司空见惯,在不知不觉中大家都已经对它产生了审美疲劳.这个时候瀑布流布局的出现,就给人带来了耳目一新的感觉,这种布局虽然看上去貌似毫无规律,但是却有一种说不上来的美感,以至于涌现出了大批的网站和应用纷纷使用这种新颖的布局来设计界面. 记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个

Android瀑布流照片

http://blog.csdn.net/guolin_blog/article/details/10470797 记得我在之前已经写过一篇关于如何在Android上实现照片墙功能的文章了,但那个时候是使用的GridView来进行布局的,这种布局方式只适用于“墙”上的每张图片大小都相同的情况,如果图片的大小参差不齐,在GridView中显示就会非常的难看.而使用瀑布流的布局方式就可以很好地解决这个问题,因此今天我们也来赶一下潮流,看看如何在Android上实现瀑布流照片墙的功能. 首先还是讲一下

android 瀑布流

我们还是来看一款示例: 看起来很像我们的gridview吧,不过又不像,因为item大小不固定的,看起来是不是别有一番风味,确实如此.就如我们的方角图形,斯通见惯后也就出现了圆角.下面我简单介绍下实现方法. 第一种: 我们在配置文件中定义好列数.如上图也就是3列.我们需要定义三个LinearLayout,然后把获取到的图片add里面就ok了. main.xml [java] view plaincopy <?xml version="1.0" encoding="utf

android 瀑布流效果(仿蘑菇街)

我们还是来看一款示例:(蘑菇街)           看起来很像我们的gridview吧,不过又不像,因为item大小不固定的,看起来是不是别有一番风味,确实如此.就如我们的方角图形,斯通见惯后也就出现了圆角.下面我简单介绍下实现方法. 第一种: 我们在配置文件中定义好列数.如上图也就是3列.我们需要定义三个LinearLayout,然后把获取到的图片add里面就ok了. main.xml [java] view plaincopy <?xml version="1.0" enc

android瀑布流效果(仿蘑菇街)

Android 转载分享(10)  我们还是来看一款示例:(蘑菇街)           看起来很像我们的gridview吧,不过又不像,因为item大小不固定的,看起来是不是别有一番风味,确实如此.就如我们的方角图形,斯通见惯后也就出现了圆角.下面我简单介绍下实现方法. 第一种: 我们在配置文件中定义好列数.如上图也就是3列.我们需要定义三个LinearLayout,然后把获取到的图片add里面就ok了. main.xml [java] view plaincopy <?xml version