zoukankan      html  css  js  c++  java
  • 安卓中的瀑布流

        过年没回家,宅在家里看了很多博客,顺手写一下自己的一些收货..

        android中的瀑布流的实现原理,来自郭大神的CSDN
        实现原理:瀑布流的布局方式虽然看起来好像排列的很随意,其实它是有很科学的排列规则的。整个界面会根据屏幕的宽度划分成等宽的若干列,由于手机的屏幕不是很大,这里我们就分成三列。每当需要添加一张图片时,会将这张图片的宽度压缩成和列一样宽,再按照同样的压缩比例对图片的高度进行压缩,然后在这三列中找出当前高度最小的一列,将图片添加到这一列中。之后每当需要添加一张新图片时,都去重复上面的操作,就会形成瀑布流格局的照片墙
    以下是个人收货......
    具体实现
    1.     这里我们用到了LRU算法去管理图片缓存的问题,之前我们一直用比较流行的软引用和若引用,但是从android2,3开始,系统封装好了一个L实现LRU算法的类,LruCache类,今天,我们就来看看该类的用法,
    2. 通过分析源代码,发现LRU其实就是我们之前用LinkHashMap包裹软引用的,只是系统加了个壳而已,具体源码分析请移步http://www.apkbus.com/blog-56480-53922.html
    3. LruCache创建,根据当前应用的最大内存/8
        1. // 获取应用程序最大可用内存
        2. int maxMemory =(int)Runtime.getRuntime().maxMemory();
          int cacheSize = maxMemory /8;
          // 设置图片缓存大小为程序最大可用内存的1/8
          mMemoryCache =newLruCache<String,Bitmap>(cacheSize){
          @Override
          protectedint sizeOf(String key,Bitmap bitmap){
          return bitmap.getByteCount();
          }
          };
         
    4. 存和取
        1. /**
          * 将一张图片存储到LruCache中。
          *
          * @param key
          * LruCache的键,这里传入图片的URL地址。
          * @param bitmap
          * LruCache的键,这里传入从网络上下载的Bitmap对象。
          */
          publicvoid addBitmapToMemoryCache(String key,Bitmap bitmap){
          if(getBitmapFromMemoryCache(key)==null){
          mMemoryCache.put(key, bitmap);
          }
          }
          /**
          * 从LruCache中获取一张图片,如果不存在就返回null。
          *
          * @param key
          * LruCache的键,这里传入图片的URL地址。
          * @return 对应传入键的Bitmap对象,或者null。
          */
          publicBitmap getBitmapFromMemoryCache(String key){
          return mMemoryCache.get(key);
          }
         
    5. 计算图片压缩比
        1. /**
          * 这里是为了实现瀑布流效果 计算图片的压缩比
          * @param options 图片参数
          * @param reqWidth 瀑布流的宽度
          * @return
          */
          publicstaticint calculateInSampleSize(BitmapFactory.Options options,
          int reqWidth){
          // 源图片的宽度
          finalint width = options.outWidth;
          int inSampleSize =1;
          if(width > reqWidth){
          // 计算出实际宽度和目标宽度的比率
          finalint widthRatio =Math.round((float) width /(float) reqWidth);
          inSampleSize = widthRatio;
          }
          return inSampleSize;
          }
         
    6. 加载图片
        1. /**
          * 将图片压缩并返回
          * @param pathName 图片地址
          * @param reqWidth
          * @return
          */
          publicstaticBitmap decodeSampledBitmapFromResource(String pathName,
          int reqWidth){
          // 第一次解析将inJustDecodeBounds设置为true,来获取图片大小
          finalBitmapFactory.Options options =newBitmapFactory.Options();
          options.inJustDecodeBounds =true;
          BitmapFactory.decodeFile(pathName, options);
          // 调用上面定义的方法计算inSampleSize值
          options.inSampleSize = calculateInSampleSize(options, reqWidth);
          // 使用获取到的inSampleSize值再次解析图片
          options.inJustDecodeBounds =false;
          returnBitmapFactory.decodeFile(pathName, options);
          }
         
    7. 自定义控件MyScollView,让界面实现滚动的关键 
      1. 它是一页页加载数据的,一页加载固定张数图片,只要用户滑倒了开头,则加载一整页数据
      2. 加载第一页图片,在layout方法里面绘制,把三列的高度绘制出来,然后加载第一批15张图片
          1. /**
            * 进行一些关键性的初始化操作,获取MyScrollView的高度,以及得到第一列的宽度值。并在这里开始加载第一页的图片。
            */
            @Override
            protectedvoid 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;
            loadMoreImages();
            }
            }
          2.  
      3. 开始加载第一页的15张图片,往线程池中添加线程任务
          1. /**
            * 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。
            */
            publicvoid loadMoreImages(){
            if(hasSDCard()){
            //这里有一个算术
            int startIndex = page * PAGE_SIZE;
            int endIndex = page * PAGE_SIZE + PAGE_SIZE;
            if(startIndex <Images.imageUrls.length){
            Toast.makeText(getContext(),"正在加载...",Toast.LENGTH_SHORT).show();
            if(endIndex >Images.imageUrls.length){
            endIndex =Images.imageUrls.length;
            }
            for(int i = startIndex; i < endIndex; i++){
            LoadImageTask task =newLoadImageTask();
            taskCollection.add(task);
            task.execute(Images.imageUrls[i]);
            }
            page++;
            }else{
            Toast.makeText(getContext(),"已没有更多图片",Toast.LENGTH_SHORT).show();
            }
            }else{
            Toast.makeText(getContext(),"未发现SD卡",Toast.LENGTH_SHORT).show();
            }
            }
           
      4. 用户滚动手指,加载下一页
          1. /**
            * 监听用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。
            */
            @Override
            publicboolean onTouch(View v,MotionEvent event){
            if(event.getAction()==MotionEvent.ACTION_UP){
            Message message =newMessage();
            message.obj =this;
            handler.sendMessageDelayed(message,5);
            }
            returnfalse;
            }
           
      5. 加载更多图片,这里判断是否滑动结束的方法用的是每隔5毫秒发消息,判断两次位置是否相同,相同则认为停止滑动
          1. /**
            * 在Handler中进行图片可见性检查的判断,以及加载更多图片的操作。
            */
            privatestaticHandler handler =newHandler(){
            publicvoid handleMessage(android.os.Message msg){
            MyScrollView myScrollView =(MyScrollView) msg.obj;
            int scrollY = myScrollView.getScrollY();
            // 如果当前的滚动位置和上次相同,表示已停止滚动 并且滚动到最底部拉
            if(scrollY == lastScrollY){
            // 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片
            if(scrollViewHeight + scrollY >= scrollLayout.getHeight()
            && taskCollection.isEmpty()){
            myScrollView.loadMoreImages();
            }
            myScrollView.checkVisibility();
            }else{
            lastScrollY = scrollY;
            Message message =newMessage();
            message.obj = myScrollView;
            // 5毫秒后再次对滚动位置进行判断
            handler.sendMessageDelayed(message,5);
            }
            };
           
      6. 把不需要的图片进行隐藏起来,节省内存空间
        注意 这里的都是getScrollY为负值 要比的是数值 所以本来是borderBottom < getScrollY()变成了borderBottom > getScrollY()了,另外一个也是一个道理
         
        1. /**
          * 遍历imageViewList中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。
          */
          publicvoid checkVisibility(){
          for(int i =0; i &lt; imageViewList.size(); i++){
          ImageView imageView = imageViewList.get(i);
          int borderTop =(Integer) imageView.getTag(R.string.border_top);
          int borderBottom =(Integer) imageView.getTag(R.string.border_bottom);
          //注意 这里的都是getScrollY为负值 要比的是数值 所以本来是borderBottom &lt; getScrollY()变成了borderBottom &gt; getScrollY()了,另外一个也是一个道理
          if(borderBottom &gt; getScrollY()&& borderTop &lt; getScrollY()+ scrollViewHeight){
          String imageUrl =(String) imageView.getTag(R.string.image_url);
          Bitmap bitmap = imageLoader.getBitmapFromMemoryCache(imageUrl);
          if(bitmap !=null){
          imageView.setImageBitmap(bitmap);
          }else{
          LoadImageTask task =newLoadImageTask(imageView);
          task.execute(imageUrl);
          }
          }else{
          imageView.setImageResource(R.drawable.empty_photo);
          }
          }
          }
         
      7. 接下来是一个自定义的AsyncTask类
          1. classLoadImageTaskextendsAsyncTask&lt;String,Void,Bitmap&gt;{
            /**
            * 图片的URL地址
            */
            privateString mImageUrl;
            /**
            * 可重复使用的ImageView
            */
            privateImageView mImageView;
            publicLoadImageTask(){
            }
            /**
            * 将可重复使用的ImageView传入
            *
            * @param imageView
            */
            publicLoadImageTask(ImageView imageView){
            mImageView = imageView;
            }
            @Override
            protectedBitmap doInBackground(String... params){
            mImageUrl = params[0];
            Bitmap imageBitmap = imageLoader.getBitmapFromMemoryCache(mImageUrl);
            if(imageBitmap ==null){
            imageBitmap = loadImage(mImageUrl);
            }
            return imageBitmap;
            }
            @Override
            protectedvoid onPostExecute(Bitmap bitmap){
            if(bitmap !=null){
            double ratio = bitmap.getWidth()/(columnWidth *1.0);
            int scaledHeight =(int)(bitmap.getHeight()/ ratio);
            addImage(bitmap, columnWidth, scaledHeight);
            }
            taskCollection.remove(this);
            }
            /**
            * 根据传入的URL,对图片进行加载。如果这张图片已经存在于SD卡中,则直接从SD卡里读取,否则就从网络上下载。
            *
            * @param imageUrl
            * 图片的URL地址
            * @return 加载到内存的图片。
            */
            privateBitmap loadImage(String imageUrl){
            File imageFile =newFile(getImagePath(imageUrl));
            if(!imageFile.exists()){
            downloadImage(imageUrl);
            }
            if(imageUrl !=null){
            Bitmap bitmap =ImageLoader.decodeSampledBitmapFromResource(imageFile.getPath(),
            columnWidth);
            if(bitmap !=null){
            imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
            return bitmap;
            }
            }
            returnnull;
            }
            /**
            * 向ImageView中添加一张图片
            *
            * @param bitmap
            * 待添加的图片
            * @param imageWidth
            * 图片的宽度
            * @param imageHeight
            * 图片的高度
            */
            privatevoid addImage(Bitmap bitmap,int imageWidth,int imageHeight){
            LinearLayout.LayoutParams params =newLinearLayout.LayoutParams(imageWidth,
            imageHeight);
            if(mImageView !=null){
            mImageView.setImageBitmap(bitmap);
            }else{
            ImageView imageView =newImageView(getContext());
            imageView.setLayoutParams(params);
            imageView.setImageBitmap(bitmap);
            imageView.setScaleType(ScaleType.FIT_XY);
            imageView.setPadding(5,5,5,5);
            imageView.setTag(R.string.image_url, mImageUrl);
            imageView.setOnClickListener(newOnClickListener(){
            @Override
            publicvoid onClick(View v){
            Intent intent =newIntent(getContext(),ImageDetailsActivity.class);
            intent.putExtra("image_path", getImagePath(mImageUrl));
            getContext().startActivity(intent);
            }
            });
            findColumnToAdd(imageView, imageHeight).addView(imageView);
            imageViewList.add(imageView);
            }
            }
            /**
            * 找到此时应该添加图片的一列。原则就是对三列的高度进行判断,当前高度最小的一列就是应该添加的一列。
            *
            * @param imageView
            * @param imageHeight
            * @return 应该添加图片的一列
            */
            privateLinearLayout findColumnToAdd(ImageView imageView,int imageHeight){
            if(firstColumnHeight &lt;= secondColumnHeight){
            if(firstColumnHeight &lt;= thirdColumnHeight){
            imageView.setTag(R.string.border_top, firstColumnHeight);
            firstColumnHeight += imageHeight;
            imageView.setTag(R.string.border_bottom, firstColumnHeight);
            return firstColumn;
            }
            imageView.setTag(R.string.border_top, thirdColumnHeight);
            thirdColumnHeight += imageHeight;
            imageView.setTag(R.string.border_bottom, thirdColumnHeight);
            return thirdColumn;
            }else{
            if(secondColumnHeight &lt;= thirdColumnHeight){
            imageView.setTag(R.string.border_top, secondColumnHeight);
            secondColumnHeight += imageHeight;
            imageView.setTag(R.string.border_bottom, secondColumnHeight);
            return secondColumn;
            }
            imageView.setTag(R.string.border_top, thirdColumnHeight);
            thirdColumnHeight += imageHeight;
            imageView.setTag(R.string.border_bottom, thirdColumnHeight);
            return thirdColumn;
            }
            }
            /**
            * 将图片下载到SD卡缓存起来。
            *
            * @param imageUrl
            * 图片的URL地址。
            */
            privatevoid downloadImage(String imageUrl){
            if(Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)){
            Log.d("TAG","monted sdcard");
            }else{
            Log.d("TAG","has no sdcard");
            }
            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 =newBufferedInputStream(con.getInputStream());
            imageFile =newFile(getImagePath(imageUrl));
            fos =newFileOutputStream(imageFile);
            bos =newBufferedOutputStream(fos);
            byte[] b =newbyte[1024];
            int length;
            while((length = bis.read(b))!=-1){
            bos.write(b,0, length);
            bos.flush();
            }
            }catch(Exception 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){
            Bitmap bitmap =ImageLoader.decodeSampledBitmapFromResource(imageFile.getPath(),
            columnWidth);
            if(bitmap !=null){
            imageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
            }
            }
            }
            /**
            * 获取图片的本地存储路径。
            *
            * @param imageUrl
            * 图片的URL地址。
            * @return 图片的本地存储路径。
            */
            privateString getImagePath(String imageUrl){
            int lastSlashIndex = imageUrl.lastIndexOf("/");
            String imageName = imageUrl.substring(lastSlashIndex +1);
            String imageDir =Environment.getExternalStorageDirectory().getPath()
            +"/PhotoWallFalls/";
            File file =newFile(imageDir);
            if(!file.exists()){
            file.mkdirs();
            }
            String imagePath = imageDir + imageName;
            return imagePath;
            }
            }
           
     





    静以修身 俭以养德
  • 相关阅读:
    BZOJ 4033: [HAOI2015]树上染色 (树形DP)
    BZOJ 1820: [JSOI2010]Express Service 快递服务 DP
    BZOJ 4664: Count 插块DP
    BZOJ 1899: [Zjoi2004]Lunch 午餐 DP
    BZOJ 4559 [JLoi2016]成绩比较 (DP+拉格朗日插值)
    BZOJ1485 [HNOI2009] 有趣的数列 (卡特兰数)
    BZOJ 2111 / Luogu P2606 [ZJOI2010]排列计数
    20190915模拟赛
    深海机器人问题
    太空飞行计划问题
  • 原文地址:https://www.cnblogs.com/Android-MR-wang/p/4297258.html
Copyright © 2011-2022 走看看