你的位置:首页 > 操作系统

[操作系统]【原创】StickHeaderListView的简单实现,解决footerView问题


1、前言:

 

前几天用了GitHub上se.emilsjolander.stickylistheaders这个组件,然后发现这个组件的listview不能添加footerView,加了footer后,滑倒footer的时候head会消失,与我项目中的需求不符。

于是就自己写了一个StickHeaderListView,实现比较简单,做法和stickylistheaders基本相同只是没有封装那么多功能,可以添加footer但不能添加header,有需要的同学可以拿去改良后用。

另外由于是临时写的一个组件,代码没做什么优化,如果有想法可以提点意见,谢谢!

 

2、示例效果:

 

3、组件源码:

 1 /** 2  * 带头部固定的列表 3  * Created by shengdong.huang on 2016/6/24. 4 */ 5 public class StickHeaderListView extends FrameLayout { 6  7   /** 页面引用 */ 8   protected Context context; 9   /** 列表视图 */ 10   protected ListView listView; 11   /** 适配器 */ 12   protected StickHeaderAdapter adapter; 13   /** 头部布局 */ 14   protected FrameLayout headLayout; 15   /** 滚动监听器 */ 16   protected OnScrollListener listener; 17   /** 提供Adapter响应的观察者 */ 18   protected DataSetObserver mDataSetObserver = new DataSetObserver() { 19     @Override 20     public void onChanged() { 21       if (listView != null && headLayout != null && adapter != null) { 22         refreshHead(listView.getFirstVisiblePosition(), true); 23       } 24     } 25  26     @Override 27     public void onInvalidated() { 28     } 29   }; 30  31   /** 32    * 滚动监听器 33   */ 34   protected AbsListView.OnScrollListener scrollListener = new AbsListView.OnScrollListener() { 35  36     @Override 37     public void onScrollStateChanged(AbsListView view, int scrollState) { 38       if (listener != null) { 39         listener.onScrollStateChanged(view, scrollState); 40       } 41     } 42  43     @Override 44     public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { 45       if (listener != null) { 46         listener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount); 47       } 48       // 刷新 49       refreshHead(firstVisibleItem, false); 50     } 51   }; 52  53   protected void refreshHead(int firstVisibleItem, boolean forceRefresh) { 54     // 防空 55     if (headLayout == null || adapter == null || adapter.getHeadPos() == null 56         || listView.getChildAt(0) == null) { 57       return; 58     } 59  60     // 获取头部位置记录 61     ArrayList<Integer> headPos = adapter.getHeadPos(); 62     // 是否有找到head 63     boolean find = false; 64  65     // 获取head中的位置 66     int prevHeadPos = -1; 67     if (headLayout.getChildCount() > 0 && headLayout.getChildAt(0) != null && 68         headLayout.getChildAt(0).getTag() != null) { 69       prevHeadPos = (int) headLayout.getChildAt(0).getTag(); 70     } 71  72     // 反向遍历头部位置记录 73     for (int i = (headPos.size() - 1); i>=0; i--) { 74       // 如果当前位置大于等于某个头部记录,表示应该使用该头部 75       if (firstVisibleItem >= headPos.get(i)) { 76         // 获取headLayout中视图的pos标签 77  78         // 构造或者从headLayout中获取视图 79         View v; 80         if (prevHeadPos == -1 || prevHeadPos != headPos.get(i) || forceRefresh) { 81           // 无Pos标签或POS标签不配对 82           headLayout.removeAllViews(); 83           v = listView.getAdapter().getView(headPos.get(i), null, null); 84           v.setTag(headPos.get(i)); 85           LayoutParams params = new FrameLayout.LayoutParams(-1, -2); 86           v.setLayoutParams(params); 87           headLayout.addView(v); 88         } else if (i+1 < headPos.size() && firstVisibleItem == headPos.get(i+1) - 1) { 89           // 当前第一个item的top值 90           int top = listView.getChildAt(0).getTop(); 91           // Pos标签配对但,有下一个head,且下一个head的pos为下一个item时 92           v = headLayout.getChildAt(0); 93           // 设置head的Top 94           LayoutParams params = (LayoutParams) v.getLayoutParams(); 95           params.setMargins(0, top, 0, -top); 96           v.setLayoutParams(params); 97         } else { 98           // 修正head top没有回到0的问题 99           v = headLayout.getChildAt(0);100           LayoutParams params = (LayoutParams) v.getLayoutParams();101           if (params.topMargin != 0) {102             params.setMargins(0, 0, 0, 0);103             v.setLayoutParams(params);104           }105         }106         find = true;107         break;108       }109     }110     // 未找到head的情况,清空Head111     if (!find && headLayout != null) {112       headLayout.removeAllViews();113     }114   }115 116   public StickHeaderListView(Context context) {117     super(context);118     this.context = context;119   }120 121   public StickHeaderListView(Context context, AttributeSet attrs) {122     super(context, attrs);123     this.context = context;124   }125 126   public void setBackgroundColor(int color) {127     if (listView != null) {128       listView.setBackgroundColor(color);129     }130   }131 132   public void addFooterView(View v) {133     if (listView != null) {134       listView.addFooterView(v);135     }136   }137 138   public boolean removeFooterView(View v) {139     if (listView != null) {140       return listView.removeFooterView(v);141     }142     return false;143   }144 145   public int getFooterViewsCount() {146     if (listView != null) {147       return listView.getFooterViewsCount();148     }149     return 0;150   }151 152   public void setDividerHeight(int height) {153     if (listView != null) {154       listView.setDividerHeight(height);155     }156   }157 158   public void setCacheColorHint(int color) {159     if (listView != null) {160       listView.setCacheColorHint(color);161     }162   }163 164   public void setSelector(int resID) {165     if (listView != null) {166       listView.setSelector(resID);167     }168   }169 170   public void setAdapter(StickHeaderAdapter adapter) {171     if (adapter instanceof BaseAdapter && listView != null) {172       this.adapter = adapter;173       if (adapter != null && mDataSetObserver != null) {174         try {175           ((BaseAdapter) adapter).unregisterDataSetObserver(mDataSetObserver);176         } catch (Exception e) {}177       }178       listView.setAdapter((ListAdapter) this.adapter);179       ((BaseAdapter) adapter).registerDataSetObserver(mDataSetObserver);180     }181   }182 183   public void setOnScrollListener(OnScrollListener listener) {184     this.listener = listener;185   }186 187   @Override188   protected void onFinishInflate() {189     super.onFinishInflate();190     // 添加列表,属性直接使用父视图191     listView = new ListView(context);192     listView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,193         ViewGroup.LayoutParams.MATCH_PARENT));194     addView(listView);195     // 添加head196     headLayout = new FrameLayout(context);197     headLayout.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,198         ViewGroup.LayoutParams.WRAP_CONTENT));199     addView(headLayout);200     // 添加滚动监听201     listView.setOnScrollListener(scrollListener);202   }203 204   public interface OnScrollListener extends AbsListView.OnScrollListener {}205 206   public interface StickHeaderAdapter {207     public ArrayList<Integer> getHeadPos();208   }209 }

 

4、示例代码:

 1 /** 2  * 主页面 3  * Created by shengdong.huang on 2016/6/20. 4 */ 5 public class MainActivity extends FragmentActivity { 6  7   private StickHeaderListView listView; 8   private TestAdapter adapter; 9 10   @Override11   protected void onCreate(@Nullable Bundle savedInstanceState) {12     super.onCreate(savedInstanceState);13     setContentView(R.layout.activity_main);14     listView = (StickHeaderListView) findViewById(R.id.list);15 16     View footer = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);17     footer.setBackgroundColor(Color.GREEN);18     listView.addFooterView(footer);19 20     View footer2 = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);21     footer2.setBackgroundColor(Color.BLUE);22     listView.addFooterView(footer2);23 24     View footer3 = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);25     footer3.setBackgroundColor(Color.RED);26     listView.addFooterView(footer3);27 28     listView = (StickHeaderListView) findViewById(R.id.list);29 30     adapter = new TestAdapter();31     listView.setAdapter(adapter);32   }33 34   public class TestAdapter extends BaseAdapter implements StickHeaderListView.StickHeaderAdapter {35 36     private ArrayList<Integer> headpos = new ArrayList<>();37 38     public TestAdapter() {39       headpos.add(0);40       headpos.add(5);41       headpos.add(15);42     }43     @Override44     public int getCount() {45       return 30;46     }47     @Override48     public Object getItem(int position) {49       return position;50     }51     @Override52     public long getItemId(int position) {53       return position;54     }55     @Override56     public View getView(final int position, View convertView, ViewGroup parent) {57       ViewHolder holder;58       if (convertView == null) {59         convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_list, null);60         holder = new ViewHolder();61         holder.text = (TextView) convertView.findViewById(R.id.text);62         convertView.setTag(holder);63       } else {64         holder = (ViewHolder) convertView.getTag();65       }66       holder.text.setText("AAAAAAAAAAAAAAAAAAAAA"+position);67       holder.text.setOnClickListener(new View.OnClickListener() {68         @Override69         public void onClick(View v) {70           Toast.makeText(MainActivity.this, "pos:"+position, Toast.LENGTH_SHORT).show();71         }72       });73       convertView.setBackgroundColor(0x110011 * position % 0xffffff + 0xff000000);74       return convertView;75     }76 77     @Override78     public ArrayList<Integer> getHeadPos() {79       return headpos;80     }81   }82 83   private class ViewHolder {84     TextView text;85   }86 }

 

5、使用方法:

1、布局中加入StickHeaderListView

2、Adapter实现StickHeaderAdapter接口

3、getHeadPos()中返回headitem的位置,标示listview item中各个head的起点(这点与stickylistheaders不同)

4、如果有需要用到一些listview的方法,请自行在StickHeaderListView中添加,但注意,不能addHeaderView,会导致滑动异常

 

-END-