您的位置:首页 > 理论基础 > 计算机网络

利用LruCache加载网络图片实现图片瀑布流效果(基础版)

2014-08-13 14:05 603 查看
PS:

2015年1月20日21:37:27

关于LoadImageAsyncTask和checkAllImageViewVisibility可能有点小bug

修改后的代码请参见升级版本的代码
http://blog.csdn.net/lfdfhl/article/details/42925193
MainActivity如下:

package cc.patience3;

import android.os.Bundle;
import android.app.Activity;
/**
* Demo描述:
* 采用瀑布流的形式加载大量网络图片
* 详细分析参见WaterfallScrollView
*
* 参考资料:
* 1 http://blog.csdn.net/guolin_blog/article/details/10470797 * 2 http://blog.csdn.net/lfdfhl/article/details/18350601 *   Thank you very much
*
*/
public class MainActivity extends Activity {

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

}


效果图如下:



WaterfallScrollView如下:

package cc.patience3;

import java.io.File;
import java.util.ArrayList;
import java.util.HashSet;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.Toast;
/**
* Demo功能:
* 加载网络图片实现图片瀑布流效果(参见截图)
*
* Demo流程:
* 1 为了加载的众多图片可以在屏幕上滑动显示,所以需要一个ScrollView控件.
*   于是自定义ScrollView
* 2 将自定义ScrollView作为布局xml文件的根布局.
*   在根布局下有一个LinearLayout它就是该自定义ScrollView的第一个子孩子.
*   即代码中waterfallScrollView.getChildAt(0)
*   将该LinearLayout均分成三个子LinearLayout,它们三的宽度平分屏幕的宽度.
*   这样我们就可以往这三个LinearLayout中不断添加图片,形成瀑布流
* 3 将网络图片添加到瀑布流的过程
*   3.1 当手指在屏幕上停止滑动时(ACTION_UP)加载图片
*   3.2 从网络中下载图片
*   3.3 找到三个LinearLayout中当前高度最小的,将图片添加进去
*   3.4 在添加图片后对ScrollView中所有ImageView进行检查.
*       对于不在屏幕上显示的ImageView将其所加载的网络图片替换成本地一张小图片.
* 4 为了加载速度和内存的有效使用,示例中采用了LruCache.
*
*
* 错误总结:
* 在使用ImageView.setTag(key, tag)看到第一个参数为int,于是为其指定一个final的int
* 运行报错:
* java.lang.IllegalArgumentException: The key must be an application-specific resource id.
* 原因是不可自己指定该值,而应该使用系统指定的int值.这么做大概是为了防止自己指定的值与系统某个值冲突吧.
* 解决办法:在Strings.xml中指定值string值然后使用其在R文件中的int值即可,例如:
* imageView.setTag(R.string.IMAGE_URL_TAG, imageUrl);其中:
* R.string.IMAGE_URL_TAG就是字符串IMAGE_URL_TAG在R文件中的int值
*
* 在此可见setTag方法的用途:为某个View保存数据.
* 该方法还是挺有用的,可以把属于该View的某些属性保存到该View里面,而不用单独找个地方来存这些数据
*
*/
public class WaterfallScrollView extends ScrollView implements OnTouchListener {
// 每页加载的图片数量
public final int PAGE_SIZE = 15;
// 当前页码
private int currentPage;
// 每一列的宽度
private int everyColumnWidth;
// 第一列的高度
private int firstColumnHeight;
// 第一列的布局
private LinearLayout mFirstLinearLayout;
// 第二列的高度
private int secondColumnHeight;
// 第二列的布局
private LinearLayout mSecondLinearLayout;
// 第三列的高度
private int thirdColumnHeight;
// 第三列的布局
private LinearLayout mThirdLinearLayout;
// 是否已经进入该界面
private boolean isFirstEnterThisScrollView = false;
// LruCache
private LruCacheImageLoader mLruCacheImageLoader;
// 记录所有正在下载或等待下载的异步任务
private HashSet<LoadImageAsyncTask> mLoadImageAsyncTaskHashSet;
// 记录ScrollView中的所有ImageView
private ArrayList<ImageView> mAllImageViewArrayList;
// 该WaterfallScrollView控件的高度
private int waterfallScrollViewHeight;
// ScrollView顶端已经向上滑出屏幕长度
private int scrollY=0;
private int lastScrollY=-1;
// 处理消息的Handle
private Handler mHandler;
// Context
private Context mContext;
private final int REFRESH=9527;
public WaterfallScrollView(Context context) {
super(context);
init(context);
}

public WaterfallScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}

public WaterfallScrollView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}

/**
* 判断scrollView是否滑动到底部的三个值:
* scrollY:ScrollView顶端已经滑出去的高度
* waterfallScrollViewHeight:ScrollView的布局高度
* scrollView.getChildAt(0).getMeasuredHeight():ScrollView内容的高度.
* 常常有一部分内容要滑动后才可见,这部分的高度也包含在了这里面
*/
private void init(Context context){
mContext=context;
this.setOnTouchListener(this);
mAllImageViewArrayList=new ArrayList<ImageView>();
mLoadImageAsyncTaskHashSet=new HashSet<LoadImageAsyncTask>();
mLruCacheImageLoader=LruCacheImageLoader.getLruCacheImageLoaderInstance();
mHandler=new Handler(){
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what==9527) {
WaterfallScrollView waterfallScrollView=(WaterfallScrollView) msg.obj;
scrollY=waterfallScrollView.getScrollY();
// 如果当前的滚动位置和上次相同,表示已停止滚动
if (lastScrollY==scrollY) {
// 当滚动的最底部,并且当前没有正在下载的任务时,开始加载下一页的图片
int scrollViewMeasuredHeight=waterfallScrollView.getChildAt(0).getMeasuredHeight();
boolean isAsyncTashHashSetEmpty=mLoadImageAsyncTaskHashSet.isEmpty();
if (waterfallScrollViewHeight+scrollY>=scrollViewMeasuredHeight&&isAsyncTashHashSetEmpty) {
waterfallScrollView.loadNextPageImages();
}
//检查所有ImageView的可见性
checkAllImageViewVisibility();
} else {
lastScrollY=scrollY;
Message message=new Message();
message.what=REFRESH;
message.obj=WaterfallScrollView.this;
// 5毫秒后再次对滚动位置进行判断
mHandler.sendMessageDelayed(message, 5);
}
}
}
};
}

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
super.onLayout(changed, l, t, r, b);
if (!isFirstEnterThisScrollView) {
isFirstEnterThisScrollView=true;
waterfallScrollViewHeight=getHeight();
mFirstLinearLayout=(LinearLayout) findViewById(R.id.firstLinearLayout);
mSecondLinearLayout=(LinearLayout) findViewById(R.id.secondLinearLayout);
mThirdLinearLayout=(LinearLayout) findViewById(R.id.thirdLinearLayout);
everyColumnWidth=mFirstLinearLayout.getWidth();
loadNextPageImages();
}
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
}

/**
* 这里对于手指抬起时(ACTION_UP)时,监听ScrollView是否已经停止滚动的判断的思路不错.
* 在ACTION_UP时直接用Handler发送一个消息在handleMessage中处理判断,如果此时还
* 没有停止滚动,则延时一定时间再次发送消息判断滚动是否停止.
* 这样做避免的在ACTION_UP时去加载图片而是在ScrollView停止滚动时去加载.
*/
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if (motionEvent.getAction()==MotionEvent.ACTION_UP) {
Message message=new Message();
message.obj=this;
message.what=REFRESH;
mHandler.sendMessageDelayed(message, 5);
}
return false;
}

private void loadNextPageImages(){
if (Utils.isExistSDCard()) {
int start=PAGE_SIZE*currentPage;
int end=PAGE_SIZE*currentPage+PAGE_SIZE;
LoadImageAsyncTask loadImageAsyncTask;
if (start<ImagesUrl.urlStringArray.length) {
if (end>ImagesUrl.urlStringArray.length) {
end=ImagesUrl.urlStringArray.length;
}
Toast.makeText(mContext, "开始加载", Toast.LENGTH_SHORT).show();
for (int i = start;i < end; i++) {
loadImageAsyncTask=new LoadImageAsyncTask();
loadImageAsyncTask.execute(ImagesUrl.urlStringArray[i]);
mLoadImageAsyncTaskHashSet.add(loadImageAsyncTask);
}
currentPage++;
} else {

}

} else {
Toast.makeText(mContext, "无SD卡", Toast.LENGTH_LONG).show();
}
}

/**
* 判断ImageView是否可见
* 如果可见:
* 1  从LruCache取出图片显示
* 2 若不在LruCache中,则开启异步任务下载
* 若不可见:
* 将ImageView显示的图片替换成本地图片
*/
private void checkAllImageViewVisibility(){
ImageView imageView=null;
for(int i=0;i<mAllImageViewArrayList.size();i++){
imageView=mAllImageViewArrayList.get(i);
int top_border=(Integer) imageView.getTag(R.string.TOP_BORDER_TAG);
int bottom_border=(Integer) imageView.getTag(R.string.BOTTOM_BORDER_TAG);
if (bottom_border > getScrollY() && top_border < getScrollY() + waterfallScrollViewHeight) {
String imageUrl=(String) imageView.getTag(R.string.IMAGE_URL_TAG);
Bitmap bitmap=mLruCacheImageLoader.getBitmapFromLruCache(imageUrl);
if (bitmap==null) {
LoadImageAsyncTask loadImageAsyncTask=new LoadImageAsyncTask();
loadImageAsyncTask.execute(imageUrl);
} else {
imageView.setImageBitmap(bitmap);
}

} else {
imageView.setImageResource(R.drawable.empty_photo);
}
}
}

/**
* 该LoadImageAsyncTask是获取网络图片的入口:
* 1 从LruCache中获取,取到则停止
* 2 若不在LruCache,则从SD卡获取
* 3 若在则从SD卡获取
* 4 若不在SD卡,则从网络获取且保持至SD卡
* 5 从SD卡获取下载的图片
* 6 添加到LruCache中
*
* 注意不管这个图片是在SD卡还是从网络下载,这都是获取图片的入口,这么做的好处
* 1   统一了获取图片的入口.
*   如果把获取图片分为图片在LruCache,图片在SD卡,图片在网络上这几种不同
*   的情况而去分别用对应的函数获取,这样势必会导致该需求的多入口.凌乱,不好优化.
*   而且这几种方式放到AsyncTask中都不会出错,尤其是网络请求耗时的情况下.
* 2 不管通过哪种方式获取到了图片,我们都要对图片再次修整,比如缩放.
*   我们可以把这些操作又统一放到异步操作的onPostExecute()方法中.
*/
private class LoadImageAsyncTask extends AsyncTask<String, Void, Bitmap>{
private String imageUrl;
private Bitmap bitmap;
@Override
protected Bitmap doInBackground(String... params) {
imageUrl=params[0];
bitmap=mLruCacheImageLoader.getBitmapFromLruCache(imageUrl);
if (bitmap==null) {
String filePath=Utils.getImageFilePath(imageUrl);
File imageFile=new File(filePath);
if (!imageFile.exists()) {
Utils.getBitmapFromNetWorkAndSaveToSDCard(imageUrl, filePath);
}
if (filePath!=null) {
bitmap=Utils.getBitmapFromSDCard(filePath, everyColumnWidth);
if (bitmap!=null) {
mLruCacheImageLoader.addBitmapToLruCache(imageUrl, bitmap);
}
}
} else {

}
return bitmap;
}

/**
* 在onPostExecute()对图片进行修整
* 因为在doInBackground()的loadImage()方法中已经把经过scale的图片存到了SD卡和LruCache中
* 并且在计算inSampleSize的时候是以宽width为标准的.
* 比如inSampleSize=2,那么保存的图的宽和高都是原来的二分之一.
* 但是请注意inSampleSize是int类型的,那么缩放出来的比例多半不是我们期望的刚好屏幕宽度的三分之一,它是有偏差的.
* 所以在这里进行修正,尤其是对高进行修正.
* 这样就保证了宽是一个定值(屏幕的三分之一),高也得到了调整,不至于严重失真.
*
*/
@Override
protected void onPostExecute(Bitmap bitmap) {
super.onPostExecute(bitmap);
mLoadImageAsyncTaskHashSet.remove(this);
if (bitmap!=null) {
double ration=bitmap.getWidth()/(everyColumnWidth*1.0);
int imageViewHeight=(int) (bitmap.getHeight()/ration);
int imageViewWidth=everyColumnWidth;
addImageToScrollView(bitmap,imageViewWidth,imageViewHeight,imageUrl);
}
}
}

/**
* 将获取到的Bitmap添加到ImageView中.
* 这里利用View.setTag()的方式为该ImageView保存了其相关信息.
* 比如该ImageView加载的图片的url,它的上下边在ScrollView中的位置信息等.
*/
private void addImageToScrollView(Bitmap bitmap,int imageViewWidth,int imageViewHeight,String imageUrl){
ImageView imageView=new ImageView(mContext);
LinearLayout.LayoutParams layoutParams=new LinearLayout.LayoutParams(imageViewWidth, imageViewHeight);
imageView.setImageBitmap(bitmap);
imageView.setLayoutParams(layoutParams);
imageView.setScaleType(ScaleType.FIT_XY);
imageView.setPadding(5, 5, 5, 5);
imageView.setTag(R.string.IMAGE_URL_TAG, imageUrl);
addImageToColumn(imageView);
mAllImageViewArrayList.add(imageView);
}

/**
* 找到高度最小的LinearLayout并且将ImageView添加进去
*/
private void addImageToColumn(ImageView imageView){
int imageViewHeight=imageView.getLayoutParams().height;
if (firstColumnHeight <= secondColumnHeight) {
if (firstColumnHeight <= thirdColumnHeight) {
imageView.setTag(R.string.TOP_BORDER_TAG, firstColumnHeight);
firstColumnHeight += imageViewHeight;
imageView.setTag(R.string.BOTTOM_BORDER_TAG, firstColumnHeight);
mFirstLinearLayout.addView(imageView);
}else{
imageView.setTag(R.string.TOP_BORDER_TAG, thirdColumnHeight);
thirdColumnHeight += imageViewHeight;
imageView.setTag(R.string.BOTTOM_BORDER_TAG, thirdColumnHeight);
mThirdLinearLayout.addView(imageView);
}
} else {
if (secondColumnHeight <= thirdColumnHeight) {
imageView.setTag(R.string.TOP_BORDER_TAG, secondColumnHeight);
secondColumnHeight += imageViewHeight;
imageView.setTag(R.string.BOTTOM_BORDER_TAG, secondColumnHeight);
mSecondLinearLayout.addView(imageView);
}else{
imageView.setTag(R.string.TOP_BORDER_TAG, thirdColumnHeight);
thirdColumnHeight += imageViewHeight;
imageView.setTag(R.string.BOTTOM_BORDER_TAG, thirdColumnHeight);
mThirdLinearLayout.addView(imageView);
}
}

}
}


LruCacheImageLoader如下:

package cc.patience3;

import android.graphics.Bitmap;
import android.util.LruCache;

public class LruCacheImageLoader {

private static LruCacheImageLoader mLruCacheImageLoader;

private static LruCache<String, Bitmap> mLruCache;

private LruCacheImageLoader(){
int maxMemory=(int) Runtime.getRuntime().maxMemory();
int size=maxMemory/6;
//设定LruCache的缓存为可用内存的六分之一
mLruCache=new LruCache<String, Bitmap>(size){
@Override
protected int sizeOf(String key, Bitmap bitmap) {
return bitmap.getByteCount();
}

};
}

public static LruCacheImageLoader getLruCacheImageLoaderInstance(){
if (mLruCacheImageLoader==null) {
mLruCacheImageLoader=new LruCacheImageLoader();
}
return mLruCacheImageLoader;
}

/**
* 从LruCache中获取图片,若不存在返回null
*/
public static Bitmap getBitmapFromLruCache(String key){
return mLruCache.get(key);
}

/**
* 往LruCache中添加图片.
* 当然要首先判断LruCache中是否已经存在该图片,若不存在再添加
*/
public static void addBitmapToLruCache(String key,Bitmap bitmap){
if (getBitmapFromLruCache(key)==null) {
mLruCache.put(key, bitmap);
}
}

}


Utils如下:

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileDescriptor;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import org.apache.http.HttpStatus;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapFactory.Options;
import android.os.Environment;

public class Utils {
public final static String IMAGES_DIR_NAME="waterfallImages";
public final static String IMAGES_DIR_PATH=Environment.getExternalStorageDirectory()+File.separator+IMAGES_DIR_NAME;

/**
* 判断SD卡是否存在
*/
public static boolean isExistSDCard() {
boolean isExist = false;
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
isExist = true;
}
return isExist;
}

/**
* 从SD卡中获取图片
*
* 注意事项:
* 这里采用BitmapFactory.decodeFileDescriptor()的方式来避免内存溢出.
* 而不是用BitmapFactory.decodeFile()的方式
*/
public static Bitmap getBitmapFromSDCard(String filePath,int requestWidth){
Bitmap bitmap=null;
try {
Options options=new Options();
options.inJustDecodeBounds=true;
BitmapFactory.decodeFile(filePath, options);
options.inSampleSize=calculateInSampleSize(options,requestWidth);
options.inJustDecodeBounds=false;
FileInputStream fileInputStream=new FileInputStream(filePath);
FileDescriptor fileDescriptor=fileInputStream.getFD();
bitmap=BitmapFactory.decodeFileDescriptor(fileDescriptor, null, options);
fileInputStream.close();
} catch (Exception e) {

}
return bitmap;
}

public static Bitmap fixBitmap(){
return null;
}

/**
* 计算图片的缩放比例
*/
public static int calculateInSampleSize(Options options,int requestWidth){
int inSampleSize=1;
//SD卡中图片的宽
int outWidth=options.outWidth;
if (outWidth>requestWidth) {
inSampleSize=Math.round((float) outWidth / (float) requestWidth);
}
return inSampleSize;
}

/**
* 依据图片的Url获取其在SDCard的存储路径
*/
public static String getImageFilePath(String imageUrl){
File dir=new File(IMAGES_DIR_PATH);
if (!dir.exists()) {
dir.mkdirs();
}
String imageFilePath=null;
String imageName=null;
int start=imageUrl.lastIndexOf("/");
int end=imageUrl.lastIndexOf(".");
imageName=imageUrl.substring(start+1, end);
imageFilePath=IMAGES_DIR_PATH+File.separator+imageName;
return imageFilePath;
}

/**
* 从网络获取图片且保存至SD卡
*/
public static void getBitmapFromNetWorkAndSaveToSDCard(String imageUrl,String filePath){
URL url=null;
File imageFile=null;
HttpURLConnection httpURLConnection=null;
FileOutputStream fileOutputStream=null;
BufferedOutputStream bufferedOutputStream=null;
InputStream inputStream=null;
BufferedInputStream bufferedInputStream=null;
try {
url=new URL(imageUrl);
httpURLConnection=(HttpURLConnection) url.openConnection();
httpURLConnection.setConnectTimeout(5*1000);
httpURLConnection.setReadTimeout(10*1000);
httpURLConnection.setDoInput(true);
httpURLConnection.setDoOutput(true);
if (httpURLConnection.getResponseCode()==HttpStatus.SC_OK) {
imageFile=new File(filePath);
if (!imageFile.getParentFile().exists()) {
imageFile.getParentFile().mkdirs();
}
if (!imageFile.exists()) {
imageFile.createNewFile();
}
fileOutputStream=new FileOutputStream(imageFile);
bufferedOutputStream=new BufferedOutputStream(fileOutputStream);
inputStream=httpURLConnection.getInputStream();
bufferedInputStream=new BufferedInputStream(inputStream);
int len=0;
byte [] buffer=new byte[1024];
while((len=bufferedInputStream.read(buffer))!=-1){
bufferedOutputStream.write(buffer, 0, len);
bufferedOutputStream.flush();
}
} else {
System.out.println("图片请求失败");
}
} catch (Exception e) {
System.out.println("e="+e.toString());
}finally{
try {
if (fileOutputStream!=null) {
fileOutputStream.close();
}
if (bufferedOutputStream!=null) {
bufferedOutputStream.close();
}
if (inputStream!=null) {
inputStream.close();
}
if (bufferedInputStream!=null) {
bufferedInputStream.close();
}
if (httpURLConnection!=null) {
httpURLConnection.disconnect();
}
} catch (Exception e) {
System.out.println("e="+e.toString());
}
}

}

}


ImagesUrl如下:

package cc.patience3;

public class ImagesUrl {

public static String urlStringArray []=new String []{
"http://img.my.csdn.net/uploads/201309/01/1378037235_3453.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037235_7476.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037235_9280.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037234_3539.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037234_6318.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037194_2965.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037193_1687.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037193_1286.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037192_8379.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037178_9374.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037177_1254.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037177_6203.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037152_6352.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037151_9565.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037151_7904.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037148_7104.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037129_8825.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037128_5291.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037128_3531.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037127_1085.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037095_7515.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037094_8001.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037093_7168.jpg",
"http://img.my.csdn.net/uploads/201309/01/1378037091_4950.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949643_6410.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949642_6939.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949630_4505.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949630_4593.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949629_7309.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949629_8247.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949615_1986.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_8482.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_3743.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949614_4199.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949599_3416.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949599_5269.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949598_7858.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949598_9982.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949578_2770.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949578_8744.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949577_5210.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949577_1998.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949482_8813.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949481_6577.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949480_4490.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949455_6792.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949455_6345.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949442_4553.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949441_8987.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949441_5454.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949454_6367.jpg",
"http://img.my.csdn.net/uploads/201308/31/1377949442_4562.jpg"
};

}


main.xml如下:

<cc.patience3.WaterfallScrollView 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" >

<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

<LinearLayout
android:id="@+id/firstLinearLayout"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" />

<LinearLayout
android:id="@+id/secondLinearLayout"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" />

<LinearLayout
android:id="@+id/thirdLinearLayout"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:layout_weight="1"
android:orientation="vertical" />

</LinearLayout>

</cc.patience3.WaterfallScrollView>
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: