在项目的进行中要用到左右滑动又能上下滑动的列表布局,开始想的是使用一个ViewPager嵌套一个ListView来进行分页处理,在滑动ViewPager的时候翻页,效果是达到了,但是拓展性不好,所以我又另想法子,现在使用一个HorizontalScrollView嵌套一个ListView可以很好的解决拓展的问题,效果如下,原谅我GIF做的如此的简陋,这里提供一个思路,希望能帮到有需要的朋友,源代码附在文后。
简单讲下实现的思路:思路应该从需求开始讲,项目中用到的这个类似电影院选座位或考场编座位,界面上有xx排xx列个位置,需要按照票面的座位号对号入座,如果一个屏幕能装下还好解决,使用GridView就应该可以应付了,但是如果横向超出一个屏幕,好像GridView就有点无能为力了,所以我这里思考使用HorizontalScorllView嵌套一层布局去实现,里面的布局我使用的是ListView,实现竖向滑动,不要问为什么,我喜欢,就是这么任性,当然如果你喜欢使用GridView,请随意
,因为他们俩都是AbsListVIew的直接子类,用法的话自己去琢磨
。
二话不说先上代码了,这次从布局看起
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context="com.cjt.horizontalscrollviewdemo.MainActivity">
<HorizontalScrollView
android:id="@+id/mHorizontalScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/titleLayout"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp"/>
<ListView
android:id="@+id/mListView"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
这就是我的主页面布局文件,在HorizontalScrollView中嵌套ListView布局,使用ScrollView或者HorizontalScrollView的时候一定要注意,因为他们比较特别,只承认一个儿子,也就是嵌套的子布局只能是一个,所以我这里使用的是LinearLayout包裹ListView控件,可能细心的你会问,还有一个LinearLayout是干嘛的,其实他不只是打酱油的,他就是上面GIF中的第几列的标题,ListView上下滑动的时候,他是保持不动的,可以理解为Excel中的表头,分析完毕。接着上代码。看下MainActivity长什么样
public class MainActivity extends AppCompatActivity {
// 界面上的控件
private ListView mListView;
private MyListAdapter mAdapter;
private LinearLayout mTitleLayout ;
// 相关的数据
private List<ItemBean> dataList = new ArrayList<>(); // 测试实体类的集合
private ItemBean bean; // 测试实体类,模拟一个座位,有头像和姓名,以及座位号
private List<String> idList = new ArrayList<>(); // 为了大家便于理解,存放票面号
private static final int COLUMN_NUM = 10; // 列数
private static final int ROW_NUM = 10; // 行数
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListView = (ListView) findViewById(R.id.mListView);
mTitleLayout = (LinearLayout) findViewById(R.id.titleLayout);
// 首先填充头部的标题布局
for (int i = 0; i < COLUMN_NUM; i++) {
View titleView = LayoutInflater.from(this).inflate(R.layout.item_text,null);
TextView title = (TextView) titleView.findViewById(R.id.itemTitleTv);
title.setText("标题"+i);
// 直接往LinearLayout中填充子元素,设置该子元素的宽高和宽所占的比重
mTitleLayout.addView(titleView,new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT, 1.0f));
}
// 准备数据,这里仅仅准备票面的座位号,相当于准备票
for (int i = 1; i <= COLUMN_NUM; i++) {
for (int j = 1; j <= ROW_NUM; j++){
idList.add(i+"-"+j);
}
}
// 下面把刚刚准备好的一批票发放到每个人的手中
for (int i = 0; i < idList.size(); i++) {
bean = new ItemBean();
bean.setId(idList.get(i)); // 给这位同学发了一张票了
bean.setPictureName("同学" + i);
bean.setPictureResId(R.mipmap.ic_launcher); // 这位同学的长相
dataList.add(bean); // 添加到数据集合
}
// 设置适配器
mAdapter = new MyListAdapter(this, dataList, COLUMN_NUM );
mListView.setAdapter(mAdapter);
}
}
代码注释也很清楚了,我就不再啰嗦了,有什么不明白的地方大家可以跟我留言,反正我也不会解答


<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="5dp">
<TextView
android:id="@+id/itemTitleTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="18sp"
android:text="--标题--" />
</LinearLayout>
这个就是标题的长相,也就是上图中标红的部分R.layout.item_text的写法。下面看下适配器Adapter是怎么写的,
public class MyListAdapter extends BaseAdapter {
private Context mContext ;
private List<ItemBean> mDataLists ;
private int mColumnNum = 0 ;
public MyListAdapter() {
}
/**
* 构造函数
* @param mContext
* @param mDataLists
*/
public MyListAdapter(Context mContext, List<ItemBean> mDataLists , int columnNum ) {
this.mContext = mContext;
this.mDataLists = mDataLists;
this.mColumnNum = columnNum ;
}
@Override
public int getCount() {
return mDataLists.size();
}
@Override
public ItemBean getItem(int i) {
return mDataLists.get(i);
}
@Override
public long getItemId(int i) {
return i;
}
@Override
public View getView(int position, View view, ViewGroup parent) {
view = LayoutInflater.from(mContext).inflate(R.layout.list_row_layout,null);
LinearLayout row = (LinearLayout) view.findViewById(R.id.listViewRow); // 行布局
if(row != null) row.removeAllViews(); // 清空行
for (int i = 0; i < mColumnNum ; i++) {
View itemView = LayoutInflater.from(mContext).inflate(R.layout.item,null);
TextView itemNameTv = (TextView) itemView.findViewById(R.id.itemNameTv);
TextView itemLocationTv = (TextView) itemView.findViewById(R.id.itemLocationTv);
ItemBean itemBean = getLocationBean(position+1 , i+1); // 获取对应的实体类
if(itemBean != null){
itemNameTv.setText(itemBean.getPictureName());
itemLocationTv.setText(itemBean.getId());
// 将每个元素添加到行布局中去
row.addView(itemView, new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT, 1.0f));
}
}
return view; // 返回加载好了内容的行布局
}
/***
* 根据座位号获取对应的元素
* @param row
* @param column
* @return
*/
private ItemBean getLocationBean(int row , int column){
String index = column+"-"+row; // 生成索引,对比索引找到对应的学生
for (int i = 0; i < this.getCount(); i++) {
if(this.getItem(i).getId().equals(index))
return this.getItem(i) ;
}
return null ;
}
}
下面摘出来的一段代码就是定位用的,在电影院里,就是凭借着票面上的xx排xx列找到自己的座位的,这里也是一样的原理
private ItemBean getLocationBean(int row , int column){
String index = column+"-"+row; // 生成索引,对比索引找到对应的学生
for (int i = 0; i < this.getCount(); i++) {
if(this.getItem(i).getId().equals(index))
return this.getItem(i) ;
}
return null ;
}
这个Adapter中也有两个布局XML文件,主要是不想手写,其实纯粹可以手写,我习惯用LayoutInflater加载XML布局来实现的,这两个布局XML也贴出来给大家。
R.layout.list_row_layout: 这个是每行的布局,作为父容器来使用
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/listViewRow" android:orientation="horizontal" android:layout_width="match_parent" android:layout_height="match_parent"/>
R.layout.item: 就是每个位子上的布局
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:gravity="center" android:padding="16dp" android:layout_height="match_parent"> <ImageView android:layout_width="60dp" android:layout_height="60dp" app:srcCompat="@mipmap/ic_launcher" android:layout_centerInParent="true" android:id="@+id/itemImageView" /> <TextView android:text="名称" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/itemImageView" android:layout_centerHorizontal="true" android:layout_marginTop="10dp" android:id="@+id/itemNameTv" /> <TextView android:text="坐标" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/itemNameTv" android:layout_alignLeft="@+id/itemNameTv" android:layout_alignStart="@+id/itemNameTv" android:layout_centerHorizontal="true" android:layout_marginTop="6dp" android:id="@+id/itemLocationTv" /> </RelativeLayout>
文末的福利放送:你也想制作文中的GIF图吗,推荐你一款软件LICEcap,这是地址:http://www.cockos.com/licecap/
源代码地址(下载不要分,拿走不谢):
Git仓库:https://github.com/1989Jiangtao/HorizontalScrollviewDemo
优快云:http://download.youkuaiyun.com/detail/u010898329/9751237