Android支持水平滚动的ListView控件
摘要 前言 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); }/** 分发触摸事件 */ @Overridepublicboolean dispatchTouchEvent(MotionEvent ev) {super.dispatchTouchEvent(ev);return mGesture.onTouchEvent(ev); }/** 手势 */private OnGestureListener mOnGesture = new GestureDetector.SimpleOnGestureListener() { @Overridepublicboolean onDown(MotionEvent e) {returntrue; } @Overridepublicboolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,float velocityY) {returnfalse; }/** 滚动 */ @Overridepublicboolean 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. */ @Overridepublicvoid 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 { @Overridepublicint getCount() {return 50;//固定显示50行数据 } @Overridepublic 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; } @Overridepublic Object getItem(int position) {returnnull; } @Overridepubliclong 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操作,所以即使用力滑也不好滑太多,希望在后续版本改进。
四、代码下载

浙公网安备 33010602011771号