原创StickHeaderListView的简单实现,解决footerView问题
Posted HalfmanHuang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了原创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的情况,清空Head 111 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 @Override 188 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 // 添加head 196 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 @Override 11 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 @Override 44 public int getCount() { 45 return 30; 46 } 47 @Override 48 public Object getItem(int position) { 49 return position; 50 } 51 @Override 52 public long getItemId(int position) { 53 return position; 54 } 55 @Override 56 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 @Override 69 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 @Override 78 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-
以上是关于原创StickHeaderListView的简单实现,解决footerView问题的主要内容,如果未能解决你的问题,请参考以下文章