带表头 固定列可上下上下滑动的可分页加载的ListView
带表头 固定列可左右上下滑动的可分页加载的ListView
项目描述:
最近做项目,遇到一个问题,就是要求ListView既要有表头,点击表头从能够对特定的列进行排序,并且要求固定第一列,右边的其他列不固定,能够向左滑动的时候,收缩到左侧,向右滑动的时候,显示已经收缩的列,总之就是满足一个需求,一个表里面有很多个列的时候,单个屏幕显示不完所有的列,因此,就做一个可收缩的列表,用于在一个控件里面显示完表中所有的字段。
小的我参考了网上的代码,名字为demoHListView(大家可以到网上搜索下载,或者在我共享的代码中去下载),是用观察者模式来实现的效果,但是我对这个示例做了改造,因为它并不可以支持分页加载以及点击列头按当前列数据排序,还是直接把我的代码贴出来。
注意:
1、ListView是用的网上封装好的支持上下拉分页加载的控件(PullToRefreshListView)。这里边儿要注意把封装控件中的ListView对象声明为成员变量,然后通过get()方法返回出来。
改造部分的代码如下:
package com.goodwin.finance.activity.fragment; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Timer; import java.util.TimerTask; import android.annotation.SuppressLint; import android.content.Intent; import android.os.Bundle; import android.text.format.DateUtils; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.HorizontalScrollView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; import com.goodwin.finance.R; import com.goodwin.finance.activity.BaseSlidingFragmentActivity; import com.goodwin.finance.activity.SearchActivity; import com.goodwin.finance.activity.TimeShareActivity; import com.goodwin.finance.bean.HsStockInfo; import com.goodwin.finance.bean.StockInfo; import com.goodwin.finance.common.constant.Constants; import com.goodwin.finance.common.view.HScrollView; import com.goodwin.finance.common.view.SlidingMenu; import com.goodwin.finance.comparators.HsStockInfoComparator; import com.goodwin.finance.database.BaseAppDbHelper; import com.goodwin.finance.net.socket.response.DataArchitectureBean; import com.goodwin.finance.net.socket.response.ICSortFastDataBean; import com.goodwin.finance.net.socket.response.ResponseICSortFastData; import com.goodwin.finance.pulltorefresh.PullToRefreshBase; import com.goodwin.finance.pulltorefresh.PullToRefreshBase.Mode; import com.goodwin.finance.pulltorefresh.PullToRefreshBase.OnRefreshListener2; import com.goodwin.finance.pulltorefresh.PullToRefreshListView; import com.goodwin.finance.widget.MarketShenAdapter; /** * @author Administrator */ public class MarketShenFragment extends BaseFragment implements OnClickListener, OnRefreshListener2<ListView> { private static final String TAG = "MarketShenFragment"; BaseAppDbHelper<StockInfo> dbHelper = new BaseAppDbHelper<StockInfo>(); // 回退按钮 private ImageView ivImgBack; // 界面标题 private TextView tvTitle; // 搜索按钮 private ImageView ivSearch; // 股票列表控件 private PullToRefreshListView ptrlHsList; // 适配器 private MarketShenAdapter adapter; // 股票列表数据对象 private List<HsStockInfo> stockInfoList; // 缓存股票列表数据 private List<HsStockInfo> cacheStockInfoList; // 头部布局 private LinearLayout llHead; // 水平托动隐藏标题控件 private HScrollView hScrollView; // 水平标题箭头 private ImageView ivZhArrow; // 根据默认进入为降序,第一次点击变升序,再点变降序;排序在后台计算,前端不做计算,页面刷新时更换; private LinearLayout llTitleZh; // 降序或升序的标志 private int sortType = Constants.sort_type.SORT_DESC; // 意图 private Intent intent; // 接收数据框架数据 private DataArchitectureBean dataArchitectureBean; // 报价列表数据 private ResponseICSortFastData responseIcSortFastData; // 排行股票详细数据 private List<ICSortFastDataBean> icSortFastDataBeanList; //第几页 private int offset; //页大小 private int count = 20; // 报价列表记时器 private Timer priceListTimer; // 记时器任务 private TimerTask priceListTimerTask; //请求第一页 private int requestFirst = 1; //是否是刷新 private boolean refreshState = true; /** * 加载界面布局 */ @Override protected int getViewId() { return R.layout.market_shen; } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); put("intoHushenTime", 0); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { View view = super.onCreateView(inflater, container, savedInstanceState); try { if (view != null) { findViews(view); initCtrl(); setListener(); } } catch (Exception e) { Log.i(TAG, "MarketShenFragment的onCreateView()方法异常!", e); e.printStackTrace(); } return view; } /** * 初始化控件 */ private void findViews(View view) { try { ivImgBack = (ImageView) view.findViewById(R.id.iv_img_back); tvTitle = (TextView) view.findViewById(R.id.tv_title); ivSearch = (ImageView) view.findViewById(R.id.iv_search); hScrollView = (HScrollView) view.findViewById(R.id.hsl_view); llTitleZh = (LinearLayout) view.findViewById(R.id.ll_title_zh); ivZhArrow = (ImageView) view.findViewById(R.id.iv_zh_arrow); ptrlHsList = (PullToRefreshListView) view.findViewById(R.id.ptrl_hs_list); llHead = (LinearLayout) view.findViewById(R.id.ll_head); } catch (Exception e) { Log.i(TAG, "MarketShenFragment的findViews()方法异常!", e); e.printStackTrace(); } } /** * 初始化分时图记时器 */ public void initTimer() { priceListTimer = new Timer(true); priceListTimerTask = new PriceListTimerTask(); if (null != priceListTimer) { priceListTimer.schedule(priceListTimerTask, 6 * 1000); } } /** * 报价列表计时器任务 */ public class PriceListTimerTask extends TimerTask { @Override public void run() { refreshState = true; requestAStockList(offset, count); priceListTimerTask.cancel(); } } /** * 取消计时器 */ private void removeTimerTask() { if (null != priceListTimer && null != priceListTimerTask) { priceListTimerTask.cancel(); } } @Override public void onResume() { super.onResume(); int time = Integer.parseInt(get("intoHushenTime").toString()); if (time != 0) { Log.i(TAG, "================再次开启沪深A股报价列表刷新计时器================="); initTimer(); } } @Override public void onPause() { super.onPause(); Log.i(TAG, "================结束计时器================="); removeTimerTask(); } @Override public void onDestroy() { super.onDestroy(); Log.i(TAG, "================离开界面结束计时器================="); put("intoHushenTime", 0); removeTimerTask(); } /** * 注册监听事件 */ private void setListener() { try { llHead.setOnTouchListener(new ListViewAndHeadViewTouchLinstener()); if (null != ptrlHsList.getLv()) { ptrlHsList.getLv().setOnTouchListener(new ListViewAndHeadViewTouchLinstener()); } ptrlHsList.setOnRefreshListener(this); ptrlHsList.setMode(Mode.BOTH); llTitleZh.setOnClickListener(this); ptrlHsList.setOnItemClickListener(onItemClickListener); ivSearch.setOnClickListener(this); ivImgBack.setOnClickListener(this); } catch (Exception e) { Log.i(TAG, "MarketShenFragment的setListener()方法异常!", e); e.printStackTrace(); } } /** * 接收到数据之后更新UI界面 */ @Override public void onUpdateUI(int taskId, Object data) { super.onUpdateUI(taskId, data); try { if (null != data) { switch (taskId) { case Constants.Hq.RECEIVE_PRICE_LIST_INFO: bindPriceList(data); break; } } } catch (Exception e) { Log.i(TAG, "MarketShenFragment的onUpdateUI()方法异常!", e); e.printStackTrace(); } } /** * 1、绑定沪深A股报价列表数据 */ @SuppressWarnings("unchecked") private void bindPriceList(Object data) { if (data instanceof ResponseICSortFastData) { dismissNetProgressBar(); responseIcSortFastData = (ResponseICSortFastData) data; icSortFastDataBeanList = responseIcSortFastData.getIcSortFastDataBeanList(); if (null != icSortFastDataBeanList && icSortFastDataBeanList.size() > 0) { //1、遍历获取到的报价列表 //2、根据报价列表的股票代码,去数据框架中查询股票名称 //3、拼装成沪深A股报价新列表对象 HashMap<String, String> codeNameMap = (HashMap<String, String>) get("codeNameMap"); ArrayList<HsStockInfo> stockInfoList = new ArrayList<HsStockInfo>(); for (ICSortFastDataBean icSortFastDataBean : icSortFastDataBeanList) { if (null != icSortFastDataBean) { String stockCode = icSortFastDataBean.getPtrStockLabel();//股票代码 int marketType = icSortFastDataBean.getMarketType();//市场类型 String stockName = codeNameMap.get(marketType + stockCode);//股票名称 double hsNew = icSortFastDataBean.getPrice();//最新 double hsZh = icSortFastDataBean.getDeltaPercent() * 100;//涨愊 double hsZd = icSortFastDataBean.getDelta(); //涨跌 double hsZs = icSortFastDataBean.getPreClose();//昨收 double hsCjl = icSortFastDataBean.getTotalVol();//成交量 总手数 double hsCje = icSortFastDataBean.getAmount();//成交额 double hsZg = icSortFastDataBean.getHigh();//最高 double hsZuidi = icSortFastDataBean.getLow();//最低 double hsJk = icSortFastDataBean.getOpen();//今开 double hsLb = icSortFastDataBean.getVolumeRate();//量比 double hsZhenhu = icSortFastDataBean.getSwing() * 100;//振幅 HsStockInfo stockInfo = new HsStockInfo(stockName, stockCode, marketType, hsNew, hsZh, hsZd, hsZs, hsCjl, hsCje, hsZg, hsZuidi, hsJk, hsLb, hsZhenhu); stockInfoList.add(stockInfo); } } if (null != stockInfoList && stockInfoList.size() > 0) { //如果是首次请求数据,则初始化适配器,然后填充数据,并且将股票数据填充进缓存列表 if (requestFirst == 1) { if (null != cacheStockInfoList) { cacheStockInfoList.clear(); } cacheStockInfoList = new ArrayList<HsStockInfo>(); cacheStockInfoList.addAll(stockInfoList); stockInfoList = (ArrayList<HsStockInfo>) HsStockInfoComparator.sortList(stockInfoList, sortType); adapter.setStockInfoList(stockInfoList); ptrlHsList.onRefreshComplete(); Log.i(TAG, "第一次请求==========" + stockInfoList.size() + "条"); Log.i(TAG, "第一次请求缓存==========" + cacheStockInfoList.size() + "条"); } else { if (!refreshState) { cacheStockInfoList.addAll(stockInfoList); cacheStockInfoList = HsStockInfoComparator.sortList(cacheStockInfoList, sortType); adapter.setStockInfoList(cacheStockInfoList); Log.i(TAG, "分页请求==========" + stockInfoList.size() + "条"); Log.i(TAG, "分页请求缓存==========" + cacheStockInfoList.size() + "条"); } ptrlHsList.onRefreshComplete(); } } else { tips("亲!已经是最后一页了!"); ptrlHsList.onRefreshComplete(); } //开启沪深A股报价列表刷新计时器 Log.i(TAG, "================开启沪深A股报价列表刷新计时器================="); initTimer(); } } } /** * 初始化控件数据 */ private void initCtrl() { try { dataArchitectureBean = (DataArchitectureBean) getGoodWinApplication().getGlobalData().get("dataArchitectureBean"); ivZhArrow.setImageResource(R.drawable.stock_arrow_down); tvTitle.setText("沪深"); llHead.setFocusable(true); llHead.setClickable(true); if (Integer.parseInt(get("intoHushenTime").toString()) == 0) { showNetProgressBar(); // 查询沪深A股报价列表 this.offset = 0; requestAStockList(offset, count); } adapter = new MarketShenAdapter(getActivity()); adapter.setHeadSrcrollView(hScrollView); ptrlHsList.setAdapter(adapter); } catch (Exception e) { Log.i(TAG, "MarketShenFragment的initCtrl()方法异常!", e); e.printStackTrace(); } } /** * 1、请求沪深A股报价列表 */ private void requestAStockList(int offset, int count) { try { if (null != dataArchitectureBean) { requestHqService.sendMessageToServer(getHQIoSession(), requestHqService.requestFeiZiXuanPriceList(dataArchitectureBean, offset, count, Constants.HqMarketType.HSZS,sortType)); } } catch (Exception e) { Log.i(TAG, "MarketShenFragment的requestAStockList()方法异常!", e); e.printStackTrace(); } } /** * ListView 水平托动监听事件 * * @author Administrator */ @SuppressLint("ClickableViewAccessibility") class ListViewAndHeadViewTouchLinstener implements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent arg1) { // 当在列头 和 listView控件上touch时,将这个touch的事件分发给 ScrollView HorizontalScrollView headSrcrollView = (HorizontalScrollView) llHead.findViewById(R.id.hsl_view); headSrcrollView.onTouchEvent(arg1); return false; } } @SuppressLint("ClickableViewAccessibility") class ViewOnTouchListener implements View.OnTouchListener { @Override public boolean onTouch(View v, MotionEvent event) { return false; } } private OnItemClickListener onItemClickListener = new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> adapterView, View view, int position, long id) { //结束记时器 Log.i(TAG, "================结束沪深A股计时器================="); put("intoHushenTime", 1); removeTimerTask(); adapter.setSelectItemPosition(position - 1); adapter.notifyDataSetInvalidated(); //结束计时器 removeTimerTask(); // 跳转到沪深A股行情分时图界面 HsStockInfo hsStockInfo = adapter.getStockInfoList().get(position - 1); //保存或更新到数据库中 StockInfo stockInfo = new StockInfo(); stockInfo.setStockCode(hsStockInfo.getStockCode()); stockInfo.setStockName(hsStockInfo.getStockName()); stockInfo.setMarketId(hsStockInfo.getMarketType() + ""); long viewTime = System.currentTimeMillis(); stockInfo.setViewTime(viewTime); HashMap<String, Object> where = new HashMap<String, Object>(); where.put("stock_code", stockInfo.getStockCode()); where.put("market_id", stockInfo.getMarketId()); dbHelper.createOrExistsUpdate(stockInfo, where, "view_time", viewTime); intent = new Intent(getActivity(), TimeShareActivity.class); intent.putExtra("hsStockInfo", hsStockInfo); getActivity().startActivity(intent); } }; @Override public void onClick(View v) { try { switch (v.getId()) { case R.id.iv_img_back: BaseSlidingFragmentActivity baseFragment = (BaseSlidingFragmentActivity) getActivity(); SlidingMenu mSlidingMenu = baseFragment.getSlideMenu(); mSlidingMenu.showMenu(); break; case R.id.iv_search: // 搜索按钮 startActivity(new Intent(getActivity(), SearchActivity.class)); break; case R.id.ll_title_zh: if(sortType == Constants.sort_type.SORT_DESC){ sortType = Constants.sort_type.SORT_ASC; ivZhArrow.setImageResource(R.drawable.stock_arrow_up); if(null != cacheStockInfoList){ cacheStockInfoList = HsStockInfoComparator.sortList(cacheStockInfoList, sortType); adapter.setStockInfoList(cacheStockInfoList); } requestAStockList(offset, count); }else{ sortType = Constants.sort_type.SORT_DESC; ivZhArrow.setImageResource(R.drawable.stock_arrow_down); if(null != cacheStockInfoList){ cacheStockInfoList = HsStockInfoComparator.sortList(cacheStockInfoList, sortType); adapter.setStockInfoList(cacheStockInfoList); } requestAStockList(offset, count); } break; } } catch (Exception e) { Log.i(TAG, "MarketShenFragment的onClick()方法异常!", e); } } @Override public void onPullDownToRefresh(PullToRefreshBase<ListView> refreshView) { try { String label = DateUtils.formatDateTime(this.getActivity(), System.currentTimeMillis(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL); refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label); requestFirst = 1; this.offset = 0; requestAStockList(offset, count); } catch (Exception e) { e.printStackTrace(); Log.i(TAG, "MarketShenFragment的onPullDownToRefresh()方法异常!", e); } } @Override public void onPullUpToRefresh(PullToRefreshBase<ListView> refreshView) { try { String label = DateUtils.formatDateTime(this.getActivity(), System.currentTimeMillis(), DateUtils.FORMAT_SHOW_TIME | DateUtils.FORMAT_SHOW_DATE | DateUtils.FORMAT_ABBREV_ALL); refreshView.getLoadingLayoutProxy().setLastUpdatedLabel(label); refreshState = false; requestFirst = 2; this.offset = offset + count; requestAStockList(offset, count); } catch (Exception e) { e.printStackTrace(); Log.i(TAG, "MarketShenFragment的onPullUpToRefresh()方法异常!", e); } }; }
页面布局部分的代码:
注意布局这部分的代码很关键,要把示例中的最外层控件移除才可以保证点击列头的时候,每一页可以点击,不会抢占列头里层控件的焦点,这也才能保证点击列头排序功能的实现。
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_head" style="@style/s_page_bg" > <!-- 沪深A股列表界面 --> <!-- 头部 --> <LinearLayout style="@style/s_head_out" android:baselineAligned="false" > <LinearLayout style="@style/s_head_left" > <ImageView android:id="@+id/iv_img_back" style="@style/s_head_left_bt" android:contentDescription="@string/imagedescription" /> </LinearLayout> <LinearLayout style="@style/s_head_center" > <TextView android:id="@+id/tv_title" style="@style/s_page_head_title" android:text="@string/s_free_title" /> </LinearLayout> <LinearLayout style="@style/s_head_right" > <ImageView android:id="@+id/iv_search" style="@style/s_head_right_bt" android:contentDescription="@string/imagedescription" /> </LinearLayout> </LinearLayout> <!-- 可水平托动的标题头部 --> <LinearLayout android:layout_width="fill_parent" android:layout_height="36dip" android:gravity="center_vertical" android:orientation="horizontal" android:background="@drawable/border"> <!-- 沪深A股 --> <RadioButton android:id="@+id/rb_stock_type" style="@style/s_horizontal_list_head_common_first" android:text="@string/s_hs_astock" /> <com.goodwin.finance.common.view.HScrollView android:id="@+id/hsl_view" android:layout_width="fill_parent" android:layout_height="fill_parent" android:scrollbars="none" > <LinearLayout android:layout_width="wrap_content" android:layout_height="fill_parent" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:gravity="center_vertical" android:orientation="horizontal" > <!-- 最新 --> <RadioButton android:id="@+id/rb_new" style="@style/s_horizontal_list_head_common" android:text="@string/s_hs_new" /> <LinearLayout android:id="@+id/ll_title_zh" android:layout_width="@dimen/st_hq_layout_width" android:layout_height="fill_parent" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:clickable="true" android:focusable="true" android:gravity="center" android:orientation="horizontal" > <!-- 涨幅 --> <TextView android:id="@+id/tv_zh" style="@style/s_horizontal_list_head_common_arrow" android:text="@string/s_hs_zh" /> <ImageView android:id="@+id/iv_zh_arrow" android:layout_width="20dip" android:layout_height="17dip" android:layout_marginLeft="3dip" android:contentDescription="@string/imagedescription" /> </LinearLayout> <LinearLayout android:layout_width="@dimen/st_hq_layout_width" android:layout_height="wrap_content" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:gravity="center" android:orientation="horizontal" > <!-- 涨跌 --> <RadioButton style="@style/s_horizontal_list_head_common_arrow" android:text="@string/s_hs_zd" /> </LinearLayout> <!-- 昨收 --> <RadioButton style="@style/s_horizontal_list_head_common" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:text="@string/s_hs_zs" /> <!-- 成交量 --> <RadioButton style="@style/s_horizontal_list_head_common" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:text="@string/s_hs_cjl" /> <!-- 成交额 --> <RadioButton style="@style/s_horizontal_list_head_common" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:text="@string/s_hs_cje" /> <!-- 最高 --> <RadioButton style="@style/s_horizontal_list_head_common" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:text="@string/s_hs_zg" /> <!-- 最低 --> <RadioButton style="@style/s_horizontal_list_head_common" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:text="@string/s_hs_zuidi" /> <!-- 今开 --> <RadioButton style="@style/s_horizontal_list_head_common" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:text="@string/s_hs_jk" /> <!-- 量比 --> <RadioButton style="@style/s_horizontal_list_head_common" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:text="@string/s_hs_lb" /> <!-- 振幅 --> <RadioButton style="@style/s_horizontal_list_head_common" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" android:text="@string/s_hs_zhenhu" /> <!-- 解决最后一个无法显示 --> <RadioButton style="@style/s_horizontal_list_head_common_last" android:layout_marginLeft="@dimen/st_hq_layout_marginLeft" /> </LinearLayout> </com.goodwin.finance.common.view.HScrollView> </LinearLayout> <com.goodwin.finance.pulltorefresh.PullToRefreshListView android:id="@+id/ptrl_hs_list" style="@style/s_list_view_common" /> </LinearLayout>