摘要 前言 ListView是一个纵向滚动的列表视图,也有朋友嵌套HorizontalScrollView来实现,比如 这里 ,但在ListView的API中明确指明了两者不可同时使用。本文分享一种办法,以方便有此需求的朋友。 正文 一、本文目标 效果图: a). 支持ListView横行滚动 b). 支持
前言
ListView是一个纵向滚动的列表视图,也有朋友嵌套HorizontalScrollView来实现,比如这里,但在ListView的API中明确指明了两者不可同时使用。本文分享一种办法,以方便有此需求的朋友。
正文
一、本文目标
效果图:
a). 支持ListView横行滚动
b). 支持固定第一列
二、 实现代码
2.1 Java类
自定义控件HVListView
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
/** * 自定义支持横向滚动的ListView * @author 农民伯伯 * */ publicclass HVListView extends ListView { /** 手势 */ private GestureDetector mGesture; /** 列头 */ public LinearLayout mListHead; /** 偏移坐标 */ privateint mOffset = 0; /** 屏幕宽度 */ privateint screenWidth; /** 构造函数 */ public HVListView(Context context, AttributeSet attrs) { super (context, attrs); mGesture = new GestureDetector(context, mOnGesture); } /** 分发触摸事件 */ @Override publicboolean dispatchTouchEvent(MotionEvent ev) { super .dispatchTouchEvent(ev); return mGesture.onTouchEvent(ev); } /** 手势 */ private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { @Override publicboolean onDown(MotionEvent e) { returntrue; } @Override publicboolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) { returnfalse; } /** 滚动 */ @Override publicboolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) { synchronized (HVListView. this ) { int moveX = (int) distanceX; int curX = mListHead.getScrollX(); int scrollWidth = getWidth(); int dx = moveX; //控制越界问题 if (curX + moveX < 0) dx = 0; if (curX + moveX + getScreenWidth() > scrollWidth) dx = scrollWidth - getScreenWidth() - curX; mOffset += dx; //根据手势滚动Item视图 for (int i = 0, j = getChildCount(); i < j; i++) { View child = ((ViewGroup) getChildAt(i)).getChildAt(1); if (child.getScrollX() != mOffset) child.scrollTo(mOffset, 0); } mListHead.scrollBy(dx, 0); } requestLayout(); returntrue; } }; /** * 获取屏幕可见范围内最大屏幕 * @return */ publicint getScreenWidth() { if (screenWidth == 0) { screenWidth = getContext().getResources().getDisplayMetrics().widthPixels; if (getChildAt(0) != null ) { screenWidth -= ((ViewGroup) getChildAt(0)).getChildAt(0) .getMeasuredWidth(); } elseif (mListHead != null ) { //减去固定第一列 screenWidth -= mListHead.getChildAt(0).getMeasuredWidth(); } } return screenWidth; } /** 获取列头偏移量 */ publicint getHeadScrollX() { return mListHead.getScrollX(); } } |
代码说明:
自定义HVListView继承自ListView,增加了横向手势监听,并在横向滚动时手动触发Layout容器内的滚动。
Activity
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
publicclass TestHVListViewActivity extends Activity { private LayoutInflater mInflater; private HVListView mListView; /** Called when the activity is first created. */ @Override publicvoid onCreate(Bundle savedInstanceState) { super .onCreate(savedInstanceState); setContentView(R.layout.main); mListView = (HVListView) findViewById(android.R.id.list); //设置列头 mListView.mListHead = (LinearLayout) findViewById(R.id.head); //设置数据 mListView.setAdapter( new DataAdapter()); mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE); } privateclass DataAdapter extends BaseAdapter { @Override publicint getCount() { return 50; //固定显示50行数据 } @Override public View getView(int position, View convertView, ViewGroup parent) { if (convertView == null ) { convertView = mInflater.inflate(R.layout.item, null ); } for (int i = 0; i < 8; i++) { ((TextView) convertView.findViewById(R.id.item2 + i)).setText( "数据" + position + "行" + (i + 2) + "列" ); } //校正(处理同时上下和左右滚动出现错位情况) View child = ((ViewGroup) convertView).getChildAt(1); int head = mListView.getHeadScrollX(); if (child.getScrollX() != head) { child.scrollTo(mListView.getHeadScrollX(), 0); } return convertView; } @Override public Object getItem(int position) { returnnull; } @Override publiclong getItemId(int position) { return 0; } } } |
代码说明:
为ListView提供了模拟数据。注意getView里面还有一段代码是校验,是专门处理同时横向和纵向滚动出现错位的情况。
2.2 XML文件
main.xml
1
2
3
4
5
6
7
8
9
10
11
12
|
<?xml version= "1.0" encoding= "utf-8" ?> <LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android" android:orientation= "vertical" android:background= "#eeffcc" android:layout_width= "wrap_content" android:layout_height= "fill_parent" > <include layout= "@layout/item" /> <com.nmbb.HVListView android:id= "@android:id/list" android:background= "#FFB84D" android:fastScrollEnabled= "true" android:fadingEdgeLength= "0.0sp" android:layout_width= "1400.0dip" android:layout_height= "fill_parent" android:drawSelectorOnTop= "false" android:cacheColorHint= "@null" android:dividerHeight= "1.0dip" > </com.nmbb.HVListView> </LinearLayout> |
代码说明:
注意这里需要指定HVListView的layout_width为滑动范围值,由item累加。
item.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
|
<?xml version= "1.0" encoding= "utf-8" ?> <LinearLayout xmlns:android= "http://schemas.android.com/apk/res/android" android:orientation= "horizontal" android:layout_width= "wrap_content" android:layout_height= "wrap_content" > <TextView android:id= "@+id/item1" android:text= "不动列头1" android:textSize= "20.0sp" android:gravity= "center" android:layout_width= "100.0dip" android:layout_height= "wrap_content" ></TextView> <LinearLayout android:orientation= "horizontal" android:id= "@+id/head" android:layout_width= "1200.0dip" android:layout_height= "wrap_content" > <TextView android:id= "@+id/item2" android:text= "不动列头2" android:textColor= "@android:color/black" android:textSize= "20.0sp" android:singleLine= "true" android:gravity= "center" android:layout_width= "150.0dip" android:layout_height= "wrap_content" ></TextView> <TextView android:id= "@+id/item3" android:text= "不动列头3" android:textSize= "20.0sp" android:singleLine= "true" android:gravity= "center" android:layout_width= "150.0dip" android:layout_height= "wrap_content" ></TextView> <TextView android:id= "@+id/item4" android:text= "不动列头4" android:textColor= "@android:color/black" android:textSize= "20.0sp" android:singleLine= "true" android:gravity= "center" android:layout_width= "150.0dip" android:layout_height= "wrap_content" ></TextView> <TextView android:id= "@+id/item5" android:text= "不动列头5" android:textSize= "20.0sp" android:singleLine= "true" android:gravity= "center" android:layout_width= "150.0dip" android:layout_height= "wrap_content" ></TextView> <TextView android:id= "@+id/item6" android:text= "不动列头6" android:textColor= "@android:color/black" android:textSize= "20.0sp" android:singleLine= "true" android:gravity= "center" android:layout_width= "150.0dip" android:layout_height= "wrap_content" ></TextView> <TextView android:id= "@+id/item7" android:text= "不动列头7" android:textSize= "20.0sp" android:singleLine= "true" android:gravity= "center" android:layout_width= "150.0dip" android:layout_height= "wrap_content" ></TextView> <TextView android:id= "@+id/item8" android:text= "不动列头8" android:textColor= "@android:color/black" android:textSize= "20.0sp" android:singleLine= "true" android:gravity= "center" android:layout_width= "150.0dip" android:layout_height= "wrap_content" ></TextView> <TextView android:id= "@+id/item9" android:text= "不动列头9" android:textSize= "20.0sp" android:singleLine= "true" android:gravity= "center" android:layout_width= "150.0dip" android:layout_height= "wrap_content" ></TextView> </LinearLayout> </LinearLayout> |
代码说明:
注意指定了每一个TextView的宽度为固定宽度,这样表格看起来就比较整齐。
三、注意问题
从代码看得出,本办法只能算个笨办法,能满足基本需求,比较麻烦的是需要自己来指定固定宽度。在企业应用展示多行多列数据时还是非常有用的,比如炒股软件也有这样的需求。
特别提醒大家注意设置固定宽度,还需要把最外面的容器的宽度设置为warp_content,以便支持容器内能够延伸。
当前不支持Fling操作,所以即使用力滑也不好滑太多,希望在后续版本改进。
四、代码下载