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. 关于显示gif的一些方法与讨论

    http://www.2cto.com/kf/201404/292468.html http://www.eoeandroid.com/forum.php?mod=viewthread&tid ...

  2. 【Unity Shaders】学习笔记——SurfaceShader(五)让纹理动起来

    [Unity Shaders]学习笔记——SurfaceShader(五)让纹理动起来 转载请注明出处:http://www.cnblogs.com/-867259206/p/5611222.html ...

  3. the error code is 2203

    权限不够 切换管理员再操作

  4. Acronis Server备份Linux系统

    Acronis Server备份Linux系统 前段时间用Acronis Disk Director Suite解决了Thinkpad笔记本在win7的分区问题(http://chenguang.bl ...

  5. [前端 1] 使用frameset框架构建网页基本布局

    导读:在做项目的过程中,发现网页的一些地方是不变的,比如说顶部.底部.而变幻的内容就只是一部分.这个时候在想,这是每次都刷新一个界面呢,还是有别的快捷方法呢.然后就找到了frameset 这个东西.本 ...

  6. iptable nat网关

    echo "1" > /proc/sys/net/ipv4/ip_forward iptables -t nat -A POSTROUTING -s 192.168.1.0/ ...

  7. WWF3自定义活动<第八篇>

    WWF提供了对原有活动进行扩展以及自定义新活动的功能,用户可以通过"Workflow Activity Library"创建和开发自定义活动. 一.自定义活动类型 默认情况下,创建 ...

  8. js种的循环语句

    //js种的循环语句 //while与do while的区别是while是满足条件后才执行 //do while是不管满不满足条件都会执行一次 //for 循环与while,do while相比循环结 ...

  9. 在centos上安装mysql5.7的三种方法

    带OS信息的是已编译的二进制文件,不带OS信息的是源码包 mysql-5.7.14-linux-glibc2.5-x86_64.tar.gz 二进制包 mysql-5.5.51.tar.gz 源码包 ...

  10. makefile 中定义宏位置需要注意一下

    CUR_DIR = $(shell pwd) CFLAGS = -g -Wall GCC = gcc GXX = g++ TARGET = exe.out SRC_FILES += $(shell f ...