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

/**
 * 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重复部分不再赘述。详细内容请下载源代码看吧。
数据以及点击事件的添加,其实是很简单的。
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滑动表头时修改表格主体的列宽。
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个是无法实现循环的。这里我借鉴网上的一张图,大家就一目了然了。

抱歉,这里不是我们的重点,如果有问题大家问度娘好了。好吧,回到我们的故事上。现在你几经调试,你发现原来是影藏的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();
		}

大概描述一下:
首先申明,当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
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值