瀑布list

item的xml
<?xml version="1.0" encoding="utf-8"?>
<com.dodowaterfall.widget.FlowView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/waterfall_image"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:src="@drawable/ic_launcher" >

</com.dodowaterfall.widget.FlowView>

 

main
<?xml version="1.0" encoding="utf-8"?>
<com.dodowaterfall.LazyScrollView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/waterfall_scroll"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

    <LinearLayout
        android:id="@+id/waterfall_container"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:background="@android:color/white" >
    </LinearLayout>

</com.dodowaterfall.LazyScrollView>

java

package com.dodowaterfall.widget;

import android.content.res.AssetManager;

public class FlowTag {
    private int flowId;
    private String fileName;
    public final int what = 1;

    public int getFlowId() {
        return flowId;
    }

    public void setFlowId(int flowId) {
        this.flowId = flowId;
    }

    public String getFileName() {
        return fileName;
    }

    public void setFileName(String fileName) {
        this.fileName = fileName;
    }

    private AssetManager assetManager;
    private int ItemWidth;

    public AssetManager getAssetManager() {
        return assetManager;
    }

    public void setAssetManager(AssetManager assetManager) {
        this.assetManager = assetManager;
    }

    public int getItemWidth() {
        return ItemWidth;
    }

    public void setItemWidth(int itemWidth) {
        ItemWidth = itemWidth;
    }
}
package com.dodowaterfall.widget;

import java.io.BufferedInputStream;
import java.io.IOException;

import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.AnimationDrawable;
import android.widget.ImageView;
import android.widget.ScrollView;
import android.widget.Toast;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup.LayoutParams;

public class FlowView extends ImageView implements View.OnClickListener,
        View.OnLongClickListener {

    private AnimationDrawable loadingAnimation;
    private FlowTag flowTag;
    private Context context;
    public Bitmap bitmap;
    private ImageLoaderTask task;
    private int columnIndex;// 图片属于第几列
    private int rowIndex;// 图片属于第几行
    private Handler viewHandler;

    public FlowView(Context c, AttributeSet attrs, int defStyle) {
        super(c, attrs, defStyle);
        this.context = c;
        Init();
    }

    public FlowView(Context c, AttributeSet attrs) {
        super(c, attrs);
        this.context = c;
        Init();
    }

    public FlowView(Context c) {
        super(c);
        this.context = c;
        Init();
    }

    private void Init() {

        setOnClickListener(this);
        this.setOnLongClickListener(this);
        setAdjustViewBounds(true);

    }

    @Override
    public boolean onLongClick(View v) {
        Log.d("FlowView", "LongClick");
        Toast.makeText(context, "长按:" + this.flowTag.getFlowId(),
                Toast.LENGTH_SHORT).show();
        return true;
    }

    @Override
    public void onClick(View v) {
        Log.d("FlowView", "Click");
        Toast.makeText(context, "单击:" + this.flowTag.getFlowId(),
                Toast.LENGTH_SHORT).show();
    }

    /**
     * 加载图片
     */
    public void LoadImage() {
        if (getFlowTag() != null) {

            new LoadImageThread().start();
        }
    }

    /**
     * 重新加载图片
     */
    public void Reload() {
        if (this.bitmap == null && getFlowTag() != null) {

            new ReloadImageThread().start();
        }
    }

    /**
     * 回收内存
     */
    public void recycle() {
        setImageBitmap(null);
        if ((this.bitmap == null) || (this.bitmap.isRecycled()))
            return;
        this.bitmap.recycle();
        this.bitmap = null;
    }

    public FlowTag getFlowTag() {
        return flowTag;
    }

    public void setFlowTag(FlowTag flowTag) {
        this.flowTag = flowTag;
    }

    public int getColumnIndex() {
        return columnIndex;
    }

    public void setColumnIndex(int columnIndex) {
        this.columnIndex = columnIndex;
    }

    public int getRowIndex() {
        return rowIndex;
    }

    public void setRowIndex(int rowIndex) {
        this.rowIndex = rowIndex;
    }

    public Handler getViewHandler() {
        return viewHandler;
    }

    public FlowView setViewHandler(Handler viewHandler) {
        this.viewHandler = viewHandler;
        return this;
    }

    class ReloadImageThread extends Thread {

        @Override
        public void run() {
            if (flowTag != null) {

                BufferedInputStream buf;
                try {
                    buf = new BufferedInputStream(flowTag.getAssetManager()
                            .open(flowTag.getFileName()));
                    bitmap = BitmapFactory.decodeStream(buf);

                } catch (IOException e) {

                    e.printStackTrace();
                }

                ((Activity) context).runOnUiThread(new Runnable() {
                    public void run() {
                        if (bitmap != null) {// 此处在线程过多时可能为null
                            setImageBitmap(bitmap);
                        }
                    }
                });
            }

        }
    }

    class LoadImageThread extends Thread {
        LoadImageThread() {
        }

        public void run() {

            if (flowTag != null) {

                BufferedInputStream buf;
                try {
                    buf = new BufferedInputStream(flowTag.getAssetManager()
                            .open(flowTag.getFileName()));
                    bitmap = BitmapFactory.decodeStream(buf);

                } catch (IOException e) {

                    e.printStackTrace();
                }
                // if (bitmap != null) {

                // 此处不能直接更新UI,否则会发生异常:
                // CalledFromWrongThreadException: Only the original thread that
                // created a view hierarchy can touch its views.
                // 也可以使用Handler或者Looper发送Message解决这个问题

                ((Activity) context).runOnUiThread(new Runnable() {
                    public void run() {
                        if (bitmap != null) {// 此处在线程过多时可能为null
                            int width = bitmap.getWidth();// 获取真实宽高
                            int height = bitmap.getHeight();

                            LayoutParams lp = getLayoutParams();

                            int layoutHeight = (height * flowTag.getItemWidth())
                                    / width;// 调整高度
                            if (lp == null) {
                                lp = new LayoutParams(flowTag.getItemWidth(),
                                        layoutHeight);
                            }
                            setLayoutParams(lp);

                            setImageBitmap(bitmap);
                            Handler h = getViewHandler();
                            Message m = h.obtainMessage(flowTag.what, width,
                                    layoutHeight, FlowView.this);
                            h.sendMessage(m);
                        }
                    }
                });

                // }

            }

        }
    }
}
package com.dodowaterfall.widget;

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.HashMap;

import android.content.res.AssetManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup.LayoutParams;
import android.widget.ImageView;
import android.widget.Toast;

//已不使用此类:AsyncTask加载过多会造成 java.util.concurrent.RejectedExecutionException
public class ImageLoaderTask extends AsyncTask<FlowTag, Void, Bitmap> {

    private FlowTag param;
    private final WeakReference<FlowView> imageViewReference;

    public ImageLoaderTask(FlowView imageView) {
        imageViewReference = new WeakReference<FlowView>(imageView);

    }

    @Override
    protected Bitmap doInBackground(FlowTag... params) {

        param = params[0];
        return loadImageFile(param.getFileName(), param.getAssetManager());
    }

    private Bitmap loadImageFile(final String filename,
            final AssetManager manager) {
        InputStream is = null;
        try {
            Bitmap bmp = null;
            // Bitmap bmp = BitmapCache.getInstance().getBitmap(filename,
            // param.getAssetManager());
            BufferedInputStream buf;
            try {
                buf = new BufferedInputStream(param.getAssetManager().open(
                        filename));
                bmp = BitmapFactory.decodeStream(buf);

            } catch (IOException e) {

                e.printStackTrace();
            }

            return bmp;
        } catch (Exception e) {
            Log.e(this.getClass().getSimpleName(), "fetchDrawable failed", e);
        } finally {
            try {
                if (is != null)
                    is.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        return null;
    }

    @Override
    protected void onPostExecute(Bitmap bitmap) {
        if (isCancelled()) {
            bitmap = null;
        }

        if (imageViewReference != null) {
            final FlowView imageView = imageViewReference.get();
            if (imageView != null) {
                if (bitmap != null) {
                    int width = bitmap.getWidth();// 获取真实宽高
                    int height = bitmap.getHeight();

                    LayoutParams lp = imageView.getLayoutParams();
                    lp.height = (height * param.getItemWidth()) / width;// 调整高度
                    imageView.setLayoutParams(lp);
                    imageView.bitmap = bitmap;
                    imageView.setImageBitmap(imageView.bitmap);// 将引用指定到同一个对象,方便销毁

                    Handler h = imageView.getViewHandler();
                    Message m = h.obtainMessage(this.param.what,
                            this.param.getFlowId(), lp.height, imageView);
                    h.sendMessage(m);
                    
                }
            }
        }
    }
}

 

package com.dodowaterfall;

import android.content.Context;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

public class LazyScrollView extends ScrollView {
    @Override
    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
        super.onScrollChanged(l, t, oldl, oldt);
        onScrollListener.onAutoScroll(l, t, oldl, oldt);
    }

    private static final String tag = "LazyScrollView";
    private Handler handler;
    private View view;

    public LazyScrollView(Context context) {
        super(context);

    }

    public LazyScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);

    }

    public LazyScrollView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);

    }

    // 这个获得总的高度
    public int computeVerticalScrollRange() {
        return super.computeHorizontalScrollRange();
    }

    public int computeVerticalScrollOffset() {
        return super.computeVerticalScrollOffset();
    }

    private void init() {

        this.setOnTouchListener(onTouchListener);
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {
                // process incoming messages here
                super.handleMessage(msg);
                switch (msg.what) {
                case 1:
                    if (view.getMeasuredHeight() - 20 <= getScrollY()
                            + getHeight()) {
                        if (onScrollListener != null) {
                            onScrollListener.onBottom();
                        }

                    } else if (getScrollY() == 0) {
                        if (onScrollListener != null) {
                            onScrollListener.onTop();
                        }
                    } else {
                        if (onScrollListener != null) {
                            onScrollListener.onScroll();
                        }
                    }
                    break;
                default:
                    break;
                }
            }
        };

    }

    OnTouchListener onTouchListener = new OnTouchListener() {

        @Override
        public boolean onTouch(View v, MotionEvent event) {

            switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                break;
            case MotionEvent.ACTION_UP:
                if (view != null && onScrollListener != null) {
                    Log.v("坐标:", "getMeasuredHeight="+view.getMeasuredHeight());
                    Log.v("坐标:", "getScrollY()="+getScrollY());
                    Log.v("坐标:", "getHeight()="+getHeight());
                    handler.sendMessageDelayed(handler.obtainMessage(1), 200);
                }
                break;

            default:
                break;
            }
            return false;
        }

    };

    /**
     * 获得参考的View,主要是为了获得它的MeasuredHeight,然后和滚动条的ScrollY+getHeight作比较。
     */
    public void getView() {
        this.view = getChildAt(0);
        if (view != null) {
            init();
        }
    }

    /**
     * 定义接口
     * 
     * @author admin
     * 
     */
    public interface OnScrollListener {
        void onBottom();

        void onTop();

        void onScroll();

        void onAutoScroll(int l, int t, int oldl, int oldt);
    }

    private OnScrollListener onScrollListener;

    public void setOnScrollListener(OnScrollListener onScrollListener) {
        this.onScrollListener = onScrollListener;
    }
}
package com.dodowaterfall;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Random;

import com.dodowaterfall.LazyScrollView.OnScrollListener;
import com.dodowaterfall.widget.FlowTag;
import com.dodowaterfall.widget.FlowView;
import android.app.Activity;
import android.content.Context;
import android.content.res.AssetManager;
import android.graphics.Rect;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.Display;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;

public class MainActivity extends Activity {

    private LazyScrollView waterfall_scroll;
    private LinearLayout waterfall_container;
    private ArrayList<LinearLayout> waterfall_items;
    private Display display;
    private AssetManager asset_manager;
    private List<String> image_filenames;
    private final String image_path = "images";
    private Handler handler;
    private int item_width;

    private int column_count = 3;// 显示列数
    private int page_count = 40;// 每次加载30张图片

    private int current_page = 0;// 当前页数

    private int[] topIndex;
    private int[] bottomIndex;
    private int[] lineIndex;
    private int[] column_height;// 每列的高度

    private HashMap<Integer, String> pins;

    private int loaded_count = 0;// 已加载数量

    private HashMap<Integer, Integer>[] pin_mark = null;

    private Context context;

    private HashMap<Integer, FlowView> iviews;
    private int range;
    int scroll_height;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        display = this.getWindowManager().getDefaultDisplay();
        item_width = display.getWidth() / column_count;// 根据屏幕大小计算每列大小
        asset_manager = this.getAssets();

        column_height = new int[column_count];
        context = this;
        iviews = new HashMap<Integer, FlowView>();
        pins = new HashMap<Integer, String>();
        pin_mark = new HashMap[column_count];

        this.lineIndex = new int[column_count];
        this.bottomIndex = new int[column_count];
        this.topIndex = new int[column_count];

        for (int i = 0; i < column_count; i++) {
            lineIndex[i] = -1;
            bottomIndex[i] = -1;
            pin_mark[i] = new HashMap();
        }

        InitLayout();

    }

    private void InitLayout() {
        waterfall_scroll = (LazyScrollView) findViewById(R.id.waterfall_scroll);
        range = waterfall_scroll.computeVerticalScrollRange();//

        waterfall_scroll.getView();
        waterfall_scroll.setOnScrollListener(new OnScrollListener() {

            @Override
            public void onTop() {
                // 滚动到最顶端
                Log.d("LazyScroll", "Scroll to top");
            }

            @Override
            public void onScroll() {

            }

            @Override
            public void onBottom() {
                // 滚动到最低端
                AddItemToContainer(++current_page, page_count);
            }

            @Override
            public void onAutoScroll(int l, int t, int oldl, int oldt) {

                // Log.d("MainActivity",
                // String.format("%d  %d  %d  %d", l, t, oldl, oldt));

                // Log.d("MainActivity", "range:" + range);
                // Log.d("MainActivity", "range-t:" + (range - t));
                scroll_height = waterfall_scroll.getMeasuredHeight();
            //    Log.d("MainActivity", "scroll_height:" + scroll_height);

                if (t > oldt) {// 向下滚动
                    if (t > 2 * scroll_height) {// 超过两屏幕后

                        for (int k = 0; k < column_count; k++) {

                            LinearLayout localLinearLayout = waterfall_items
                                    .get(k);

                            if (pin_mark[k].get(Math.min(bottomIndex[k] + 1,
                                    lineIndex[k])) <= t + 3 * scroll_height) {// 最底部的图片位置小于当前t+3*屏幕高度

                                ((FlowView) waterfall_items.get(k)
                                        .getChildAt(
                                                Math.min(1 + bottomIndex[k],
                                                        lineIndex[k])))
                                        .Reload();

                                bottomIndex[k] = Math.min(1 + bottomIndex[k],
                                        lineIndex[k]);

                            }
                            Log.d("MainActivity",
                                    "headIndex:" + topIndex[k]
                                            + "  footIndex:" + bottomIndex[k]
                                            + "  headHeight:"
                                            + pin_mark[k].get(topIndex[k]));
                            if (pin_mark[k].get(topIndex[k]) < t - 2
                                    * scroll_height) {// 未回收图片的最高位置<t-两倍屏幕高度

                                int i1 = topIndex[k];
                                topIndex[k]++;
                                ((FlowView) localLinearLayout.getChildAt(i1))
                                        .recycle();
                                Log.d("MainActivity", "recycle,k:" + k
                                        + " headindex:" + topIndex[k]);

                            }
                        }

                    }
                } else {// 向上滚动

                    for (int k = 0; k < column_count; k++) {
                        LinearLayout localLinearLayout = waterfall_items.get(k);
                        if (pin_mark[k].get(bottomIndex[k]) > t + 3
                                * scroll_height) {
                            ((FlowView) localLinearLayout
                                    .getChildAt(bottomIndex[k])).recycle();

                            bottomIndex[k]--;
                        }

                        if (pin_mark[k].get(Math.max(topIndex[k] - 1, 0)) >= t
                                - 2 * scroll_height) {
                            ((FlowView) localLinearLayout.getChildAt(Math.max(
                                    -1 + topIndex[k], 0))).Reload();
                            topIndex[k] = Math.max(topIndex[k] - 1, 0);
                        }
                    }

                }

            }
        });

        waterfall_container = (LinearLayout) this
                .findViewById(R.id.waterfall_container);
        handler = new Handler() {

            @Override
            public void dispatchMessage(Message msg) {

                super.dispatchMessage(msg);
            }

            @Override
            public void handleMessage(Message msg) {

                // super.handleMessage(msg);

                switch (msg.what) {
                case 1:

                    FlowView v = (FlowView) msg.obj;
                    int w = msg.arg1;
                    int h = msg.arg2;
                    // Log.d("MainActivity",
                    // String.format(
                    // "获取实际View高度:%d,ID:%d,columnIndex:%d,rowIndex:%d,filename:%s",
                    // v.getHeight(), v.getId(), v
                    // .getColumnIndex(), v.getRowIndex(),
                    // v.getFlowTag().getFileName()));
                    String f = v.getFlowTag().getFileName();

                    // 此处计算列值
                    int columnIndex = GetMinValue(column_height);

                    v.setColumnIndex(columnIndex);

                    column_height[columnIndex] += h;

                    pins.put(v.getId(), f);
                    iviews.put(v.getId(), v);
                    waterfall_items.get(columnIndex).addView(v);

                    lineIndex[columnIndex]++;

                    pin_mark[columnIndex].put(lineIndex[columnIndex],
                            column_height[columnIndex]);
                    bottomIndex[columnIndex] = lineIndex[columnIndex];
                    break;
                }

            }

            @Override
            public boolean sendMessageAtTime(Message msg, long uptimeMillis) {
                return super.sendMessageAtTime(msg, uptimeMillis);
            }
        };

        waterfall_items = new ArrayList<LinearLayout>();

        for (int i = 0; i < column_count; i++) {
            LinearLayout itemLayout = new LinearLayout(this);
            LinearLayout.LayoutParams itemParam = new LinearLayout.LayoutParams(
                    item_width, LayoutParams.WRAP_CONTENT);

            itemLayout.setPadding(2, 2, 2, 2);
            itemLayout.setOrientation(LinearLayout.VERTICAL);

            itemLayout.setLayoutParams(itemParam);
            waterfall_items.add(itemLayout);
            waterfall_container.addView(itemLayout);
        }

        // 加载所有图片路径

        try {
            image_filenames = Arrays.asList(asset_manager.list(image_path));

        } catch (IOException e) {
            e.printStackTrace();
        }
        // 第一次加载
        AddItemToContainer(current_page, page_count);
    }

    private void AddItemToContainer(int pageindex, int pagecount) {
        int currentIndex = pageindex * pagecount;

        int imagecount = 10000;// image_filenames.size();
        for (int i = currentIndex; i < pagecount * (pageindex + 1)
                && i < imagecount; i++) {
            loaded_count++;
            Random rand = new Random();
            int r = rand.nextInt(image_filenames.size());
            AddImage(image_filenames.get(r),
                    (int) Math.ceil(loaded_count / (double) column_count),
                    loaded_count);
        }

    }

    private void AddImage(String filename, int rowIndex, int id) {

        FlowView item = new FlowView(context);
        // item.setColumnIndex(columnIndex);

        item.setRowIndex(rowIndex);
        item.setId(id);
        item.setViewHandler(this.handler);
        // 多线程参数
        FlowTag param = new FlowTag();
        param.setFlowId(id);
        param.setAssetManager(asset_manager);
        param.setFileName(image_path + "/" + filename);
        param.setItemWidth(item_width);

        item.setFlowTag(param);
        item.LoadImage();
        // waterfall_items.get(columnIndex).addView(item);

    }

    private int GetMinValue(int[] array) {
        int m = 0;
        int length = array.length;
        for (int i = 0; i < length; ++i) {

            if (array[i] < array[m]) {
                m = i;
            }
        }
        return m;
    }
}

 

posted on 2013-03-05 10:46  _star  阅读(278)  评论(0)    收藏  举报

导航