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 = newGestureDetector(context, mOnGesture);    }/** 分发触摸事件 */    @Overridepublicboolean dispatchTouchEvent(MotionEvent ev) {super.dispatchTouchEvent(ev);returnmGesture.onTouchEvent(ev);    }/** 手势 */private OnGestureListener mOnGesture = newGestureDetector.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();            }        }returnscreenWidth;    }/** 获取列头偏移量 */publicint getHeadScrollX() {returnmListHead.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(newDataAdapter());        mInflater = (LayoutInflater) getSystemService(LAYOUT_INFLATER_SERVICE);    }privateclass DataAdapter extends BaseAdapter {        @Overridepublicint getCount() {return50;//固定显示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);            }returnconvertView;        }        @Overridepublic Object getItem(int position) {returnnull;        }        @Overridepubliclong getItemId(int position) {return0;        }    }} | 
代码说明:
为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号
浙公网安备 33010602011771号