【原创】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本身的属性发生变化, ...
随机推荐
- 剑指Offer:面试题12——打印1到最大的n位数(java实现)
问题描述: 输入数字n,按顺序打印出从1到最大的n位十进制数,比如输入3,则打印出1,2,3一直到最大的3位数即999. 思路1:最简单的想法就是先找出最大的n位数,然后循环打印即可. public ...
- vc 取windows系统信息 版本 cpu信息 内存信息 ie版本信息 office版本
头文件: /*! Copyright (C) *---------------------------------------------------------------------------- ...
- C程序之修改Windows的控制台颜色(转载)
Windows的CMD可以和Linux下的终端一样可以有五颜六色,目前我在网上找到2种方法可以修改Windows的CMD,当然都是在代码中修改的.在“CMD”->“属性”->“颜色”,这种 ...
- 发几个Flex的学习资源
书籍: 目前在看两本 <Essential.ActionScript.3.0> <Flex 4 In Action> 还有两本当手册翻阅,非常喜欢Cookbook这种题材的书, ...
- KVM虚拟化(一)—— 介绍与简单使用
一.架构及介绍 KVM(Kernel-based Virtual Machine)它由 Quramnet 开发,该公司于 2008年被 Red Hat 收购: 自Linux 2.6.20后整合到内核, ...
- 【Python】django安装
官方下载:https://www.djangoproject.com/download/ 报错 [root@test Django-]# python setup.py install Traceba ...
- Use EnCase to acquire data from a smartphone
Yesterday someone asked me a question can EnCase acquire data from a smartphone, and my reply was &q ...
- hdu1203
#include <stdio.h> #include <math.h> #define mmin(x,y) (x)<(y)?(x):(y) int main(){ +] ...
- 新手浅谈C#关于abstract和interface
abstract:抽象类,是对某一类对象的抽象,继承抽象类的类和抽象类是属于关系 interface:接口,可以说是一种规范,继承接口的类表达则是,我继承了这个接口,我能做什么 举个常用的例子:抽象一 ...
- MyEclipse中使用debug调试程序
最基本的操作是: 1.首先在一个java文件中设断点,然后debug as-->open debug Dialog,然后在对话框中选类后--> Run 当程序走到断 ...