在做一个项目的时候,需要我们使用瀑布流的界面来显示相关的界面。

   说实话如果让我自己完完全全的想来做的话,也没有想到和做出,在此觉得自己有点像个代码农民啊,经常在网上收集一些代码,但是很少有自己完完全全的思考,锻炼自己的思维模式和思维方式,来完成出自己的代码。。。


以下就是我在网上学习到的代码,然后在结合我自己的理解,重写了一遍。


程序最重要的对功能的思维方法和把自己的想的实现,现在真心觉得,尽量不要在网上找别人的代码,虽然他山之石可以攻玉,但是自己的思维还是需要锻炼。。。


算了,就暂时不感慨了,,,


看代码和注释。。。


思维的方法:

首先瀑布流是在窗体上下滑动的,所以就需要一个SrcollView来做界面的root元素,然后在其下就是一个容器(LineraLayout)水平排列 ,在这个容器下面,就有X列个LineraLayout垂直排列,来作为每一列的容器。

然后在加载相关的图片资源,使用多线程进行加载,加载完成之后,在使用Handler和UI更新线程来更新界面。

这样子瀑布流就完成了。。。(当然通过完善SrcollView滑动的接口,可以对数据进行有条件的删除,因为图片资源过多可能会花费大量的内容资源)


重写的MyScrollView的类,UI界面的根元素

public class MyScrollView extends ScrollView
 {
//界面
View view;
//消息传递
Handler handler;

//滑动监听(自己完成)
OnSrcollViewListener onSrcollViewListener;


//触摸事件
OnTouchListener onTouchListener = new OnTouchListener()
{
@Override
public boolean onTouch(View v, MotionEvent event)
{
if( event.getAction() == MotionEvent.ACTION_DOWN  )
{
System.out.println("onTouchListener按下");
}
else if( event.getAction() == MotionEvent.ACTION_UP )
{
System.out.println("onTouchListener起来");
Message msg  = new Message();
msg.arg1 = 1;
handler.sendMessageDelayed(msg, 200);
}
return false;
}
};

//初始化函数
private void init()
{
//处理接受到的消息
handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
MyScrollView.this.setOnTouchListener(onTouchListener);
switch (msg.arg1)
{
case 1:
//对滚动条滑动之后的结果处理
if( view.getMeasuredHeight() - 20 <= getScrollY() + view.getBottom() )
{
//到底部
if( onSrcollViewListener != null )
onSrcollViewListener.OnButtom();
}
else if( getScrollY() == 0 )
{
//到顶部
if( onSrcollViewListener != null )
onSrcollViewListener.OnTop();
}
else
{
//其他
if (onSrcollViewListener != null)
onSrcollViewListener.OnScroll();
}
break;
default:
break;
}
}//handleMessage
};
}

//获得组见,并且加载数据
public void getView()
{
this.view = getChildAt(0);
if( view != null )
{
init();
}
}

//接口
public interface OnSrcollViewListener
{
public void OnTop();
public void OnAutoScroll(int l, int t, int oldl, int oldt );
public void OnScroll();
public void OnButtom();
}

//-------------
public MyScrollView(Context context)
{
super(context);
}

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


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



@Override
protected int computeVerticalScrollOffset()
{
return super.computeVerticalScrollOffset();
}


@Override
protected int computeVerticalScrollRange()
{
return super.computeVerticalScrollRange();
}


@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt)
{
super.onScrollChanged(l, t, oldl, oldt);
onSrcollViewListener.OnAutoScroll(l, t, oldl, oldt);
}


public void setOnSrcollViewListener(OnSrcollViewListener onSrcollViewListener)
{
this.onSrcollViewListener = onSrcollViewListener;
}
 }



主ACTIVITY类:
public class MyWaterFallDemo1Activity extends Activity
 {
//滑动
MyScrollView scrollView;

//主容器
LinearLayout mainLayout;

//三个小容器
ArrayList<LinearLayout> childLinearLayouts;

//获得手机的屏幕大小
Display display;

//当前列数
int ColCounts = 3;

//图片的名字
List<String> picNames;

//读取ass文件夹的文件夹工具
AssetManager assetManager;

//文件读取的路径
static String path = "images";

//每一列高度
int colHeights[];

//每一列的个数
int sumOfCols[];

//每一列的宽度(通过计算好了的)
int colHeight;

//消息传递和接受消息
Handler handler;

//上下文环境
Context context;

//每一次加载图片的数目
int page_count = 30;

//已经加载的个数
int count_loades = 0;

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

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

scrollView = (MyScrollView)findViewById(R.id.scroll);
scrollView.getView();
scrollView.setOnSrcollViewListener(new OnSrcollViewListener()
{
@Override
public void OnTop()
{
System.out.println("OnTop");
}

@Override
public void OnScroll()
{
System.out.println("OnScroll");
}

@Override
public void OnButtom()
{
System.out.println("OnButtom");
}

@Override
public void OnAutoScroll(int l, int t, int oldl, int oldt)
{
System.out.println("OnAutoScroll");
}
});

//每一列的高度
colHeights = new int[ColCounts];
//每一列的图片个数
sumOfCols = new int[ColCounts];

display = this.getWindowManager().getDefaultDisplay();
colHeight = display.getWidth() / ColCounts;
assetManager = this.getAssets();

context = this;

//处理接受到的消息(当图片加载完成之后)
handler = new Handler()
{
@Override
public void handleMessage(Message msg)
{
super.handleMessage(msg);
switch (msg.what)
{
case 1:
//获得接收到的图片
MyView view = (MyView) msg.obj;
int w = msg.arg1;
int h = msg.arg2;
//选择加入在那一个列中区(当前列的高度是其他所有列的最小一个)
int choiceCol = getMinCol(colHeights);
//加入在当前列中区
childLinearLayouts.get(choiceCol).addView(view);
//更新数据
sumOfCols[choiceCol]++;//当前列数目+1
colHeights[choiceCol] += h;//当前列的高度+h
break;
default:
break;
}
}
@Override
public void dispatchMessage(Message msg)
{
super.dispatchMessage(msg);
}
@Override
public boolean sendMessageAtTime(Message msg, long uptimeMillis)
{
return super.sendMessageAtTime(msg, uptimeMillis);
}
};

//获得主容器
mainLayout = (LinearLayout)findViewById(R.id.MainLineraLayout);
childLinearLayouts = new ArrayList<LinearLayout>();

//加载图片
for( int i = 0 ; i < ColCounts ; i++ )
{
//初始化子容器,并且加载进去
LinearLayout tLayout = new LinearLayout(context);
LayoutParams params = new LayoutParams(colHeight,LayoutParams.WRAP_CONTENT);
tLayout.setPadding(2, 2, 2, 2);
tLayout.setLayoutParams(params);
tLayout.setOrientation(LinearLayout.VERTICAL);
childLinearLayouts.add(tLayout);
mainLayout.addView(tLayout);
}

//加速图片数据
try
{
picNames =  Arrays.asList( assetManager.list(path) );
}
catch (Exception e)
{
e.printStackTrace();
}
//第一次加载30张图片
initAddData(current_page,page_count);
}

//第一次加载30张图片
private void initAddData(int pageindex, int pagecount)
{
for( int i = current_page*pagecount ; i < (current_page+1)*pagecount;  i++ )
{
//总数+1
count_loades++;
Random random = new Random();
//随机获得图片
int tmp = random.nextInt( picNames.size() );
//读取在写入
addImage(picNames.get(tmp), (int) Math.ceil(count_loades / (double) ColCounts), count_loades);
}
}

//加入图片
private void addImage(String name , int rowIndex , int id)
{
//创建图片资源
MyView view = new MyView(context);

//设置参数
view.setRow(rowIndex);
view.setId(id);
view.setViewhHandler(handler);

//设置加载参数
MyViewFlag flag = new MyViewFlag();
flag.setId(id);
flag.setAssetManager(assetManager);
flag.setFileNames(path+"/"+name);
flag.setItemWidth(colHeight);

view.setFlag(flag);
//读取加载(开辟线程)
view.loadImage();
}


//获得3列长度最小的列数
public int getMinCol( int array[] )
{
int pos = 0;
for( int i = 0 ; i < array.length ; i++ )
{
if( array[i] < array[pos] )
{
pos = i;
}
}
return pos;
}
 }
主界面的布局文件
main1.xml
<?xml version="1.0" encoding="utf-8"?>
 <com.ljz.waterfall.view.MyScrollView 
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:id="@+id/scroll"
     android:scrollbars="vertical">
     
     <LinearLayout 
         android:layout_width="fill_parent"
      android:layout_height="fill_parent"
      android:id="@+id/MainLineraLayout"
       android:background="@android:color/white" 
      android:scrollbars="horizontal">
     </LinearLayout>
     
 </com.ljz.waterfall.view.MyScrollView>
MyView.java图片类
public class MyView extends ImageView implements  View.OnClickListener,
View.OnLongClickListener
 {
Context context;//上下文
int row;  //当前行数
int col;  //列数
Handler viewhHandler;//消息传递对象


Bitmap bitmap;//图片资源
MyViewFlag flag;//加载参数


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


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


public MyView(Context c)
{
super(c);
this.context = c;
init();
}


private void init()
{
setOnClickListener(this);
setOnLongClickListener(this);
setAdjustViewBounds(true);
}


@Override
public boolean onLongClick(View v)
{
Toast.makeText(context, "长按:" + this.flag.getId(), Toast.LENGTH_SHORT).show();
return false;
}


@Override
public void onClick(View arg0)
{
Toast.makeText(context, "短按:" + this.flag.getId(), Toast.LENGTH_SHORT).show();
}


//提取加载图片
public void loadImage()
{
if( getFlag() != null )
{
new LoadThread().start();
}
}


//加载线程
class LoadThread extends Thread
{
public void run()
{
if( flag == null )
{
return ;
}
//读取图片数据
BufferedInputStream buf;
try
{
buf = new BufferedInputStream(flag.getAssetManager().open(flag.getFileNames()));
bitmap = BitmapFactory.decodeStream(buf);
}
catch (IOException e)
{
e.printStackTrace();
}
//UI线程更新
((Activity)context).runOnUiThread(new Runnable()
{
@Override
public void run()
{
if( bitmap != null )
{
int w = bitmap.getWidth();
int h = bitmap.getHeight();
LayoutParams p =getLayoutParams();
//获得缩放后的高度大小
int layout_h = (h*flag.getItemWidth())/w;
if( p == null )
{
p  = new LayoutParams(flag.getItemWidth(),layout_h);
}
setLayoutParams(p);
setImageBitmap(bitmap);
//发送消息到MainActivity中区
Handler handler = getViewhHandler();
Message m = handler.obtainMessage(flag.what, w,layout_h,MyView.this);
handler.sendMessage(m);
}
}
});
}
}


public int getRow()
{
return row;
}


public void setRow(int row)
{
this.row = row;
}


public int getCol()
{
return col;
}


public void setCol(int col)
{
this.col = col;
}


public Handler getViewhHandler()
{
return viewhHandler;
}


public void setViewhHandler(Handler viewhHandler)
{
this.viewhHandler = viewhHandler;
}


public MyViewFlag getFlag()
{
return flag;
}


public void setFlag(MyViewFlag flag)
{
this.flag = flag;
}
 }
MyViewFlag.java加载类
public class MyViewFlag
 {
int id;  //图片ID
String fileNames;//图片名字
private AssetManager assetManager;//读取工具
private int ItemWidth;//每一列的宽度
public final int what = 1;//标记


public int getId()
{
return id;
}
public void setId(int id)
{
this.id = id;
}
public String getFileNames()
{
return fileNames;
}
public void setFileNames(String fileNames)
{
this.fileNames = fileNames;
}
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;
}
public int getWhat()
{
return what;
}

 }

更新:

这段期间一直在做瀑布流的代码,在实际的接触中验证了2个事实。。。

1.开辟线程的方法并没有线程池管用。。。

2.使用这个方法的瀑布流会造成内存溢出。。。溢出的原因通过时用mat得出的结果是:LineraLayout这个空间。。。因为一直都在使用Inflate这个方法,而没有重复使用view导致data object不断的增长,最后达到应用程序不可以容忍的地步27M就别kill掉了。。。

3.要良好的让应用程序内存稳定的话,最好使用重复是可视范围的控件 ,这样非常好的。。。、、、