PinnedHeaderListView源码详解

来源:互联网 发布:百度浏览器网络收藏夹 编辑:程序博客网 时间:2024/05/24 04:44

一、github项目地址与效果:

https://github.com/JimiSmith/PinnedHeaderListView

这里写图片描述

这里写图片描述

这里写图片描述

二、项目目录。

这里写图片描述

可以看到,目录只有四个java文件,其实非常简单,MainActivity文件和TestSectionedAdapter文件和我们以前写一般的listview的两个文件是大致一样的,重点在PinnedHeaderListView和SectionedBaseAdapter文件。

三、功能实现的思路。

(1)本项目中存在三种header:

——–>第一种是MainActivity中由
listView.addHeaderView(header1);
listView.addHeaderView(header2);
添加进去的header,他的添加只是为了显示效果,没有太大用处,这种header的效果如下
这里写图片描述
在MainActivity中添加了两次,而我的demo只添加了一次,所以添加了两次会有两个headerview在上面。

———->第二种是listview中和普通item本质一样的headerView,它与普通item的区别之在于布局不太一样,但实质上也是listview中的一个item。为了下面的陈述清楚,这里为这种headerView取名为listHeader。

———->第三种就是我们看到的悬浮的headerView,它是根据listHeader的位置,在适当时候,创建或者消失的一个view,从而产生悬浮的效果。取名为suspendHeader。注意,suspendHeader的布局外表和listHeader一模一样,为的就是产生listHeader脱离了listview进行悬浮的错觉,其实listHeader一直在listview中,而真正悬浮的是suspendHeader。

(2)功能实现的思路。

将ListView逻辑上分成若干个section,每个section有一个listHeader,当listHeader滑动到顶端时,会在ListView上绘制一个悬浮的suspendHeader,suspendHeader的布局和这个listHeader一样,当下面的listHeader2达到顶部与listHeader相交时,根据滑动距离将suspendHeader向上移,直到suspendHeader消失,listHeader2会悬浮在顶端,这样就实现了我们看到的效果。

四、具体源代码的分析

(1)MAinActivity:

PinnedHeaderListView listView =
(PinnedHeaderListView) findViewById(R.id.pinnedListView);
TestSectionedAdapter sectionedAdapter = new TestSectionedAdapter();
listView.setAdapter(sectionedAdapter);
明显的,用到了PinnedHeaderListView 和TestSectionedAdapter ;

(2)TestSectionedAdapter :

TestSectionedAdapter 继承于SectionedBaseAdapter,在TestSectionedAdapter 中不过是实现了SectionedBaseAdapter中的六个抽象方法:

public Object getItem(int section, int position)
public long getItemId(int section, int position)
public int getSectionCount()
public int getCountForSection(int section)
public View getItemView(int section, int position, View convertView, ViewGroup parent)
public View getSectionHeaderView(int section, View convertView, ViewGroup parent)

这六个方法通过名字就能知道他的功能,他们是为了留给用户个性定制listHeader布局和普通listItem布局的,而留给用户实现的方法。

(3)SectionedBaseAdapter:

SectionedBaseAdapter
extends BaseAdapter implements PinnedSectionedHeaderAdapter;

而PinnedSectionedHeaderAdapter是PinnedHeaderListView中定义的内部接口:
public static interface PinnedSectionedHeaderAdapter {
public boolean isSectionHeader(int position);

    public int getSectionForPosition(int position);    public View getSectionHeaderView(int section, View convertView, ViewGroup parent);    public int getSectionHeaderViewType(int section);    public int getCount();}

首先,由于继承于BaseAdapter,就必须实现一下几个方法:

public final int getCount()
public final Object getItem(int position)
public final int getItemViewType(int position)
public abstract long getItemId(int section, int position)
等,这样才能setAdapter到listview中;
实质上来说,这几个方法其实是封装了TestSectionedAdapter中实现的六个方法,为的是适配listview对baseAdapter的要求。

(4)PinnedHeaderListView:

extends ListView implements OnScrollListener
所以他就是个普普通通的listview而已,重点在他的onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount)方法中;
—->onScroll :
ListView滚动的时候,会不断的回调这个方法,然后在这个方法里实现suspendHeader显示逻辑的控制。

1、先对ListView添加的Header的情况进行处理,这是我们通过ListView的addHeaderView()添加的,文章开始的使用方法介绍中就是添加了两个Header。这种情况下,刚开始是不会有悬浮效果的,因为还没有进入section。

2、得到了mCurrentHeader,就是要悬浮显示的suspendHeader。
mCurrentHeader = getSectionHeaderView(section, mCurrentHeaderViewType != viewType ? null : mCurrentHeader);
追踪到getSectionHeaderView中看到
—>
View view = mAdapter.getSectionHeaderView(section, oldView, this);
—>
继续看SectionedBaseAdapter的getSectionHeaderView,发现是抽象方法,故看TestSectionedAdapter 中的实现
—>
LinearLayout layout = null;
LayoutInflater inflator = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
layout = (LinearLayout) inflator.inflate(R.layout.header_item, null);
这下子就清楚了,原来suspendHeader是一个布局与listHeader相同的view而已(listHeader用的也是R.layout.header_item)

3、ensurePinnedHeaderLayout(mCurrentHeader)保证mCurrentHeader可以悬浮在ListView顶部的固定位置。

4、剩下的代码就是用来控制suspendHeader移动的。因为当下方section的suspendHeader快要到达顶端时,会将之前悬浮的suspendHeader顶出显示区域,然后直到之前suspendHeader消失,新的suspendHeader就会悬浮在ListView顶端。这里的关键就是通过View的位置来计算之前悬浮suspendHeader的偏移量mHeaderOffset,然后通过invalidate触发dispatchDraw方法以重绘View。


五、补充

实现这种功能的开源项目还有StickyListHeaders等,StickyListHeaders思路大体相同,但更为复杂,且复用性更高,github地址为
https://github.com/emilsjolander/StickyListHeaders

谢谢大家!

2 0
原创粉丝点击