HorizontalScrollView实现可左右移动表格
一、ScrollView,HorizontalScrollView的简单介绍
Android当中比较常用的两个布局容器:ScrollView和HorizontalScrollView,从字面意义上来看也是非常的简单的,ScrollView就是一个可以滚动的View,这个滚动的方向是垂直方向的,而HorizontalScrollView则是一个水平方向的可以滚动的View。
二、实现效果

三、实现代码讲解
1. 首先讲解一下布局的实现,看实现的gif动画:
第一列的内容是不动的,之后几列的内容可以左右移动。所以第一个列是一个TextView,之后的几列使用MySheetHorizontalScrollView包裹的多个TextView。
第一行也是不动的,下面的几行可以上下滑动,用一个ListView来实现这个效果。
整体布局sheet_scroll_view_layout.xml
<?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">
<include
layout="@layout/sheet_scroll_view_item"
/>
<ListView
android:id="@+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent">
</ListView>
</LinearLayout>
单行具体布局sheet_scroll_view_item.xml
<?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="50dp"
android:orientation="horizontal">
<TextView
android:id="@+id/name_tv"
android:layout_width="100dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="姓名" />
<com.example.chenpeng.julyapplication.CustomView.SheetScrollView.MySheetHorizontalScrollView
android:id="@+id/sheet_scroll_view"
android:layout_width="wrap_content"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/team_tv"
android:layout_width="100dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="效力球队" />
<TextView
android:id="@+id/wz_tv"
android:layout_width="100dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="位置" />
<TextView
android:id="@+id/nl_tv"
android:layout_width="100dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="年龄" />
<TextView
android:id="@+id/nx_tv"
android:layout_width="100dp"
android:layout_height="match_parent"
android:gravity="center"
android:text="年薪" />
</LinearLayout>
</com.example.chenpeng.julyapplication.CustomView.SheetScrollView.MySheetHorizontalScrollView>
</LinearLayout>
2. MySheetHorizontalScrollView分析
MySheetHorizontalScrollView继承HorizontalScrollView,具有水平移动的功能。MySheetHorizontalScrollView类中创建内部类SheetScrollViewObserver和接口OnSheetScrollChangedListener。
SheetScrollViewObserver主要用于存储OnSheetScrollChangedListener对象;当滑动时,一起调用OnSheetScrollChangedListener中onSheetScrollChanged方法,所有存储的对象一起滑动。
/**
* 主要用来存每个MySheetHorizontalScrollView的onScrollChanged的监听事件
* 最后notifyAllSheetScrollChangedListener一起移动
*/
public class MySheetHorizontalScrollView extends HorizontalScrollView {
private SheetScrollViewObserver mSheetScrollViewObserver;
public MySheetHorizontalScrollView(Context context) {
this(context, null);
}
public MySheetHorizontalScrollView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MySheetHorizontalScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mSheetScrollViewObserver = new SheetScrollViewObserver();
}
/**
* l:滑动之后的x的坐标。
* t:滑动之后的y的坐标。
* oldl:滑动之前的x坐标。
* oldt:滑动之前的y坐标。
*/
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt) {
if (mSheetScrollViewObserver != null && (l != oldl || t != oldt)) {
mSheetScrollViewObserver.notifyAllSheetScrollChangedListener(l, t, oldl, oldt);
}
super.onScrollChanged(l, t, oldl, oldt);
}
public void addSheetScrollViewListener(OnSheetScrollChangedListener listener) {
mSheetScrollViewObserver.addSheetScrollChangedListener(listener);
}
public void removeSheetScrollViewListener(OnSheetScrollChangedListener listener) {
mSheetScrollViewObserver.removeSheetScrollChangedListener(listener);
}
/***
* OnSheetScrollChangedListener接口
*/
public interface OnSheetScrollChangedListener {
void onSheetScrollChanged(int l, int t, int oldl, int oldt);
}
/**
* 主要用于存储OnSheetScrollChangedListener,
* 当滑动时,一起调用OnSheetScrollChangedListener中onSheetScrollChanged方法
*/
public static class SheetScrollViewObserver {
List<OnSheetScrollChangedListener> mListenerList;
public SheetScrollViewObserver() {
mListenerList = new ArrayList<>();
}
public void addSheetScrollChangedListener(OnSheetScrollChangedListener listener) {
mListenerList.add(listener);
}
public void removeSheetScrollChangedListener(OnSheetScrollChangedListener listener) {
mListenerList.remove(listener);
}
public void notifyAllSheetScrollChangedListener(int l, int t, int oldl, int oldt) {
if (mListenerList == null || mListenerList.size() <= 0) {
return;
} else {
for (int i = 0; i < mListenerList.size(); i++) {
OnSheetScrollChangedListener listener = mListenerList.get(i);
if (listener != null)
listener.onSheetScrollChanged(l, t, oldl, oldt);
}
}
}
}
}
3. MySheetAdapter
这是布局文件sheet_scroll_view_layout.xml中ListView的适配器。主要看getView中的代码:
mHeaderSheetScrollView.addSheetScrollViewListener(new MySheetScrollViewListenerImp(holder.sheetScrollView));
标题栏中的mHeaderSheetScrollView会利用MySheetScrollViewListenerImp这个类包裹listView的item中的sheetScrollView。MySheetScrollViewListenerImp实现了MySheetHorizontalScrollView.OnSheetScrollChangedListener接口。通过addSheetScrollViewListener方法,这样mHeaderSheetScrollView会关联到所有listView中的SheetScrollView。
holder.sheetScrollView.setOnTouchListener(new ListViewAttachHeadViewTouchListener());
给每个listView中的item中的sheetScrollView设置触摸监听,看一下ListViewAttachHeadViewTouchListener类发现每个sheetScrollView的触摸监听交给mHeaderSheetScrollView处理。
这样所有的sheetScrollView都交给mHeaderSheetScrollView处理,当sheetScrollView中的一个开始水平移动时,mHeaderSheetScrollView会进行处理,会调用到MySheetHorizontalScrollView.onScrollChanged(),然后会调用SheetScrollViewObserver.notifyAllSheetScrollChangedListener(),这样每个sheetScrollView就会都调用MySheetScrollViewListenerImp中的onSheetScrollChanged方法。最后大家一起水平移动。
package com.example.chenpeng.julyapplication.CustomView.SheetScrollView;
import android.content.Context;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.List;
import java.util.Map;
/**
* Created by chenpeng on 2018/9/6.
*/
public class MySheetAdapter extends BaseAdapter {
private MySheetHorizontalScrollView mHeaderSheetScrollView;
private List<? extends Map<String, ?>> mDatas;
private int[] mViewIds;
private Context mContext;
private int mLayoutId;
private String[] mForm;
private int mSheetScrollViewId;
public MySheetAdapter(Context context, List<? extends Map<String, ?>> datas, int layoutId, int[] viewIds, String[] form, int scrollViewId, MySheetHorizontalScrollView headerScrollView) {
mContext = context;
mDatas = datas;
mLayoutId = layoutId;
mViewIds = viewIds;
mForm = form;
mSheetScrollViewId = scrollViewId;
mHeaderSheetScrollView = headerScrollView;
}
@Override
public int getCount() {
return mDatas.size();
}
@Override
public Object getItem(int position) {
return mDatas.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(mLayoutId, null);
holder.sheetScrollView = convertView.findViewById(mSheetScrollViewId);
//mHeaderSheetScrollView带动ListView下面的scrollView移动
mHeaderSheetScrollView.addSheetScrollViewListener(new MySheetScrollViewListenerImp(holder.sheetScrollView));
//ListView下面scrollView的onTouch交给mHeaderSheetScrollView处理
holder.sheetScrollView.setOnTouchListener(new ListViewAttachHeadViewTouchListener());
int size = mForm.length;
holder.textViews = new TextView[mForm.length];
for (int i = 0; i < size; i++) {
holder.textViews[i] = convertView.findViewById(mViewIds[i]);
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
//填充数据
for (int i = 0; i < mForm.length; i++) {
holder.textViews[i].setText((String) mDatas.get(position).get(mForm[i]));
}
return convertView;
}
class ViewHolder {
TextView[] textViews;
MySheetHorizontalScrollView sheetScrollView;
}
/**
* MySheetHorizontalScrollView的对象包裹进来,
* 实现接口OnSheetScrollChangedListener中onSheetScrollChanged的方法
*/
class MySheetScrollViewListenerImp implements MySheetHorizontalScrollView.OnSheetScrollChangedListener {
MySheetHorizontalScrollView mScrollView;
public MySheetScrollViewListenerImp(MySheetHorizontalScrollView scrollView) {
mScrollView = scrollView;
}
@Override
public void onSheetScrollChanged(int l, int t, int oldl, int oldt) {
mScrollView.smoothScrollTo(l, t);
}
}
/**
* 将ListView中scrollView的OuTouch事件交给mHeaderSheetScrollView
*/
public class ListViewAttachHeadViewTouchListener implements View.OnTouchListener {
@Override
public boolean onTouch(View v, MotionEvent event) {
mHeaderSheetScrollView.onTouchEvent(event);
return false;
}
}
}
4. MySheetScrollViewActivity
Activity如何使用MySheetHorizontalScrollView和MySheetAdapter:
package com.example.chenpeng.julyapplication.CustomView.SheetScrollView;
public class MySheetScrollViewActivity extends Activity {
private MySheetHorizontalScrollView mSheetHorizontalScrollView;
private ListView mListView;
private MySheetAdapter mSheetAdapter;
private List<Map<String, String>> datas = new ArrayList<>();
private String[] form = {"name", "team", "wx", "nl", "nx"};
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.sheet_scroll_view_layout);
mListView = findViewById(R.id.listView);
mSheetHorizontalScrollView = findViewById(R.id.sheet_scroll_view);
for (int i = 0; i < 30; i++) {
Map<String, String> map = new HashMap<>();
map.put(form[0], "james_" + i);
map.put(form[1], "骑士" + i);
map.put(form[2], "SF");
map.put(form[3], "32");
map.put(form[4], "3000 W");
datas.add(map);
}
mSheetAdapter = new MySheetAdapter(MySheetScrollViewActivity.this, datas, R.layout.sheet_scroll_view_item,
new int[]{R.id.name_tv, R.id.team_tv, R.id.wz_tv, R.id.nl_tv, R.id.nx_tv}, form,
R.id.sheet_scroll_view, mSheetHorizontalScrollView);
mListView.setAdapter(mSheetAdapter);
}
}