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问题的更多相关文章

  1. 《zw版·Halcon-delphi系列原创教程》简单的令人发指,只有10行代码的车牌识别脚本

    <zw版·Halcon-delphi系列原创教程>简单的令人发指,只有10行代码的车牌识别脚本 简单的令人发指,只有10行代码的车牌识别脚本      人脸识别.车牌识别是opencv当中 ...

  2. 最简单的解决Chrome浏览器主页被hao123、360和2345篡改的方法是什么

    最简单的解决Chrome浏览器主页被hao123.360和2345篡改的方法是什么 一.总结 一句话总结:打开chrome的安装目录,将chrome.exe改成chrome1.exe即可,然后发送一个 ...

  3. 奔五的人学ios:swift竟然没有字符串包括,找个简单的解决方法

    swift关于字符串的推断中 有前导.有后缀 两个方法.竟然没有包括推断. 经过学习找了个简单的解决方法: extension String { func has(v:String)->Bool ...

  4. 红帽企业版RHEL7.1在研域工控板上,开机没有登陆窗口 -- 编写xorg.conf 简单三行解决Ubuntu分辩率不可调的问题

    红帽企业版RHEL7.1在研域工控板上,开机没有登陆窗口 没有登陆窗口 的原因分析: 没有登陆窗口的原因是因为有多个屏幕在工作,其中一个就是build-in 屏幕(内置的虚拟屏幕)和外接的显示器,并且 ...

  5. 五大Linux简单命令解决系统性能问题

    五大Linux简单命令解决系统性能问题 2010-12-17 10:07 James Turnbull TechTarget中国 字号:T | T 管理Linux主机的性能看起来经常象是在变魔术一样. ...

  6. [原创]MYSQL的简单入门

    MYSQL简单入门: 查询库名称:show databases; information_schema mysql test 2:创建库 create database 库名 DEFAULT CHAR ...

  7. 一个简单的解决方法:word文档打不开,错误提示mso.dll模块错误。

    最近电脑Word无故出现故障,无法打开,提示错误信息如下: 问题事件名称: APPCRASH应用程序名: WINWORD.EXE应用程序版本: 11.0.8328.0应用程序时间戳: 4c717ed1 ...

  8. iOS开发雕虫小技之傻瓜式定位神器-超简单方式解决iOS后台定时定位

    1.概述 由于公司一款产品的需求,最近一直在研究iOS设备的后台定位.主要的难点就是,当系统进入后台之后,程序会被挂起,届时定时器.以及代码都不会Run~ 所以一旦用户将我的App先换到了后台,我的定 ...

  9. 简单方法解决bootstrap3 modal异步加载只一次的问题

    用过bootstrap3自身的modal的remote属性的人可能都有相同的疑惑:就是点击弹出modal后再次点击会从缓存中加载内容,而不会再次走后台,解决办法就是只要让modal本身的属性发生变化, ...

随机推荐

  1. C/C++程序员面试易错题

    c部分::::::::::::::::::::::::::::::::::: . 关键字volatile有什么含意? 并给出三个不同的例 子. [参考答案]一个定义为volatile的变量是说这变量可 ...

  2. DB2命令大全

    1.1查看表空间 db2 list tablespaces show detail 1.2查看数据库的表死锁 方法一: 打开监控   db2 update monitor switches using ...

  3. vb 随机获取6个1-33的数

    Private Sub random(ByVal num As Integer, ByVal min As Integer, ByVal max As Integer) Dim i As Intege ...

  4. Entity Framework Lambda 实现多列Group by,并汇总求和

    var result = DataSummaryRepository.FindBy(x => x.UserID == argMemberNo && x.SummaryDate & ...

  5. VS2010之MFC串口通信的编写教程

    http://wenku.baidu.com/link?url=K1XPdj9Dcf2of_BsbIdbPeeZ452uJqiF-s773uQyMzV2cSaPRIq6RddQQH1zr1opqVBM ...

  6. SQLServer 窗口函数

    一.窗口函数的作用 窗口函数是对一组值进行操作,不需要使用GROUP BY 子句对数据进行分组,还能够在同一行中同时返回基础行的列和聚合列.窗口函数,基础列和聚合列的查询都非常简单. 二.语法格式 窗 ...

  7. WF4.0 Activities<第一篇>

    一.基元工具 1.Delay Delay用于延迟一段时间执行下面的流程.在WF中实例是单线程运行的,Delay并不是Thread.Sleep方法实现的. Delay有一个Duration属性,用于设置 ...

  8. 修改VS解决方案及工程名,解决如何打开高/版本VS项目

    对于VS2008等低版本与高版本VS之间的转换问题: 对照下面2个版本的不同点自由修改,切换到相应的版本文件(红字修改,灰色删除) ---------------------------------- ...

  9. Windows 2008修改密码策略方法

    Windows Server 2008默认强制要求定期更改密码,这个功能有时实在是让人烦不胜烦,适当情况下可以考虑关闭. 方法如下: 1.按windows键+R(或者点开始---动行)打开运行窗口,输 ...

  10. poj2503 哈希

    这题目主要是难在字符串处理这块. #include <stdio.h> #include <string.h> #include <stdlib.h> #defin ...