/**

 * 源码片段

 * 自定义的ScrollView,在其中动态地对图片进行添加。

 * 

 * @author guolin

 */

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,这里onLayout中的初始化只需加载一次

     */

    private boolean loadOnce;

 

    /**

     * 对图片进行管理的工具类

     */

    private ImageLoader p_w_picpathLoader;

 

    /**

     * 第一列的布局

     */

    private LinearLayout firstColumn;

 

    /**

     * 第二列的布局

     */

    private LinearLayout secondColumn;

 

    /**

     * 第三列的布局

     */

    private LinearLayout thirdColumn;

 

    /**

     * 记录所有正在下载或等待下载的任务。

     */

    private static Set<loadp_w_picpathtask> taskCollection;

 

    /**

     * MyScrollView下的直接子布局。

     */

    private static View scrollLayout;

 

    /**

     * MyScrollView布局的高度。

     */

    private static int scrollViewHeight;

 

    /**

     * 记录上垂直方向的滚动距离。

     */

    private static int lastScrollY = -1;

 

    /**

     * 记录所有界面上的图片,用以可以随时控制对图片的释放。

     */

    private List<p_w_picpathview> p_w_picpathViewList = new ArrayList<p_w_picpathview>();

 

    /**

     * 在Handler中进行图片可见性检查的判断,以及加载更多图片的操作。

     */

    private static Handler handler = new Handler() {

 

        public void 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 = new Message();

                message.obj = myScrollView;

                // 5毫秒后再次对滚动位置进行判断

                handler.sendMessageDelayed(message, 5);

            }

        };

 

    };

 

    /**

     * MyScrollView的构造函数。

     * 

     * @param context

     * @param attrs

     */

    public MyScrollView(Context context, AttributeSet attrs) {

        super(context, attrs);

        p_w_picpathLoader = ImageLoader.getInstance();

        taskCollection = new HashSet<loadp_w_picpathtask>();

        setOnTouchListener(this);

    }

 

    /**

     * 进行一些关键性的初始化操作,获取MyScrollView的高度,以及得到第一列的宽度值。并在这里开始加载第一页的图片。

     */

    @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;

            loadMoreImages();

        }

    }

 

    /**

     * 监听用户的触屏事件,如果用户手指离开屏幕则开始进行滚动检测。

     */

    @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;

    }

 

    /**

     * 开始加载下一页的图片,每张图片都会开启一个异步线程去下载。

     */

    public void loadMoreImages() {

        if (hasSDCard()) {

            int startIndex = page * PAGE_SIZE;

            int endIndex = page * PAGE_SIZE + PAGE_SIZE;

            if (startIndex < Images.p_w_picpathUrls.length) {

                Toast.makeText(getContext(), "正在加载...", Toast.LENGTH_SHORT)

                        .show();

                if (endIndex > Images.p_w_picpathUrls.length) {

                    endIndex = Images.p_w_picpathUrls.length;

                }

                for (int i = startIndex; i < endIndex; i++) {

                    LoadImageTask task = new LoadImageTask();

                    taskCollection.add(task);

                    task.execute(Images.p_w_picpathUrls[i]);

                }

                page++;

            } else {

                Toast.makeText(getContext(), "已没有更多图片", Toast.LENGTH_SHORT)

                        .show();

            }

        } else {

            Toast.makeText(getContext(), "未发现SD卡", Toast.LENGTH_SHORT).show();

        }

    }

 

    /**

     * 遍历p_w_picpathViewList中的每张图片,对图片的可见性进行检查,如果图片已经离开屏幕可见范围,则将图片替换成一张空图。

     */

    public void checkVisibility() {

        for (int i = 0; i < p_w_picpathViewList.size(); i++) {

            ImageView p_w_picpathView = p_w_picpathViewList.get(i);

            int borderTop = (Integer) p_w_picpathView.getTag(R.string.border_top);

            int borderBottom = (Integer) p_w_picpathView

                    .getTag(R.string.border_bottom);

            if (borderBottom > getScrollY()

                    && borderTop < getScrollY() + scrollViewHeight) {

                String p_w_picpathUrl = (String) p_w_picpathView.getTag(R.string.p_w_picpath_url);

                Bitmap bitmap = p_w_picpathLoader.getBitmapFromMemoryCache(p_w_picpathUrl);

                if (bitmap != null) {

                    p_w_picpathView.setImageBitmap(bitmap);

                } else {

                    LoadImageTask task = new LoadImageTask(p_w_picpathView);

                    task.execute(p_w_picpathUrl);

                }

            } else {

                p_w_picpathView.setImageResource(R.drawable.empty_photo);

            }

        }

    }

 

    /**

     * 判断手机是否有SD卡。

     * 

     * @return 有SD卡返回true,没有返回false。

     */

    private boolean hasSDCard() {

        return Environment.MEDIA_MOUNTED.equals(Environment

                .getExternalStorageState());

    }

 

    /**

     * 异步下载图片的任务。

     * 

     * @author guolin

     */

    class LoadImageTask extends AsyncTask<string, void,="" bitmap=""> {

 

        /**

         * 图片的URL地址

         */

        private String mImageUrl;

 

        /**

         * 可重复使用的ImageView

         */

        private ImageView mImageView;

 

        public LoadImageTask() {

        }

 

        /**

         * 将可重复使用的ImageView传入

         * 

         * @param p_w_picpathView

         */

        public LoadImageTask(ImageView p_w_picpathView) {

            mImageView = p_w_picpathView;

        }

 

        @Override

        protected Bitmap doInBackground(String... params) {

            mImageUrl = params[0];

            Bitmap p_w_picpathBitmap = p_w_picpathLoader

                    .getBitmapFromMemoryCache(mImageUrl);

            if (p_w_picpathBitmap == null) {

                p_w_picpathBitmap = loadImage(mImageUrl);

            }

            return p_w_picpathBitmap;

        }

 

        @Override

        protected void 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 p_w_picpathUrl

         *            图片的URL地址

         * @return 加载到内存的图片。

         */

        private Bitmap loadImage(String p_w_picpathUrl) {

            File p_w_picpathFile = new File(getImagePath(p_w_picpathUrl));

            if (!p_w_picpathFile.exists()) {

                downloadImage(p_w_picpathUrl);

            }

            if (p_w_picpathUrl != null) {

                Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(

                        p_w_picpathFile.getPath(), columnWidth);

                if (bitmap != null) {

                    p_w_picpathLoader.addBitmapToMemoryCache(p_w_picpathUrl, bitmap);

                    return bitmap;

                }

            }

            return null;

        }

 

        /**

         * 向ImageView中添加一张图片

         * 

         * @param bitmap

         *            待添加的图片

         * @param p_w_picpathWidth

         *            图片的宽度

         * @param p_w_picpathHeight

         *            图片的高度

         */

        private void addImage(Bitmap bitmap, int p_w_picpathWidth, int p_w_picpathHeight) {

            LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(

                    p_w_picpathWidth, p_w_picpathHeight);

            if (mImageView != null) {

                mImageView.setImageBitmap(bitmap);

            } else {

                ImageView p_w_picpathView = new ImageView(getContext());

                p_w_picpathView.setLayoutParams(params);

                p_w_picpathView.setImageBitmap(bitmap);

                p_w_picpathView.setScaleType(ScaleType.FIT_XY);

                p_w_picpathView.setPadding(5, 5, 5, 5);

                p_w_picpathView.setTag(R.string.p_w_picpath_url, mImageUrl);

                findColumnToAdd(p_w_picpathView, p_w_picpathHeight).addView(p_w_picpathView);

                p_w_picpathViewList.add(p_w_picpathView);

            }

        }

 

        /**

         * 找到此时应该添加图片的一列。原则就是对三列的高度进行判断,当前高度最小的一列就是应该添加的一列。

         * 

         * @param p_w_picpathView

         * @param p_w_picpathHeight

         * @return 应该添加图片的一列

         */

        private LinearLayout findColumnToAdd(ImageView p_w_picpathView,

                int p_w_picpathHeight) {

            if (firstColumnHeight <= secondColumnHeight) {

                if (firstColumnHeight <= thirdColumnHeight) {

                    p_w_picpathView.setTag(R.string.border_top, firstColumnHeight);

                    firstColumnHeight += p_w_picpathHeight;

                    p_w_picpathView.setTag(R.string.border_bottom, firstColumnHeight);

                    return firstColumn;

                }

                p_w_picpathView.setTag(R.string.border_top, thirdColumnHeight);

                thirdColumnHeight += p_w_picpathHeight;

                p_w_picpathView.setTag(R.string.border_bottom, thirdColumnHeight);

                return thirdColumn;

            } else {

                if (secondColumnHeight <= thirdColumnHeight) {

                    p_w_picpathView.setTag(R.string.border_top, secondColumnHeight);

                    secondColumnHeight += p_w_picpathHeight;

                    p_w_picpathView

                            .setTag(R.string.border_bottom, secondColumnHeight);

                    return secondColumn;

                }

                p_w_picpathView.setTag(R.string.border_top, thirdColumnHeight);

                thirdColumnHeight += p_w_picpathHeight;

                p_w_picpathView.setTag(R.string.border_bottom, thirdColumnHeight);

                return thirdColumn;

            }

        }

 

        /**

         * 将图片下载到SD卡缓存起来。

         * 

         * @param p_w_picpathUrl

         *            图片的URL地址。

         */

        private void downloadImage(String p_w_picpathUrl) {

            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 p_w_picpathFile = null;

            try {

                URL url = new URL(p_w_picpathUrl);

                con = (HttpURLConnection) url.openConnection();

                con.setConnectTimeout(5 * 1000);

                con.setReadTimeout(15 * 1000);

                con.setDoInput(true);

                con.setDoOutput(true);

                bis = new BufferedInputStream(con.getInputStream());

                p_w_picpathFile = new File(getImagePath(p_w_picpathUrl));

                fos = new FileOutputStream(p_w_picpathFile);

                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 (p_w_picpathFile != null) {

                Bitmap bitmap = ImageLoader.decodeSampledBitmapFromResource(

                        p_w_picpathFile.getPath(), columnWidth);

                if (bitmap != null) {

                    p_w_picpathLoader.addBitmapToMemoryCache(p_w_picpathUrl, bitmap);

                }

            }

        }

 

        /**

         * 获取图片的本地存储路径。

         * 

         * @param p_w_picpathUrl

         *            图片的URL地址。

         * @return 图片的本地存储路径。

         */

        private String getImagePath(String p_w_picpathUrl) {

            int lastSlashIndex = p_w_picpathUrl.lastIndexOf("/");

            String p_w_picpathName = p_w_picpathUrl.substring(lastSlashIndex + 1);

            String p_w_picpathDir = Environment.getExternalStorageDirectory()

                    .getPath() + "/PhotoWallFalls/";

            File file = new File(p_w_picpathDir);

            if (!file.exists()) {

                file.mkdirs();

            }

            String p_w_picpathPath = p_w_picpathDir + p_w_picpathName;

            return p_w_picpathPath;

        }

    }

 

}</string,></loadp_w_picpathtask></p_w_picpathview></p_w_picpathview></loadp_w_picpathtask>