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. linux多线程

    #include <pthread.h> pthread_t pid; pthread_create(&pid, 0, pFunc, pArgs); //创建线程(linux下线程 ...

  2. 用C#编写游戏脚本

    大学宿舍玩游戏的时候,为了简化重复的键鼠动作,有学习过按键精灵和TC脚本开发工具,并做了一些小脚本,基本达到了当时的需求.不知不觉,已经毕业了3年了,无聊之余又玩起了游戏,对于一些无趣的重复行为,于是 ...

  3. im4java开发向导

    0.搜索ImageMagick下载安装 1.Setting up the Environment    引入im4java到classpath    设置图片处理引擎的command searchpa ...

  4. 利用Meida Service的Java SDK来调用Azure Media Services的Index V2实现视频字幕自动识别

    Azure Media Services新的Index V2 支持自动将视频文件中的语音自动识别成字幕文件WebVtt,非常方便的就可以跟Azure Media Player集成,将一个原来没字幕的视 ...

  5. SDUT 3347 数据结构实验之数组三:快速转置

    数据结构实验之数组三:快速转置 Time Limit: 1000MS Memory Limit: 65536KB Submit Statistic Problem Description 转置运算是一 ...

  6. EB(存储单位)

    abbr.艾字节,1EB=1024PB 计算机的存储单位 位 bit (比特)(Binary Digits):存放一位二进制数,即 0 或 1,最小的存储单位. 字节 byte:8个二进制位为一个字节 ...

  7. 解决在 使用 AjaxFileUploder 插件时,不能获取返回的 json 结果数据

    在MVC  项目 中使用 AjaxFileUploader 这个插件时,在上传图片或文件时,在控制器中返回的是 json数据,可是在 ie,或 googleChrome 浏览器中却出现 返回的json ...

  8. 洛谷P1530 分数化小数 Fractions to Decimals

    P1530 分数化小数 Fractions to Decimals 103通过 348提交 题目提供者该用户不存在 标签USACO 难度普及/提高- 提交  讨论  题解 最新讨论 暂时没有讨论 题目 ...

  9. Java基础——IO流

    今天刚刚看完java的io流操作,把主要的脉络看了一遍,不能保证以后使用时都能得心应手,但是最起码用到时知道有这么一个功能可以实现,下面对学习进行一下简单的总结: IO流主要用于硬板.内存.键盘等处理 ...

  10. ASP.NET MVC 开源项目学习之ProDinner (三)

    第四层:Resources   这一层里面主要是几个资源文件. 资源文件知识小杂烩: 几乎每一个生产性应用程序都需要使用资源.资源是在逻辑上由应用程序部署的任何非可执行数据.资源可以在应用程序中作为错 ...