/**
* DemoListView
* @version 1.0
* @author WuXx
* @Time 2014-08-15
* */
/*
一、实现功能:
(1)当列数较多,超过一屏时,整体视图支持左右滑动;
(2)当单列数据较长,可以通过拖拽表头改变列宽;
(3)为表格中每一项添加点击事件。
二、效果图:
三、搭建布局:
四、主要代码:
由易到难顺序:MyOnItemClickListener、MyListView、MyAdapter
(1)MyOnItemClickListener,回调函数机制。由MyAdapter调用。<span style="font-weight: normal;">public interface MyOnItemClickListener {
public void OnItemClickListener(View view,int line,int row,long id);
}
</span>
(2)MyListView,表头滑动事件,滑动事件会与HorizontalScrollView的滑动冲突,解决方案:在MotionEvent.ACTION_DOWN,设置一下HorizontalScrollView的touch不监听就好了。<span style="font-weight: normal;">/**
* Set TouchListener on tile ,if ACTION_MOVE is called ,the listView will change its columnWidth
* */
private void setTitleTouchListener(View v) {
// TODO Auto-generated method stub
for(int i=0;i<titles.length;i++){
final int column = i;
v.findViewById(titles[i]).setOnTouchListener(new OnTouchListener() {
int x = 0;
int x1 = 0;
int width = 0;
boolean isMoved = false;
int t = 20;
@SuppressLint("NewApi")</span>
<span style="font-weight: normal;"> @Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
//two teps
// 1.when touch down and move, change the width of head;
// 2.touch up ,change the width of the columns ;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
x = (int) event.getX();
hs.requestDisallowInterceptTouchEvent(true);
return true;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
x1 = (int) event.getX();
width= v.getMeasuredWidth()+(x1-x)+t;
if(t!=0)
t=0;
v.setLayoutParams(new LinearLayout.LayoutParams(width,v.getMeasuredHeight()));
x = x1;
isMoved = true;
return true;
}
if(event.getAction()==MotionEvent.ACTION_UP){
if(isMoved)
adapter.setColumnWidth(column, width);
return true;
}
if(event.getAction()==MotionEvent.ACTION_CANCEL){
if(isMoved)
adapter.setColumnWidth(column, width);
return true;
}
return true;
}
});
}
}</span>
(3)MyAdapter,这个有点小麻烦。参考SimpleAdapter来写的,不过略有不同。与SimpleAdapter重复部分不再赘述。详细内容请下载源代码看吧。
<span style="font-weight: normal;">public interface MyOnItemClickListener {
public void OnItemClickListener(View view,int line,int row,long id);
}
</span>
<span style="font-weight: normal;">/**
* Set TouchListener on tile ,if ACTION_MOVE is called ,the listView will change its columnWidth
* */
private void setTitleTouchListener(View v) {
// TODO Auto-generated method stub
for(int i=0;i<titles.length;i++){
final int column = i;
v.findViewById(titles[i]).setOnTouchListener(new OnTouchListener() {
int x = 0;
int x1 = 0;
int width = 0;
boolean isMoved = false;
int t = 20;
@SuppressLint("NewApi")</span>
<span style="font-weight: normal;"> @Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
//two teps
// 1.when touch down and move, change the width of head;
// 2.touch up ,change the width of the columns ;
if (event.getAction() == MotionEvent.ACTION_DOWN) {
x = (int) event.getX();
hs.requestDisallowInterceptTouchEvent(true);
return true;
}
if (event.getAction() == MotionEvent.ACTION_MOVE) {
x1 = (int) event.getX();
width= v.getMeasuredWidth()+(x1-x)+t;
if(t!=0)
t=0;
v.setLayoutParams(new LinearLayout.LayoutParams(width,v.getMeasuredHeight()));
x = x1;
isMoved = true;
return true;
}
if(event.getAction()==MotionEvent.ACTION_UP){
if(isMoved)
adapter.setColumnWidth(column, width);
return true;
}
if(event.getAction()==MotionEvent.ACTION_CANCEL){
if(isMoved)
adapter.setColumnWidth(column, width);
return true;
}
return true;
}
});
}
}</span>
数据以及点击事件的添加,其实是很简单的。private void bindView(int position, View v) {
// TODO Auto-generated method stub
for (int i = 0; i < mFrom.length; i++) {
final int line = position;
final int row = i;
TextView txt = (TextView) v.findViewById(mTo[i]);
txt.setText((String) mData.get(position).get(mFrom[i]));
txt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (MyAdapter.this.listener != null)
MyAdapter.this.listener.OnItemClickListener(v, line,
row, 0);
}
});
}
}
修改列宽,当MyListView滑动表头时修改表格主体的列宽。
private void bindView(int position, View v) {
// TODO Auto-generated method stub
for (int i = 0; i < mFrom.length; i++) {
final int line = position;
final int row = i;
TextView txt = (TextView) v.findViewById(mTo[i]);
txt.setText((String) mData.get(position).get(mFrom[i]));
txt.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
// TODO Auto-generated method stub
if (MyAdapter.this.listener != null)
MyAdapter.this.listener.OnItemClickListener(v, line,
row, 0);
}
});
}
}
public void setColumnWidth(int column, int width) {
for (int i = 0; i < viewList.size(); i++) {
View v = viewList.get(i);
TextView txt = (TextView) v.findViewById(mTo[column]);
txt.setLayoutParams(new LinearLayout.LayoutParams(width, txt
.getHeight()));
}
}
好吧,问题来了。当使用此方法进行显示时,当你滑动表头修改列宽,你再下拉查看未显示的数据,这个时候惊喜来了,空白?嗯是的。发现有一行的部分数据项是空白的,当你继续下拉,你会发现这个空白会循环的出现在你屏幕上,也许你猜到了这其中的原因。我们可以举一个例子,假如你的ListView第一次加载出来,屏幕上显示的item数量为23个,但是,getView调用了24次,当然,我这么说是不负责的,好吧,准确的说是生成了24个View(就是你的item),why 24?因为有一个影藏的。第24个View的产生是因为安卓内部有一个recycler机制,实现了View的循环使用,显而易见,23个是无法实现循环的。这里我借鉴网上的一张图,大家就一目了然了。
public void setColumnWidth(int column, int width) {
for (int i = 0; i < viewList.size(); i++) {
View v = viewList.get(i);
TextView txt = (TextView) v.findViewById(mTo[column]);
txt.setLayoutParams(new LinearLayout.LayoutParams(width, txt
.getHeight()));
}
}
抱歉,这里不是我们的重点,如果有问题大家问度娘好了。好吧,回到我们的故事上。现在你几经调试,你发现原来是影藏的View在捣乱。也许你想到了,我们不让它影藏,问题不就解决了嘛。ok。你把MyListView初始化完成后,让其在0.1s内完成“下滑2格、上滑2格”的动作,这样当然就不存在影藏的View,不过你发现效果不是很理想,因为空白还是偶尔会出现。固执的你,继续疯狂的调试。突然,灵光闪过,你发现了问题的根源。MyAdapter的正常调用顺序是:getView-->setColumnWidth(getView的主要作用是初始化布局,即创建View并给其赋值,而setColumnWidth的作用就是通过setLayoutParams方法修改布局的宽度)。但是对于影藏的View顺序却恰恰相反,所以你决定,重新修改代码,将影藏的View的顺序也修改为getView-->setColumnWidth。于是在你新一版的代码中,getView初始化完成后,你又通过setLayoutParams的方法修改了一次布局。
这时,聪明的你发现了,我们没有涉及到如何在getView中找出影藏的View。其实这个问题我也没有特别好的解决方案,目前我采取的方案,可以在把空白问题出现的概率控制在比较小的范围。这种方案是通过5、6次调试通过log日志得出的,有一定的局限性。
通过getView给出的参数,position、parent以及用来存储视图的ViewList,进行判断影藏的View。代码如下:
if(lastViewsSize==viewList.size()){
if(position!=0){
if(position==parent.getChildCount()&&position==viewList.size()-1){
setWidth(v);
}
}
}else {
lastViewsSize = viewList.size();
}
if(lastViewsSize==viewList.size()){
if(position!=0){
if(position==parent.getChildCount()&&position==viewList.size()-1){
setWidth(v);
}
}
}else {
lastViewsSize = viewList.size();
}