• android瀑布流照片墙实现代码详解


    照片墙的实现,是需要往手机里面添加很多图片的,如果没有对资源进行合理的释放,程序很快就会出现OOM.所以需要用到LruCache算法来缓存图片.

    1,首先是图片资源类,这个类中包含了很多图片链接.
    public class AllImages {
        public final static String[] imageUrls = new String[] {
                "//img-my.csdn.net/uploads/201309/01/1378037235_3453.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037235_7476.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037235_9280.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037234_3539.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037234_6318.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037194_2965.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037193_1687.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037193_1286.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037192_8379.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037178_9374.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037177_1254.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037177_6203.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037152_6352.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037151_9565.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037151_7904.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037148_7104.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037129_8825.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037128_5291.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037128_3531.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037127_1085.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037095_7515.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037094_8001.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037093_7168.jpg",
                "//img-my.csdn.net/uploads/201309/01/1378037091_4950.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949643_6410.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949642_6939.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949630_4505.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949630_4593.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949629_7309.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949629_8247.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949615_1986.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949614_8482.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949614_3743.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949614_4199.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949599_3416.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949599_5269.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949598_7858.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949598_9982.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949578_2770.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949578_8744.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949577_5210.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949577_1998.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949482_8813.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949481_6577.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949480_4490.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949455_6792.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949455_6345.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949442_4553.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949441_8987.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949441_5454.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949454_6367.jpg",
                "//img-my.csdn.net/uploads/201308/31/1377949442_4562.jpg" };
    }
    2.有了图片,还需要一个图片的工具类.新建一个ImageTools.
    //图片处理的工具类
    public class ImageTools {
        //图片缓存,用于缓存所有下载好的图片
        private static LruCache<String, Bitmap> mMemoryCache;
        
        //ImageTools实例
        private static ImageTools imageTools;
        
        public ImageTools() {
            //获取应用程序的最大可用内存
            int maxMemory=(int)Runtime.getRuntime().maxMemory();
            int cacheMemory=maxMemory/8;
            mMemoryCache=new LruCache<String, Bitmap>(cacheMemory){
                @Override
                protected int sizeOf(String key, Bitmap bitmap) {
                    return bitmap.getByteCount();
                }
            };
        }
        //获取ImageTools的实例
        public static ImageTools getInstance(){
            if(imageTools==null){
                imageTools=new ImageTools();
            }
            return imageTools;
        }
        //将图片放入LruCache中
        public void addBitmapToMemoryCache(String key,Bitmap bitmap){
            if(getBitmapFromMemoryCache(key)==null){
                mMemoryCache.put(key, bitmap);
            }
        }
        //从LruCache中获取一张图片,如果不存在就返回null
        public Bitmap getBitmapFromMemoryCache(String key){
            return mMemoryCache.get(key);
        }
        //求出图片需要压缩的比例
        public static int calculateInSampleSize(BitmapFactory.Options option,int reqWidth){
            //得到原图片的宽度
            final int width=option.outWidth;
            int inSampleSize=1;
            if(width>reqWidth){
                //计算出实际宽度与目标宽度的比例
                final int widthRadio=Math.round((float)width/(float)reqWidth);
                inSampleSize=widthRadio;
            }
            return inSampleSize;
        }
        //压缩图片
        public static Bitmap decodeSampedBitmapFromResource(String pathName,int reqWidth){
            // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
            final BitmapFactory.Options options=new Options();
            options.inJustDecodeBounds=true;
            BitmapFactory.decodeFile(pathName, options);
            options.inSampleSize = calculateInSampleSize(options, reqWidth);
            // 使用获取到的inSampleSize值再次解析图片
            options.inJustDecodeBounds = false;
            return BitmapFactory.decodeFile(pathName, options);
        }
    }
    这里将ImageTools设为单例,并在构造函数中初始化了LruCache类,提供了操作LruCache的读取和增加方法  以及两个对图片进行处理的方法.
    3.接下来就是最重要的MyScrollView类.核心类.
    public class MyScrollView extends ScrollView  implements OnTouchListener {
        //每页要加载的图片数量
        public static final int PAGE_SIZE=15;
        //记录当前加载到了第几页
        private int page;
        //每一列的宽度
        private int columnWidth;
        //第一列的高度
        private int firstColumnHeight;
        //第二列的高度
        private int secondColumnHeight;
        //第三列的高度
        private int thirdColumnHeight;
        //是否已经加载过Layout
        private boolean loadOnce;
        //对图片进行管理的工具类
        private ImageTools imageTools;
        //第一列的布局
        private LinearLayout firstColumn;
        //第二列布局
        private LinearLayout secondColumn;
        //第三列布局
        private LinearLayout thirdColumn;
        //MyScrollView下的子布局
        private static View scrollLayout;
        //MyScrollView的高度
        private static int scrollViewHeight;
        //记录上次垂直滚动的距离
        private static int lastScrollY=-1;
        //记录界面上所有的图片
        private List<ImageView> imageList=new ArrayList<ImageView>();
        
        //记录所有正在下载的任务
        private static Set<LoadImageTask> imageTaskList;
        
        private static Handler mh=new Handler(){
            @Override
            public void handleMessage(Message msg) {
                MyScrollView mScrollView=(MyScrollView) msg.obj;
                int scrollY=mScrollView.getScrollY();
                if(scrollY==lastScrollY){  //如果当前滚动位置与上次滚动位置相同,则说明滚动停止
                    // 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片
                    if(scrollViewHeight+scrollY>=scrollLayout.getHeight()&&imageTaskList.isEmpty()){
                        mScrollView.loadMoreImage();
                    }
                    mScrollView.checkVisiblility();
                }
                else{
                    lastScrollY=scrollY;
                    Message message = new Message();
                    message.obj = mScrollView;
                    // 5毫秒后再次对滚动位置进行判断
                    mh.sendMessageDelayed(message, 5);
                }
            }
        };
        
        public MyScrollView(Context context, AttributeSet attrs) {
            super(context, attrs);
            imageTools=ImageTools.getInstance();
            imageTaskList=new HashSet<MyScrollView.LoadImageTask>();
            setOnTouchListener(this);
        }
        
        @Override
        public boolean onTouch(View v, MotionEvent event) {
            if(event.getAction()==MotionEvent.ACTION_UP){
                Message msg=new Message();
                msg.obj=this;
                mh.sendMessageDelayed(msg,5);
            }
            return false;
        }
        
        @Override
        protected void onLayout(boolean changed, int l, int t, int r, int b) {
            super.onLayout(changed, l, t, r, b);
            if(changed&&!loadOnce){
                scrollViewHeight=getHeight();
                scrollLayout=getChildAt(0);
                firstColumn=(LinearLayout) findViewById(R.id.first_column);
                secondColumn=(LinearLayout) findViewById(R.id.second_column);
                thirdColumn=(LinearLayout) findViewById(R.id.third_column);
                columnWidth=firstColumn.getWidth();
                loadOnce=true;
                loadMoreImage();
            }
        }
        
        //加载更多图片
        public void loadMoreImage(){
            if(hasSDCard()){
                int startIndex=PAGE_SIZE*page;
                int endIndex=PAGE_SIZE*page+PAGE_SIZE;
                if(startIndex<AllImages.imageUrls.length){  //判断图片是否已经加载完毕
                    Toast.makeText(getContext(), "正在加载",Toast.LENGTH_SHORT).show();
                    if(endIndex>AllImages.imageUrls.length){
                        endIndex=AllImages.imageUrls.length;
                    }
                    for (int i = startIndex; i < endIndex; i++) {
                        LoadImageTask task=new LoadImageTask();
                        imageTaskList.add(task);
                        task.execute(AllImages.imageUrls[i]);
                    }
                    page++;
                }
                else{
                    Toast.makeText(getContext(), "加载完毕",Toast.LENGTH_SHORT).show();
                }
            }
            else{
                Toast.makeText(getContext(), "没有内存卡",Toast.LENGTH_SHORT).show();
            }
        }
        
        //对图片的可见性进行检查,如果已经移出视线外,则将图片设为一张空白图片
        public void checkVisiblility(){
            for (int i = 0; i < imageList.size(); i++) {
                ImageView imageView=imageList.get(i);
                int border_top=(Integer) imageView.getTag(R.string.border_top);
                int border_bottom=(Integer) imageView.getTag(R.string.border_bottom);
                if(border_bottom>getScrollY()&&border_top<getScrollY()+scrollViewHeight){   //检查是不是在可见视图之内
                    String imageUrl=(String) imageView.getTag(R.string.image_url);
                    Bitmap imageBitmap=imageTools.getBitmapFromMemoryCache(imageUrl);
                    if(imageBitmap==null){
                        LoadImageTask task=new LoadImageTask(imageView);
                        task.execute(imageUrl);
                    }
                    else{
                        imageView.setImageBitmap(imageBitmap);
                    }
                }
                else{  //如果已经不再屏幕内
                    imageView.setImageResource(R.drawable.ic_launcher);
                }
            }
        }
        
        //判断手机是否有SD卡
        private boolean hasSDCard(){
            return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
        }
        
        //异步加载图片的任务
        class LoadImageTask extends AsyncTask<String, Void, Bitmap>{
            //图片地址Url
            private String imageUrl;
            //可重复使用的ImageView
            private ImageView mImageView;
            
            public LoadImageTask() {
                
            }
            //将可重复使用的ImageView传入
            public LoadImageTask(ImageView imageView) {
                this.mImageView=imageView;
            }
            
            @Override
            protected Bitmap doInBackground(String... params) { //根据imageUrl作为key值查找Bitmap,如果没有,调用loadImage去加载
                imageUrl=params[0];
                Bitmap imageBitmap=imageTools.getBitmapFromMemoryCache(imageUrl);
                if(imageBitmap==null){
                    imageBitmap=loadImage(imageUrl);
                }
                return imageBitmap;
            }
            @Override
            protected void onPostExecute(Bitmap imageBitmap) {
                if(imageBitmap!=null){
                    double ratio=imageBitmap.getWidth()/(columnWidth*1.0);   //得到宽度的压缩比
                    int scaleHeight=(int)(imageBitmap.getHeight()/ratio);    //根据宽度的压缩比  得到这张图片的高度
                    addImage(imageBitmap, columnWidth, scaleHeight);
                }
                imageTaskList.remove(this);
            }
            
            //根据图片的Url去加载图片   首先判断该图片是否在内存卡里面,如果没有,则去下载,如果有,则经过压缩处理后返回
            private Bitmap loadImage(String imageUrl){
                File imageFile=new File(getImagePath(imageUrl));
                if(!imageFile.exists()){
                    downloadImage(imageUrl);
                }
                if(imageUrl!=null){
                    Bitmap bitmap=ImageTools.decodeSampedBitmapFromResource(imageFile.getPath(), columnWidth);
                    if(bitmap!=null){
                        imageTools.addBitmapToMemoryCache(imageUrl, bitmap);
                        return bitmap;
                    }
                }
                return null;
            }
            
            
            //向ImageView中添加一张图片
            private void addImage(Bitmap bitmap,int imageWidth,int imageHeight){
                LinearLayout.LayoutParams params=new LinearLayout.LayoutParams(imageWidth, imageHeight);
                if(mImageView!=null){
                    mImageView.setImageBitmap(bitmap);
                }
                else{
                    ImageView imageView=new ImageView(getContext());
                    imageView.setLayoutParams(params);
                    imageView.setImageBitmap(bitmap);
                    imageView.setScaleType(ScaleType.FIT_XY);
                    imageView.setPadding(5, 5, 5, 5);
                    imageView.setTag(R.string.image_url,imageUrl);
                    findColumnToAdd(imageView, imageHeight).addView(imageView);
                    imageList.add(imageView);
                    }
            }
            
            
            //找到应该添加图片的一列,选取三列中高度最小的一列返回.
            private LinearLayout findColumnToAdd(ImageView imageView,int imageHeight){
                if(firstColumnHeight<=secondColumnHeight){
                    if(firstColumnHeight<=thirdColumnHeight){
                        imageView.setTag(R.string.border_top,firstColumnHeight);
                        firstColumnHeight+=imageHeight;
                        imageView.setTag(R.string.border_bottom,firstColumnHeight);
                        return firstColumn;
                    }
                    else{
                        imageView.setTag(R.string.border_top,thirdColumnHeight);
                        thirdColumnHeight+=imageHeight;
                        imageView.setTag(R.string.border_bottom,thirdColumnHeight);
                        return thirdColumn;
                    }
                }
                else{
                    if(secondColumnHeight<=thirdColumnHeight){
                        imageView.setTag(R.string.border_top,secondColumnHeight);
                        secondColumnHeight+=imageHeight;
                        imageView.setTag(R.string.border_bottomsecondColumnHeight);
                        return secondColumn;
                    }
                    else{
                        imageView.setTag(R.string.border_top,thirdColumnHeight);
                        thirdColumnHeight+=imageHeight;
                        imageView.setTag(R.string.border_bottom,thirdColumnHeight);
                        return thirdColumn;
                    }
                }
            }
            
            //将图片下载到SD卡缓存
            private void downloadImage(String imageUrl){
                HttpURLConnection con=null;
                FileOutputStream fos=null;
                BufferedOutputStream bos=null;
                BufferedInputStream bis=null;
                File imageFile=null;
                try {
                    URL url=new URL(imageUrl);
                    con=(HttpURLConnection) url.openConnection();
                    con.setConnectTimeout(5*1000);
                    con.setReadTimeout(15*1000);
                    con.setDoInput(true);
                    con.setDoOutput(true);
                    bis=new BufferedInputStream(con.getInputStream());
                    imageFile =new File(getImagePath(imageUrl));//获得了图片的路径
                    fos=new FileOutputStream(imageFile);
                    bos=new BufferedOutputStream(fos);
                    byte[] b=new byte[1024];
                    int length;
                    while((length=bis.read(b))!=-1){
                        bos.write(b, 0, length);
                        bos.flush();
                    }
                    
                } catch (MalformedURLException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                finally{
                        try {
                            if(bis!=null){
                                bis.close();
                            }
                            if(bos!=null){
                                bos.close();
                            }
                            if(con!=null){
                                con.disconnect();
                            }
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                }
                if(imageFile!=null){
                    //裁剪图片
                    Log.e("info""path---"+imageFile.getPath());
                    Bitmap bitmap=ImageTools.decodeSampedBitmapFromResource(imageFile.getPath(), columnWidth);
                    if(bitmap!=null){
                        imageTools.addBitmapToMemoryCache(imageUrl, bitmap);
                    }
                }
            }
            
        }
        //获取图片的本地缓存路径
        private String getImagePath(String imageUrl){
            int lastIndex=imageUrl.lastIndexOf("/");
            String imageName=imageUrl.substring(lastIndex+1);
            String imageDir=Environment.getExternalStorageDirectory().getPath()+"/PhotoWallFall/";
            File file=new File(imageDir);
            if(!file.exists()){
                file.mkdirs();
            }
            String imagePath=imageDir+imageName;
            return imagePath;
        }
    }  
    这个自定义控件是继承自ScrollView类的,这样就允许用户可以通过滚动的方式浏览更多的图片,loadMoreImage()是专门用来浏览下一页图片的.


    看一看loadMoreImages()方法的内部细节了。在这个方法中,使用了一个循环来加载这一页中的每一张图片,每次都会开启一
    个LoadImageTask,用于对图片进行异步加载。然后在LoadImageTask中,首先会先检查一下这张图片是不是已经存在于SD卡中了,如果还没
    存在,就从网络上下载,然后把这张图片存放在LruCache中。接着将这张图按照一定的比例进行压缩,并找出当前高度最小的一列,把压缩
    后的图片添加进去就可以了。
    另外,为了保证照片墙上的图片都能够合适地被回收,这里还加入了一个可见性检查的方法,即checkVisibility()方法。这个方法的核心思
    想就是检查目前照片墙上的所有图片,判断出哪些是可见的,哪些是不可见。然后将那些不可见的图片都替换成一张空图,这样就可以保证
    程序始终不会占用过高的内存。当这些图片又重新变为可见的时候,只需要再从LruCache中将这些图片重新取出即可。如果某张图片已经从
    LruCache中被移除了,就会开启一个LoadImageTask,将这张图片重新加载到内存中.

    4.打开activity_main.xml
    <com.example.photodemo.MyScrollView 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"
        android:id="@+id/scroll_view"
        tools:context=".MainActivity" >
        
        <LinearLayout 
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            >
            <LinearLayout 
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:id="@+id/first_column"
                android:orientation="vertical"
                ></LinearLayout>
                    <LinearLayout 
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:id="@+id/second_column"
                android:orientation="vertical"
                ></LinearLayout>
                            <LinearLayout 
                android:layout_width="0dp"
                android:layout_height="wrap_content"
                android:layout_weight="1"
                android:id="@+id/third_column"
                android:orientation="vertical"
                ></LinearLayout>
            
        </LinearLayout>
    </com.example.photodemo.MyScrollView>

    5.当然,因为是获取网络图片,所以必须加上联网权限 ,以及将图片缓存到SD卡,所以需要写权限.
        <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
        <uses-permission android:name="android.permission.INTERNET" /> 
     






    qq3061280@163.com
  • 相关阅读:
    Cannot find a free socket for the debugger
    maven自己创建jar坐标(比如oracle驱动包)
    svn“Previous operation has not finished; run 'cleanup' if it was interrupted“报错的解决方法
    java发http请求
    Spring MVC
    函数节流 防抖
    js 执行顺序
    铺平多维对象数组 js
    vue 线上图标404
    vue-devtools--------vue浏览器工具
  • 原文地址:https://www.cnblogs.com/aibuli/p/fcb4793f118ba7bff3572487fb019775.html
Copyright © 2020-2023  润新知