藉助HorizontalScrollView實現多列ListView的橫向滑動(含動態加載)

在Android中使用ListVIew顯示多列數據表時會遇到屏幕寬度不夠, 無法完全顯示的問題, 以下的例子是我結合網上的各種方案總結的一種較好的解.


自定義Adapter: 

public class HolderAdapter extends BaseAdapter {
	private static final String TAG = "HolderAdapter";

	/**
	 * List中的數據
	 */
	private List<Data> currentData;
	/**
	 * ListView頭部
	 */
	private RelativeLayout mHead;
	/**
	 * layout ID 
	 */
	private int id_row_layout;
	private LayoutInflater mInflater;

	int[] colors = { Color.rgb(102, 102, 51), Color.rgb(153, 153, 51) };
	// int[] colors = { Color.BLACK, Color.BLACK };

	public HolderAdapter(Context context, int id_row_layout,
			List<Data> currentData, RelativeLayout mHead) {
		Log.v(TAG + ".HolderAdapter", " 初始化");
		
		this.id_row_layout = id_row_layout;
		this.mInflater = LayoutInflater.from(context);
		this.currentData = currentData;
		this.mHead = mHead;

	}

	public int getCount() {
		// TODO Auto-generated method stub
		return this.currentData.size();
	}

	public Object getItem(int position) {
		// TODO Auto-generated method stub
		return null;
	}

	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 * 向List中添加數據
	 * 
	 * @param items
	 */
	public void addItem(List<Data> items) {
		for (Data item : items) {
			currentData.add(item);
		}
	}

	/**
	 * 清空當List中的數據
	 */
	public void cleanAll() {
		this.currentData.clear();
	}

	public View getView(int position, View convertView, ViewGroup parentView) {
		ViewHolder holder = null;
		if (convertView == null) {
			convertView = mInflater.inflate(id_row_layout, null);
			holder = new ViewHolder();

			MyHScrollView scrollView1 = (MyHScrollView) convertView
					.findViewById(R.id.horizontalScrollView1);

			holder.scrollView = scrollView1;
			holder.txt1 = (TextView) convertView.findViewById(R.id.textView1);
			holder.txt2 = (TextView) convertView.findViewById(R.id.textView2);
			holder.txt3 = (TextView) convertView.findViewById(R.id.textView3);
			holder.txt4 = (TextView) convertView.findViewById(R.id.textView4);
			holder.txt5 = (TextView) convertView.findViewById(R.id.textView5);

			MyHScrollView headSrcrollView = (MyHScrollView) mHead
					.findViewById(R.id.horizontalScrollView1);
			headSrcrollView
					.AddOnScrollChangedListener(new OnScrollChangedListenerImp(
							scrollView1));

			convertView.setTag(holder);
			// 隔行變色
			convertView.setBackgroundColor(colors[position % 2]);
			// mHolderList.add(holder);
		} else {
			// 隔行變色
			convertView.setBackgroundColor(colors[position % 2]);
			holder = (ViewHolder) convertView.getTag();
		}
		holder.txt1.setText(currentData.get(position).getStr1() + 1 + "列");
		holder.txt2.setText(currentData.get(position).getStr1() + 2 + "列");
		holder.txt3.setText(currentData.get(position).getStr1() + 3 + "列");
		holder.txt4.setText(currentData.get(position).getStr1() + 4 + "列");
		holder.txt5.setText(currentData.get(position).getStr1() + 5 + "列");

		return convertView;
	}

	class OnScrollChangedListenerImp implements OnScrollChangedListener {
		MyHScrollView mScrollViewArg;

		public OnScrollChangedListenerImp(MyHScrollView scrollViewar) {
			mScrollViewArg = scrollViewar;
		}

		public void onScrollChanged(int l, int t, int oldl, int oldt) {
			mScrollViewArg.smoothScrollTo(l, t);
		}
	};

	class ViewHolder {
		TextView txt1;
		TextView txt2;
		TextView txt3;
		TextView txt4;
		TextView txt5;
		HorizontalScrollView scrollView;
	}

}


繼承LinearLayout寫一個容器攔截TouchEvent

public class InterceptScrollContainer extends LinearLayout {
	private static final String TAG = "InterceptScrollContainer";

	public InterceptScrollContainer(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public InterceptScrollContainer(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}

	/* (non-Javadoc)
	 * @see android.view.ViewGroup#onInterceptTouchEvent(android.view.MotionEvent)
	 * 攔截TouchEvent
	 */
	@Override
	public boolean onInterceptTouchEvent(MotionEvent ev) {
		// TODO Auto-generated method stub
		Log.i(TAG,"onInterceptTouchEvent");
		return true;
		//return super.onInterceptTouchEvent(ev);
	}
}

public interface LoadStateInterface {
	/* 加載完成 */
	public void onLoadComplete(List<Data> remotDate);
}

/*
 * 自定義的 滾動控件
 * 重載了 onScrollChanged(滾動條變化),監聽每次的變化通知給 觀察(此變化的)觀察者
 * 可使用 AddOnScrollChangedListener 來訂閱本控件的 滾動條變化
 * */
public class MyHScrollView extends HorizontalScrollView {
	ScrollViewObserver mScrollViewObserver = new ScrollViewObserver();

	public MyHScrollView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		// TODO Auto-generated constructor stub
	}

	public MyHScrollView(Context context, AttributeSet attrs) {
		super(context, attrs);
		// TODO Auto-generated constructor stub
	}

	public MyHScrollView(Context context) {
		super(context);
		// TODO Auto-generated constructor stub
	}
	
	@Override
	public boolean onTouchEvent(MotionEvent ev) {
		return super.onTouchEvent(ev);
		//return false;
	}

	@Override
	protected void onScrollChanged(int l, int t, int oldl, int oldt) {
		/*
		 * 當滾動條移動後,引發 滾動事件。通知給觀察者,觀察者會傳達給其他的。
		 */
		if (mScrollViewObserver != null) {
			mScrollViewObserver.NotifyOnScrollChanged(l, t, oldl, oldt);
		}
		super.onScrollChanged(l, t, oldl, oldt);
	}

	/*
	 * 訂閱 本控件 的 滾動條變化事件
	 * */
	public void AddOnScrollChangedListener(OnScrollChangedListener listener) {
		mScrollViewObserver.AddOnScrollChangedListener(listener);
	}

	/*
	 * 取消 訂閱 本控件 的 滾動條變化事件
	 * */
	public void RemoveOnScrollChangedListener(OnScrollChangedListener listener) {
		mScrollViewObserver.RemoveOnScrollChangedListener(listener);
	}

	/*
	 * 當發生了滾動事件時
	 */
	public static interface OnScrollChangedListener {
		public void onScrollChanged(int l, int t, int oldl, int oldt);
	}

	/*
	 * 觀察者
	 */
	public static class ScrollViewObserver {
		List<OnScrollChangedListener> mList;

		public ScrollViewObserver() {
			super();
			mList = new ArrayList<OnScrollChangedListener>();
		}

		public void AddOnScrollChangedListener(OnScrollChangedListener listener) {
			mList.add(listener);
		}

		public void RemoveOnScrollChangedListener(
				OnScrollChangedListener listener) {
			mList.remove(listener);
		}

		public void NotifyOnScrollChanged(int l, int t, int oldl, int oldt) {
			if (mList == null || mList.size() == 0) {
				return;
			}
			for (int i = 0; i < mList.size(); i++) {
				if (mList.get(i) != null) {
					mList.get(i).onScrollChanged(l, t, oldl, oldt);
				}
			}
		}
	}
}

public class MainActivity extends Activity implements OnScrollListener {
	private static final String TAG = "InfoActivity";
	ListView mListView1;
	RelativeLayout mHead;
	LinearLayout main;
	HolderAdapter holderAdapter;
	private int last_item_position;// 最後item的位置
	private boolean isLoading = false;// 是否加載過,控制加載次數
	private int currentPage = 1;// 當前頁,默認爲1
	private int pageSize = 20;// 每頁顯示十條信息
	private View loadingView;// 加載視圖的佈局

	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		mHead = (RelativeLayout) findViewById(R.id.head);
		mHead.setFocusable(true);
		mHead.setClickable(true);
		mHead.setBackgroundColor(Color.BLACK);
		mHead.setOnTouchListener(new ListViewAndHeadViewTouchLinstener());

		// 加載視圖佈局
		loadingView = LayoutInflater.from(this).inflate(
				R.layout.list_page_load, null);

		mListView1 = (ListView) findViewById(R.id.listView1);
		mListView1.setOnTouchListener(new ListViewAndHeadViewTouchLinstener());
		mListView1.setCacheColorHint(0);
		// 添加底部加載視圖
		mListView1.addFooterView(loadingView);
		// 滾動條監聽
		mListView1.setOnScrollListener(this);

		// 創建當前用於顯示視圖的數據
		List<Data> currentData = RemoteDataUtil.createUpdateData(currentPage,
				pageSize);
		currentPage = currentPage + 1;

		/*
		 * List<Data> datas = new ArrayList<Data>();
		 * 
		 * for (int i = 0; i < 10; i++) { Data data = new Data(); data.setStr1(i
		 * + "行"); data.setStr2(i + ""); data.setStr3(i + ""); data.setStr4(i +
		 * ""); data.setStr5(i + ""); data.setStr6(i + ""); data.setStr7(i +
		 * ""); data.setStr8(i + ""); datas.add(data); }
		 */
		holderAdapter = new HolderAdapter(this, R.layout.item, currentData,
				mHead);
		mListView1.setAdapter(holderAdapter);
		// OnClick監聽
		mListView1.setOnItemClickListener(new OnItemClickListener() {

			public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
					long arg3) {
				// TODO Auto-generated method stub
				Log.i("MainActivity ListView", "onItemClick Event");
				Toast.makeText(MainActivity.this, "點了第" + arg2 + "個",
						Toast.LENGTH_SHORT).show();
			}
		});

	}

	class ListViewAndHeadViewTouchLinstener implements View.OnTouchListener {

		public boolean onTouch(View arg0, MotionEvent arg1) {
			// 當在列頭 和 listView控件上touch時,將這個touch的事件分發給 ScrollView
			HorizontalScrollView headSrcrollView = (HorizontalScrollView) mHead
					.findViewById(R.id.horizontalScrollView1);
			HorizontalScrollView headSrcrollView2 = (HorizontalScrollView) mHead
					.findViewById(R.id.horizontalScrollView1);
			headSrcrollView.onTouchEvent(arg1);
			headSrcrollView2.onTouchEvent(arg1);
			return false;
		}
	}

	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {

		last_item_position = firstVisibleItem + visibleItemCount - 1;

		if (last_item_position == totalItemCount - 2) {
			// 這裏控制當焦點落在某一個位置時,開始加載.
			// 當前是在第9個位置開始加載,改爲totalItemCount-1
			// 則會在第10個位置開始加載

			/**
			 * Loading 標記當前視圖是否處於加載中,如果正在加載(isLoading=true)就不執行更新操作
			 * 加載完成後isLoading=false,在 loadingHandler 中改變狀態
			 */
			if (!isLoading) {

				// 開啓一個線程加載數據
				isLoading = true;
				RemoteDataUtil.setRemoteDataByPage(currentPage, pageSize,
						new LoadStateInterface() {
							public void onLoadComplete(List<Data> remotDate) {
								holderAdapter.addItem(remotDate);
								handler.sendEmptyMessage(0);
							}
						});
			}
			;
		}
		;

		// 當ListView沒有FooterView時,添加FooterView(---loadingView---)
		if (mListView1.getFooterViewsCount() == 0) {
			handler.sendEmptyMessage(1);
		}
	}

	Handler handler = new Handler() {
		public void handleMessage(android.os.Message msg) {
			switch (msg.what) {
			case 0: {
				// 更新
				holderAdapter.notifyDataSetChanged();
				// 刪除FooterView
				mListView1.removeFooterView(loadingView);
				// 進入下一頁,此時視圖未加載.
				isLoading = false;
				// 當前頁自加
				currentPage = currentPage + 1;
				break;
			}
			case 1: {
				mListView1.addFooterView(loadingView);
				break;
			}
			default: {
				Log.w(TAG, "未知的Handler Message:" + msg.obj.toString());
			}
			}

		};
	};

	public void onScrollStateChanged(AbsListView view, int scrollState) {
		// TODO Auto-generated method stub

	}

	/**
	 * 監聽ListView的OnItemClick事件
	 * 
	 * @param arg0
	 * @param arg1
	 * @param arg2
	 * @param arg3
	 */
	public void onItemClick(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
		// TODO Auto-generated method stub
		Log.i("MainActivity ListView", "onItemClick Event");
		Toast.makeText(MainActivity.this, "點了第" + arg2 + "個",
				Toast.LENGTH_SHORT).show();
	}
}


ListView佈局
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:descendantFocusability="blocksDescendants"
    android:orientation="horizontal"
    android:padding="5dp" >

    <TextView
        android:id="@+id/textView1"
        android:layout_width="80dp"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_marginLeft="5dp"
        android:layout_marginRight="5dp"
        android:textColor="#FFFFFF"
        android:text="Column1"
        android:textAppearance="?android:attr/textAppearanceMedium" />

    <com.ytu.mush.hlistview.InterceptScrollContainer
        android:id="@+id/scroollContainter"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@id/textView1"
        android:focusable="false" >

        <com.ytu.mush.hlistview.MyHScrollView
            android:id="@+id/horizontalScrollView1"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:focusable="false"
            android:scrollbars="none" >

            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:focusable="false"
                android:orientation="horizontal" >

                <TextView
                    android:id="@+id/textView2"
                    android:layout_width="80dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:text="Column2"
                    android:textColor="#FFFFFF"
                    android:textAppearance="?android:attr/textAppearanceMedium" />

                <TextView
                    android:id="@+id/textView3"
                    android:layout_width="80dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:text="Column3"
                    android:textColor="#FFFFFF"
                    android:textAppearance="?android:attr/textAppearanceMedium" />

                <TextView
                    android:id="@+id/textView4"
                    android:layout_width="80dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:text="Column4"
                    android:textColor="#FFFFFF"
                    android:textAppearance="?android:attr/textAppearanceMedium" />

                <TextView
                    android:id="@+id/textView5"
                    android:layout_width="80dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:text="Column5"
                    android:textColor="#FFFFFF"
                    android:textAppearance="?android:attr/textAppearanceMedium" />

                <TextView
                    android:id="@+id/textView6"
                    android:layout_width="80dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:text="Column6"
                    android:textColor="#FFFFFF"
                    android:textAppearance="?android:attr/textAppearanceMedium" />

                <TextView
                    android:id="@+id/textView7"
                    android:layout_width="80dp"
                    android:layout_height="wrap_content"
                    android:layout_marginLeft="5dp"
                    android:layout_marginRight="5dp"
                    android:text="Column7"
                    android:textColor="#FFFFFF"
                    android:textAppearance="?android:attr/textAppearanceMedium" />
            </LinearLayout>
        </com.ytu.mush.hlistview.MyHScrollView>
    </com.ytu.mush.hlistview.InterceptScrollContainer>

</RelativeLayout>

說明: 真機測試的時候發現在2.2上只能在表頭橫向滑動

源碼下載



發佈了34 篇原創文章 · 獲贊 30 · 訪問量 14萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章