Android · 2014年12月14日 0

Android实现类似Excel显示数据功能(支持拖动改变列宽)v 1.0

/**
* DemoListView
* @version 1.0
* @author WuXx
* @Time 2014-08-15
* */
/*

一、实现功能:

(1)当列数较多,超过一屏时,整体视图支持左右滑动;

(2)当单列数据较长,可以通过拖拽表头改变列宽;

(3)为表格中每一项添加点击事件。

 

二、效果图:

图片有些小。

三、搭建布局:
四、主要代码:
由易到难顺序:MyOnItemClickListener、MyListView、MyAdapter
(1)MyOnItemClickListener,回调函数机制。由MyAdapter调用。
  1. <span style=“font-weight: normal;”>public interface MyOnItemClickListener {  
  2.     public void OnItemClickListener(View view,int line,int row,long id);  
  3. }  
  4. </span>  
(2)MyListView,表头滑动事件,滑动事件会与HorizontalScrollView的滑动冲突,解决方案:在MotionEvent.ACTION_DOWN,设置一下HorizontalScrollView的touch不监听就好了。
  1. <span style=“font-weight: normal;”>/** 
  2.  * Set TouchListener on tile ,if ACTION_MOVE is called ,the listView will change its columnWidth 
  3.  * */  
  4.     private void setTitleTouchListener(View v) {  
  5.         // TODO Auto-generated method stub  
  6.         for(int i=0;i<titles.length;i++){  
  7.             final int column = i;  
  8.             v.findViewById(titles[i]).setOnTouchListener(new OnTouchListener() {  
  9.                 int x = 0;  
  10.                 int x1 = 0;  
  11.                 int width = 0;  
  12.                 boolean isMoved = false;  
  13.                 int t = 20;  
  14.                 @SuppressLint(“NewApi”)</span>  
  15.     <span style=“font-weight: normal;”>           @Override  
  16.                 public boolean onTouch(View v, MotionEvent event) {  
  17.                     // TODO Auto-generated method stub  
  18.                     //two teps  
  19.                     // 1.when touch down and move, change the width of head;  
  20.                     // 2.touch up ,change the width of the columns ;  
  21.                       
  22.                     if (event.getAction() == MotionEvent.ACTION_DOWN) {  
  23.                         x = (int) event.getX();  
  24.                         hs.requestDisallowInterceptTouchEvent(true);  
  25.                         return true;  
  26.                     }  
  27.                     if (event.getAction() == MotionEvent.ACTION_MOVE) {  
  28.                         x1 = (int) event.getX();  
  29.                         width= v.getMeasuredWidth()+(x1-x)+t;  
  30.                         if(t!=0)  
  31.                             t=0;  
  32.                         v.setLayoutParams(new LinearLayout.LayoutParams(width,v.getMeasuredHeight()));  
  33.                         x = x1;  
  34.                         isMoved = true;  
  35.                         return true;  
  36.                     }  
  37.                     if(event.getAction()==MotionEvent.ACTION_UP){  
  38.                         if(isMoved)  
  39.                             adapter.setColumnWidth(column, width);  
  40.                         return true;  
  41.                           
  42.                     }  
  43.                     if(event.getAction()==MotionEvent.ACTION_CANCEL){  
  44.                         if(isMoved)  
  45.                             adapter.setColumnWidth(column, width);  
  46.                         return true;  
  47.                     }  
  48.                       
  49.                     return true;  
  50.                 }  
  51.             });  
  52.         }  
  53.     }</span>  
(3)MyAdapter,这个有点小麻烦。参考SimpleAdapter来写的,不过略有不同。与SimpleAdapter重复部分不再赘述。详细内容请下载源代码看吧。
数据以及点击事件的添加,其实是很简单的。
  1. private void bindView(int position, View v) {  
  2.         // TODO Auto-generated method stub  
  3.         for (int i = 0; i < mFrom.length; i++) {  
  4.             final int line = position;  
  5.             final int row = i;  
  6.             TextView txt = (TextView) v.findViewById(mTo[i]);  
  7.             txt.setText((String) mData.get(position).get(mFrom[i]));  
  8.             txt.setOnClickListener(new OnClickListener() {  
  9.                 @Override  
  10.                 public void onClick(View v) {  
  11.                     // TODO Auto-generated method stub  
  12.                     if (MyAdapter.this.listener != null)  
  13.                         MyAdapter.this.listener.OnItemClickListener(v, line,  
  14.                                 row, 0);  
  15.                 }  
  16.             });  
  17.         }  
  18.     }  
修改列宽,当MyListView滑动表头时修改表格主体的列宽。
  1. public void setColumnWidth(int column, int width) {  
  2.         for (int i = 0; i < viewList.size(); i++) {  
  3.             View v = viewList.get(i);  
  4.             TextView txt = (TextView) v.findViewById(mTo[column]);  
  5.             txt.setLayoutParams(new LinearLayout.LayoutParams(width, txt  
  6.                     .getHeight()));  
  7.         }  
  8.     }  
好吧,问题来了。当使用此方法进行显示时,当你滑动表头修改列宽,你再下拉查看未显示的数据,这个时候惊喜来了,空白?嗯是的。发现有一行的部分数据项是空白的,当你继续下拉,你会发现这个空白会循环的出现在你屏幕上,也许你猜到了这其中的原因。我们可以举一个例子,假如你的ListView第一次加载出来,屏幕上显示的item数量为23个,但是,getView调用了24次,当然,我这么说是不负责的,好吧,准确的说是生成了24个View(就是你的item),why 24?因为有一个影藏的。第24个View的产生是因为安卓内部有一个recycler机制,实现了View的循环使用,显而易见,23个是无法实现循环的。这里我借鉴网上的一张图,大家就一目了然了。
抱歉,这里不是我们的重点,如果有问题大家问度娘好了。好吧,回到我们的故事上。现在你几经调试,你发现原来是影藏的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。代码如下:
  1. if(lastViewsSize==viewList.size()){  
  2.             if(position!=0){  
  3.                 if(position==parent.getChildCount()&&position==viewList.size()-1){  
  4.                     setWidth(v);  
  5.                 }  
  6.             }  
  7.         }else {  
  8.             lastViewsSize = viewList.size();  
  9.         }  
大概描述一下:
首先申明,当ListView在一个屏幕上显示23个时,它并不只是调用24次Adapter的getView,正常情况应该是23(或者说是24,这个值我不能确定)的3、4倍,为什么有这么多呢?其中有一次是用于确定布局的宽度和高度用于draw的,其他的我不清楚。
但是,可以确定当最后一次调用getView时是用于显示的,我们如何确定这一次呢,通过log日志可以看出,此时viewList.size()已经为常值,所以第一个判断条件便是如此产生的,当然我们需要排除掉position为0的干扰,最后我们找出我们影藏的View,然后setWidth,游戏就这样结束了。我觉得有必要把调试过程中log日志也贴出来:
源代码:http://download.csdn.net/detail/hello1234123/7765011

 

转自:http://blog.csdn.net/hello1234123/article/details/38590073

Share this: