【原创】StickHeaderListView的简单实现,解决footerView问题
1、前言:
前几天用了GitHub上se.emilsjolander.stickylistheaders这个组件,然后发现这个组件的listview不能添加footerView,加了footer后,滑倒footer的时候head会消失,与我项目中的需求不符。
于是就自己写了一个StickHeaderListView,实现比较简单,做法和stickylistheaders基本相同只是没有封装那么多功能,可以添加footer但不能添加header,有需要的同学可以拿去改良后用。
另外由于是临时写的一个组件,代码没做什么优化,如果有想法可以提点意见,谢谢!
2、示例效果:

3、组件源码:
/**
* 带头部固定的列表
* Created by shengdong.huang on 2016/6/24.
*/
public class StickHeaderListView extends FrameLayout { /** 页面引用 */
protected Context context;
/** 列表视图 */
protected ListView listView;
/** 适配器 */
protected StickHeaderAdapter adapter;
/** 头部布局 */
protected FrameLayout headLayout;
/** 滚动监听器 */
protected OnScrollListener listener;
/** 提供Adapter响应的观察者 */
protected DataSetObserver mDataSetObserver = new DataSetObserver() {
@Override
public void onChanged() {
if (listView != null && headLayout != null && adapter != null) {
refreshHead(listView.getFirstVisiblePosition(), true);
}
} @Override
public void onInvalidated() {
}
}; /**
* 滚动监听器
*/
protected AbsListView.OnScrollListener scrollListener = new AbsListView.OnScrollListener() { @Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if (listener != null) {
listener.onScrollStateChanged(view, scrollState);
}
} @Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
if (listener != null) {
listener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
}
// 刷新
refreshHead(firstVisibleItem, false);
}
}; protected void refreshHead(int firstVisibleItem, boolean forceRefresh) {
// 防空
if (headLayout == null || adapter == null || adapter.getHeadPos() == null
|| listView.getChildAt(0) == null) {
return;
} // 获取头部位置记录
ArrayList<Integer> headPos = adapter.getHeadPos();
// 是否有找到head
boolean find = false; // 获取head中的位置
int prevHeadPos = -1;
if (headLayout.getChildCount() > 0 && headLayout.getChildAt(0) != null &&
headLayout.getChildAt(0).getTag() != null) {
prevHeadPos = (int) headLayout.getChildAt(0).getTag();
} // 反向遍历头部位置记录
for (int i = (headPos.size() - 1); i>=0; i--) {
// 如果当前位置大于等于某个头部记录,表示应该使用该头部
if (firstVisibleItem >= headPos.get(i)) {
// 获取headLayout中视图的pos标签 // 构造或者从headLayout中获取视图
View v;
if (prevHeadPos == -1 || prevHeadPos != headPos.get(i) || forceRefresh) {
// 无Pos标签或POS标签不配对
headLayout.removeAllViews();
v = listView.getAdapter().getView(headPos.get(i), null, null);
v.setTag(headPos.get(i));
LayoutParams params = new FrameLayout.LayoutParams(-1, -2);
v.setLayoutParams(params);
headLayout.addView(v);
} else if (i+1 < headPos.size() && firstVisibleItem == headPos.get(i+1) - 1) {
// 当前第一个item的top值
int top = listView.getChildAt(0).getTop();
// Pos标签配对但,有下一个head,且下一个head的pos为下一个item时
v = headLayout.getChildAt(0);
// 设置head的Top
LayoutParams params = (LayoutParams) v.getLayoutParams();
params.setMargins(0, top, 0, -top);
v.setLayoutParams(params);
} else {
// 修正head top没有回到0的问题
v = headLayout.getChildAt(0);
LayoutParams params = (LayoutParams) v.getLayoutParams();
if (params.topMargin != 0) {
params.setMargins(0, 0, 0, 0);
v.setLayoutParams(params);
}
}
find = true;
break;
}
}
// 未找到head的情况,清空Head
if (!find && headLayout != null) {
headLayout.removeAllViews();
}
} public StickHeaderListView(Context context) {
super(context);
this.context = context;
} public StickHeaderListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
} public void setBackgroundColor(int color) {
if (listView != null) {
listView.setBackgroundColor(color);
}
} public void addFooterView(View v) {
if (listView != null) {
listView.addFooterView(v);
}
} public boolean removeFooterView(View v) {
if (listView != null) {
return listView.removeFooterView(v);
}
return false;
} public int getFooterViewsCount() {
if (listView != null) {
return listView.getFooterViewsCount();
}
return 0;
} public void setDividerHeight(int height) {
if (listView != null) {
listView.setDividerHeight(height);
}
} public void setCacheColorHint(int color) {
if (listView != null) {
listView.setCacheColorHint(color);
}
} public void setSelector(int resID) {
if (listView != null) {
listView.setSelector(resID);
}
} public void setAdapter(StickHeaderAdapter adapter) {
if (adapter instanceof BaseAdapter && listView != null) {
this.adapter = adapter;
if (adapter != null && mDataSetObserver != null) {
try {
((BaseAdapter) adapter).unregisterDataSetObserver(mDataSetObserver);
} catch (Exception e) {}
}
listView.setAdapter((ListAdapter) this.adapter);
((BaseAdapter) adapter).registerDataSetObserver(mDataSetObserver);
}
} public void setOnScrollListener(OnScrollListener listener) {
this.listener = listener;
} @Override
protected void onFinishInflate() {
super.onFinishInflate();
// 添加列表,属性直接使用父视图
listView = new ListView(context);
listView.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
addView(listView);
// 添加head
headLayout = new FrameLayout(context);
headLayout.setLayoutParams(new FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
addView(headLayout);
// 添加滚动监听
listView.setOnScrollListener(scrollListener);
} public interface OnScrollListener extends AbsListView.OnScrollListener {} public interface StickHeaderAdapter {
public ArrayList<Integer> getHeadPos();
}
}
4、示例代码:
/**
* 主页面
* Created by shengdong.huang on 2016/6/20.
*/
public class MainActivity extends FragmentActivity { private StickHeaderListView listView;
private TestAdapter adapter; @Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
listView = (StickHeaderListView) findViewById(R.id.list); View footer = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);
footer.setBackgroundColor(Color.GREEN);
listView.addFooterView(footer); View footer2 = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);
footer2.setBackgroundColor(Color.BLUE);
listView.addFooterView(footer2); View footer3 = LayoutInflater.from(this).inflate(R.layout.item_head_foot, null);
footer3.setBackgroundColor(Color.RED);
listView.addFooterView(footer3); listView = (StickHeaderListView) findViewById(R.id.list); adapter = new TestAdapter();
listView.setAdapter(adapter);
} public class TestAdapter extends BaseAdapter implements StickHeaderListView.StickHeaderAdapter { private ArrayList<Integer> headpos = new ArrayList<>(); public TestAdapter() {
headpos.add(0);
headpos.add(5);
headpos.add(15);
}
@Override
public int getCount() {
return 30;
}
@Override
public Object getItem(int position) {
return position;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
ViewHolder holder;
if (convertView == null) {
convertView = LayoutInflater.from(MainActivity.this).inflate(R.layout.item_list, null);
holder = new ViewHolder();
holder.text = (TextView) convertView.findViewById(R.id.text);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.text.setText("AAAAAAAAAAAAAAAAAAAAA"+position);
holder.text.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(MainActivity.this, "pos:"+position, Toast.LENGTH_SHORT).show();
}
});
convertView.setBackgroundColor(0x110011 * position % 0xffffff + 0xff000000);
return convertView;
} @Override
public ArrayList<Integer> getHeadPos() {
return headpos;
}
} private class ViewHolder {
TextView text;
}
}
5、使用方法:
1、布局中加入StickHeaderListView
2、Adapter实现StickHeaderAdapter接口
3、getHeadPos()中返回headitem的位置,标示listview item中各个head的起点(这点与stickylistheaders不同)
4、如果有需要用到一些listview的方法,请自行在StickHeaderListView中添加,但注意,不能addHeaderView,会导致滑动异常
-END-
【原创】StickHeaderListView的简单实现,解决footerView问题的更多相关文章
- 《zw版·Halcon-delphi系列原创教程》简单的令人发指,只有10行代码的车牌识别脚本
<zw版·Halcon-delphi系列原创教程>简单的令人发指,只有10行代码的车牌识别脚本 简单的令人发指,只有10行代码的车牌识别脚本 人脸识别.车牌识别是opencv当中 ...
- 最简单的解决Chrome浏览器主页被hao123、360和2345篡改的方法是什么
最简单的解决Chrome浏览器主页被hao123.360和2345篡改的方法是什么 一.总结 一句话总结:打开chrome的安装目录,将chrome.exe改成chrome1.exe即可,然后发送一个 ...
- 奔五的人学ios:swift竟然没有字符串包括,找个简单的解决方法
swift关于字符串的推断中 有前导.有后缀 两个方法.竟然没有包括推断. 经过学习找了个简单的解决方法: extension String { func has(v:String)->Bool ...
- 红帽企业版RHEL7.1在研域工控板上,开机没有登陆窗口 -- 编写xorg.conf 简单三行解决Ubuntu分辩率不可调的问题
红帽企业版RHEL7.1在研域工控板上,开机没有登陆窗口 没有登陆窗口 的原因分析: 没有登陆窗口的原因是因为有多个屏幕在工作,其中一个就是build-in 屏幕(内置的虚拟屏幕)和外接的显示器,并且 ...
- 五大Linux简单命令解决系统性能问题
五大Linux简单命令解决系统性能问题 2010-12-17 10:07 James Turnbull TechTarget中国 字号:T | T 管理Linux主机的性能看起来经常象是在变魔术一样. ...
- [原创]MYSQL的简单入门
MYSQL简单入门: 查询库名称:show databases; information_schema mysql test 2:创建库 create database 库名 DEFAULT CHAR ...
- 一个简单的解决方法:word文档打不开,错误提示mso.dll模块错误。
最近电脑Word无故出现故障,无法打开,提示错误信息如下: 问题事件名称: APPCRASH应用程序名: WINWORD.EXE应用程序版本: 11.0.8328.0应用程序时间戳: 4c717ed1 ...
- iOS开发雕虫小技之傻瓜式定位神器-超简单方式解决iOS后台定时定位
1.概述 由于公司一款产品的需求,最近一直在研究iOS设备的后台定位.主要的难点就是,当系统进入后台之后,程序会被挂起,届时定时器.以及代码都不会Run~ 所以一旦用户将我的App先换到了后台,我的定 ...
- 简单方法解决bootstrap3 modal异步加载只一次的问题
用过bootstrap3自身的modal的remote属性的人可能都有相同的疑惑:就是点击弹出modal后再次点击会从缓存中加载内容,而不会再次走后台,解决办法就是只要让modal本身的属性发生变化, ...
随机推荐
- Linux系统自启动脚本
只需编辑/etc/init.d/rc.local文件,在最后加上你的脚本即可.比如:我已经编写了一个脚本shell.sh,存放在/home/mars704/Desktop/ 下面在终端输入 gedit ...
- xml规范及xml解析
http://www.cnblogs.com/wang-meng/p/5374498.html 1,XML基础介绍 xml的概念: XML 指可扩展标记语言(EXtensible Markup Lan ...
- 搭建基于Windows + Apache + PHP + MySQL的Moodle平台
说起Moodle,我相信学习教育技术的同学应该都不陌生,在大学的网络与远程教育课中,我有幸与传说中的Moodle相遇,然后相知,但是我绝对不会跟你们说一直到我毕业,我都没有找到一个合适的时间与这位Ms ...
- tornado框架之路一
Web 服务器 每个页面都以 HTML 的形式传送到你的浏览器中,HTML 是一种浏览器用来描述页面内容和结构的语言.那些负责发送 HTML 到浏览器的应用称之为“Web 服务器”,会让你迷惑的是,这 ...
- org.springframework.core.NestedIOException
今天遇到的一个小异常,报错信息如下: 2013-11-13 10:00:32 org.apache.catalina.core.StandardContext listenerStart严重: Exc ...
- 来自Google产品管理和营销高级副总裁Jonathan Rosenberg的42条军规(转)
#35 营造一个说 yes 的文化 你想要建立一个充满正能量和积极思考的地方.“组织里有阻碍变革发生的抗体.所以许多大公司无法创新.如果你是一个创新者,你就变成了病毒,所有的抗体都想杀死你.”在这种情 ...
- c# 了解委托
public delegate void aHandler(string text); public class A1 { public void Print(string txt) { Consol ...
- vs2010 编译curl-7.42.1
curl是一个sftp ssl 等功能工具. dos 进入到curl-7.42.1\lib下来:逐步执行以下操作 call "C:/Program Files/Microsoft Visua ...
- 二十、ValueStack的常用方法
二十.ValueStack的常用方法 void set(String key,Object value):先获取根栈栈顶的Map,如果不存在,压入一个新的Map public String execu ...
- javaSE第八天
第八天 43 1. 如何制作帮助文档(了解) 43 2. 通过JDK提供的API学习了Math类(掌握) 44 (1)API(Application Programming Inte ...