Android 横向ScrollView照片浏览器实现(致敬郭神)

因为项目中需要用到所以实现的一个横向的照片浏览器,使用横向SrollView实现。

实现效果如下:

实现思路:

在开始做之前呢,本着有轮子就用轮子的精神,也还是去百度找了很久,诸如"Android横向照片墙"、"Android横向照片流"、"Android横向照片选择器"…完全没有好咩。。查来查去都是那几篇竖向照片瀑布流的,横向的反正我是没找到。

然后,在将郭神的《ndroid瀑布流照片墙实现,体验不规则排列的美感》一文拜读之后(http://blog.csdn.net/guolin_blog/article/details/10470797),灵鸡一动,想着既然竖向的能实现,那么横向的岂不是转个方向就好了?

虽然最终实现起来还是花了一点时间,但是基本原理好像就是转了个方向。。。但是修改后的代码和郭神的源码虽说图片加载部分我们没有改动,但在很多地方都还是很很大区别的。下面特别说明一下。

需要注意的点有以下三个方面:

1、不再继承自普通的ScrollView而是继承自HorizontalScrollView

2、在onLayout的初始化中不再初始化三列照片(郭神写的是瀑布流嘛~),而是改为横向的一列,并使每张照片的宽高相等,使之保持正方形。

3、在可见性判断中不再根据高度而是根据宽度进行判断:

    /**
     * 遍历imageViewList中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。
     */
    public void checkVisibility() {
        for (int i = 0; i < imageViewList.size(); i++) {
            ImageView imageView = imageViewList.get(i);
            int borderLeft = (Integer) imageView.getTag(R.string.border_top);
            int borderRight = (Integer) imageView
                    .getTag(R.string.border_bottom);
            if (borderLeft > getScrollX()
                    && borderRight < getScrollX() + ColumnWith) {
                String imageUrl = (String) imageView.getTag(R.string.image_url);
                //从内存中获取图片
                Bitmap bitmap = NGImageLoader.getBitmapFromMemoryCache(imageUrl);
                if (bitmap!=null) {
                    imageView.setImageBitmap(bitmap);
                }else {
                    //TODO 显示空图
                }

            } else {
            }
        }
    }

源码实现如下:

package com.whale.nangua.toquan.view;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.HorizontalScrollView;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import com.whale.nangua.toquan.R;
import com.whale.nangua.toquan.UserInfoActivity;

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

/**
 * Created by nangua on 2016/8/3.
 */
public class NGOthersInfoAtyHorizotalScrollView extends HorizontalScrollView implements View.OnTouchListener{

    /**
     * 是否已加载过一次layout,这里onLayout中的初始化只需加载一次
     */
    private boolean loadOnce;

    /**
     *  列的布局
     */
    private   LinearLayout linearlayout_userinfo_personal_column;

    /**
     * 总的宽度
     */
    private static int ColumnWith;

    /**
     * 每张照片的宽度
     */
    private int perphotoWidth;

    /**
     * 记录上垂直方向的滚动距离。
     */
    private static int lastScrollX = -1;

    /**
     * MyScrollView下的直接子布局。
     */
    private static View scrollLayout;

    /**
     * 进行一些关键性的初始化操作,NGOthersInfoAtyHorizotalScrollView,以及得到高度值。并在这里开始加载第一页的图片。
     */
    @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();
            linearlayout_userinfo_personal_column = new LinearLayout(getContext());
            linearlayout_userinfo_personal_column.setLayoutParams(
                    new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT
                            , ViewGroup.LayoutParams.MATCH_PARENT)
            );
            linearlayout_userinfo_personal_column.setOrientation(LinearLayout.HORIZONTAL);
            this.addView(linearlayout_userinfo_personal_column);

            perphotoWidth = scrollViewHeight;  //每张照片的宽度等于列高
            loadOnce = true;
            //加载下一页图片
            loadMoreImages();

            scrollLayout = getChildAt(0);
        }
    }

    /**
     * 记录当前已加载到第几页
     * @param context
     */
    private int page = 0;

    /**
     * 每页要加载的图片数量
     * 默认为5张
     * @param context
     */
    public static final int PAGE_SIZE = 5;

    /**
     * 记录所有正在下载或等待下载的任务。
     */
    private static Set<LoadImageTask> taskCollection;

    /**
     * 对图片进行管理的工具类
     */
    private com.whale.nangua.toquan.utils.NGImageLoader NGImageLoader;

    /**
     * MyScrollView布局的高度。
     */
    private static int scrollViewHeight;

    /**
     * MyScrollView的构造函数。
     * @param context
     * @param attrs
     */
    public NGOthersInfoAtyHorizotalScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);

        NGImageLoader = NGImageLoader.getInstance();
        taskCollection = new HashSet<LoadImageTask>();
        setOnTouchListener(this);
    }

    /**
     * 在Handler中进行图片可见性检查的判断,以及加载更多图片的操作。
     */
    private static Handler handler = new Handler() {
        public void handleMessage(android.os.Message msg) {
            NGOthersInfoAtyHorizotalScrollView myScrollView = (NGOthersInfoAtyHorizotalScrollView) msg.obj;
            int scrollX = myScrollView.getScrollX();
            // 如果当前的滚动位置和上次相同,表示已停止滚动
            if (scrollX == lastScrollX) {
                // 当滚动的最右边,并且当前没有正在下载的任务时,开始加载下一页的图片
                if ( ColumnWith + scrollX >= (scrollLayout.getWidth() )
                        && taskCollection.isEmpty()) {
                    myScrollView.loadMoreImages();
                }
                myScrollView.checkVisibility();
            } else {
                lastScrollX = scrollX;
                Message message = new Message();
                message.obj = myScrollView;
                // 5毫秒后再次对滚动位置进行判断
                handler.sendMessageDelayed(message, 5);
            }
        }
    };

    /**
     * 监听用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。
     */
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_UP) {
            Message message = new Message();
            message.obj = this;
            handler.sendMessageDelayed(message, 5);
        }
        return false;
    }

    /**
     * 遍历imageViewList中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。
     */
    public void checkVisibility() {
        for (int i = 0; i < imageViewList.size(); i++) {
            ImageView imageView = imageViewList.get(i);
            int borderLeft = (Integer) imageView.getTag(R.string.border_top);
            int borderRight = (Integer) imageView
                    .getTag(R.string.border_bottom);
            if (borderLeft > getScrollX()
                    && borderRight < getScrollX() + ColumnWith) {
                String imageUrl = (String) imageView.getTag(R.string.image_url);
                //从内存中获取图片
                Bitmap bitmap = NGImageLoader.getBitmapFromMemoryCache(imageUrl);
                if (bitmap!=null) {
                    imageView.setImageBitmap(bitmap);
                }else {
                    //TODO 显示空图
                }

            } else {
            }
        }
    }

    static boolean hasOver = false;

    private String[] imageUrls;
    public void setImageUrls(String[] imageUrls) {
        this.imageUrls = imageUrls;
    }

    /**
     * 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。
     */
    public void loadMoreImages() {
        if (hasSDCard()&& imageUrls!=null) {  //判断是否有SD卡
            int startIndex = page * PAGE_SIZE;  //起始位置
            int endIndex = page * PAGE_SIZE + PAGE_SIZE;    //结束位置
            if (startIndex < imageUrls.length) {
                //Toast.makeText(getContext(), "正在加载...", Toast.LENGTH_SHORT).show();
                if (endIndex > imageUrls.length) {
                    endIndex = imageUrls.length;
                }
                for (int i = startIndex; i < endIndex; i++) {
                    //加载图片
                    LoadImageTask task = new LoadImageTask();
                    taskCollection.add(task);
                    task.execute(imageUrls[i]);

                }
                page++;
            } else {
                if (hasOver == false) {
                    Toast.makeText(getContext(), "已没有更多图片", Toast.LENGTH_SHORT).show();
                    hasOver = true;
                }
            }
        } else {
            //Toast.makeText(getContext(), "未发现SD卡", Toast.LENGTH_SHORT).show();
        }
    }

    /**
     * 判断手机是否有SD卡。
     * @return 有SD卡返回true,没有返回false。
     */
    private boolean hasSDCard() {
        return Environment.MEDIA_MOUNTED.equals(Environment
                .getExternalStorageState());
    }

    public int dpToPx(Resources res, int dp) {
        return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, res.getDisplayMetrics());
    }

    /**
     * 记录所有界面上的图片,用以可以随时控制对图片的释放。
     */
    private List<ImageView> imageViewList = new ArrayList<ImageView>();

    /**
     * 异步下载图片的任务。
     *
     * @author guolin
     */
    class LoadImageTask extends AsyncTask<String, Void, Bitmap> {

        @Override
        public boolean equals(Object o) {
            return super.equals(o);
        }

        @Override
        public int hashCode() {
            return super.hashCode();
        }

        /**
         * 图片的URL地址
         */
        private String mImageUrl;

        /**
         * 可重复使用的ImageView
         */
        private ImageView mImageView;

        public LoadImageTask() {
        }

        /**
         * 将可重复使用的ImageView传入
         *
         * @param imageView
         */
        public LoadImageTask(ImageView imageView) {
            mImageView = imageView;
        }

        @Override
        protected Bitmap doInBackground(String... params) {
            mImageUrl = params[0];
            Bitmap imageBitmap = NGImageLoader
                    .getBitmapFromMemoryCache(mImageUrl);
            if (imageBitmap == null) {
                imageBitmap = loadImage(mImageUrl);
            }
            return imageBitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            if (bitmap != null) {
                addImage(bitmap, perphotoWidth, perphotoWidth);
            }
            taskCollection.remove(this);
        }

        /**
         * 根据传入的URL,对图片进行加载。如果这张图片已经存在于SD卡中,则直接从SD卡里读取,否则就从网络上下载。
         *
         * @param imageUrl
         *            图片的URL地址
         * @return 加载到内存的图片。
         */
        private Bitmap loadImage(String imageUrl) {
            File imageFile = new File(getImagePath(imageUrl));
            if (!imageFile.exists()) {
                downloadImage(imageUrl);
            }
            if (imageUrl != null) {
                Bitmap bitmap = NGImageLoader.decodeSampledBitmapFromResource(
                        imageFile.getPath(), perphotoWidth);
                if (bitmap != null) {
                    NGImageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
                    return bitmap;
                }
            }
            return null;
        }

        /**
         * 向ImageView中添加一张图片
         *
         * @param bitmap
         *            待添加的图片
         * @param imageWidth
         *            图片的宽度
         * @param imageHeight
         *            图片的高度
         */
        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(ImageView.ScaleType.FIT_XY);
                //imageView.setPadding(5, 5, 5, 5);
                imageView.setTag(R.string.image_url, mImageUrl);

                //这里应该计算图片当前的位置
                // imageView.setTag(R.string.border_top, firstColumnHeight);
                imageView.setTag(R.string.border_top, ColumnWith);
                ColumnWith += perphotoWidth;//增加总宽
                imageView.setTag(R.string.border_bottom, ColumnWith);
                linearlayout_userinfo_personal_column.addView(imageView);

                imageViewList.add(imageView);
            }
        }

        /**
         * 将图片下载到SD卡缓存起来。
         *
         * @param imageUrl
         *            图片的URL地址。
         */
        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 (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 = NGImageLoader.decodeSampledBitmapFromResource(
                        imageFile.getPath(), perphotoWidth);
                if (bitmap != null) {
                    NGImageLoader.addBitmapToMemoryCache(imageUrl, bitmap);
                }
            }
        }

        /**
         * 获取图片的本地存储路径。
         *
         * @param imageUrl
         *            图片的URL地址。
         * @return 图片的本地存储路径。
         */
        private String getImagePath(String imageUrl) {
            int lastSlashIndex = imageUrl.lastIndexOf("/");
            String imageName = imageUrl.substring(lastSlashIndex + 1);
            String imageDir = Environment.getExternalStorageDirectory()
                    .getPath() + "/PhotoWallFalls/";
            File file = new File(imageDir);
            if (!file.exists()) {
                file.mkdirs();
            }
            String imagePath = imageDir + imageName;
            return imagePath;
        }
    }

}

QAQ周一真是一天都困啊~明天还是要努力爬起来去上班~加油for自己~

时间: 2024-12-28 01:33:33

Android 横向ScrollView照片浏览器实现(致敬郭神)的相关文章

android的照片浏览器(一)至返回所有图片文件

今天开始写android的照片浏览器 首先要解决的问题是要得到sdcard下面所有是图片的文件的目录 于是我先写了一个普通的java类 来得到后缀是.jpg,.bmp.png.jpeg的文件 package com.jiangqq.utlis; import java.io.File; import java.util.ArrayList; public class FileUtils { private static ArrayList filelist = new ArrayList();

Android 自定义ScrollView(具有反弹效果的ScrollView,能够兼容横向的滑动)

package com.itau.jingdong.widgets; import android.content.Context; import android.graphics.Rect; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.view.animation.TranslateAnimation; import and

iOS开源照片浏览器框架SGPhotoBrowser的设计与实现

简介 近日在制作一个开源加密相册时附带着设计了一个照片浏览器,在进一步优化后发布到了GitHub供大家使用,该框架虽然没有MWPhotoBrowser那么强大,但是使用起来更为方便,操作更符合常规相册习惯,自定义和修改源码也十分简单. 本文主要介绍这个照片浏览器框架的技术要点,如果要深入研究和使用,可以在下面的链接中下载源码. 如果你对这个框架有兴趣,可以点击这里前去GitHub下载源码,欢迎Star与指出不足. 效果图 缩略图预览,点击缩略图进入原图浏览,点击底部工具栏可以进入编辑模式. 批量

竖向ScrollView+横向ScrollView+ListView,实现listView可以左右上下滑动

竖向ScrollView中嵌套横向ScrollView,横向ScrollView嵌套ListView. 具体代码见附件. <ScrollView android:layout_width="match_parent" android:layout_height="match_parent"> <RelativeLayout android:layout_width="match_parent" android:layout_he

IOS 使用SDWebImage实现仿新浪微博照片浏览器

使用第三方库SDWebImage实现仿新浪微博照片浏览器,可以下载图片缓存,点击之后滚动查看相片,具体效果如下: 代码如下: WeiboImageView.h: #import <UIKit/UIKit.h> @interface WeiboImageView : UIImageView @property (nonatomic, assign) CGRect originRect; - (instancetype)initWithFrame:(CGRect)frame; @end Weibo

android自带的浏览器如何下载apk包

============问题描述============ 如题! 目前android上其它的浏览器因为自己做过处理,所以下载没问题.. 可是android自带的浏览器下载有时候下载错误.有时候下载的是.htm文件,恶心啊,貌似把apk当成恶意软件了,请问大神们该怎么处理.. 和contentType设置有关吗,我换了好多个,没用啊,我的服务端是用java写的 ============解决方案1============ response.setContentType("application/vn

Android自定义ScrollView实现一键置顶功能

效果图如下: (ps:动态图有太大了,上传不了,就给大家口述一下要实现的功能吧) 要实现的功能:当ScrollView向上滑动超过一定距离后,就渐变的出现一个置顶的按钮,当滑动距离小于我们指定的距离时,按钮又消失. 实现原理:就是监听View的onScrollChanged()方法(回调方法),获取到ScrolView滑动的距离,如果大于我们的距离,则出现置顶按钮,否则,直接隐藏,且当按钮出现点击事件,让整个ScrollView滑动到(0,0)位置即可,不废话,直接上代码 自定义的ScrollV

终结照片浏览器应用ios源码

开发环境:Swift 1.2  .Xcode 6.3 (一)总体说明1.本框架有OC版本,本次主要是汲取了OC版本所有bug与不足,同时听取在大量使用者的反馈与心声.修复与极大的改善了之前的OC版本并在此基础之上精心推出的Swift完美版,https://github.com/nsdictionary/CorePhotoBrowserVC.2.纯swift支持,本地与网络支持,横竖屏支持,iPhone所有屏幕支持,iPad平板也支持并力求零Bug.3.依赖框架说明:CFSnapKit(布局库).

【Android】Scrollview 相关问题汇总

去除Scrollview 滑动边界渐变颜色 去掉滚动条,并将在滑动时,边界不会变成灰白 <horizontalscrollview android:overScrollMode="never" android:background="@null" android:scrollbars="none" android:fadingEdge="none" /> scrollview初始化定位滚动条的位置在需要的地方调用m