项目中用到四个list集合展示一个页面,并且每个页面都会有一个标题栏.找了半天资料决定用pinnedheaderlistview开源项目.最后需求又来了,需要一个删除的功能,又去网上找资料,发现没有实现删除的demo,于是自己把pinnedheaderlistview源码分析了下,其实也就一个类.下面我贴上代码,希望后面的朋友少走弯路.
pinnedheaderlistview 实现滚动,重绘,添加回调函数
import android.content.context;
import android.graphics.canvas;
import android.util.attributeset;
import android.view.view;
import android.view.viewgroup;
import android.widget.*;
import android.widget.abslistview.onscrolllistener;
public class pinnedheaderlistview extends listview implements onscrolllistener{
private onscrolllistener monscrolllistener;
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();
}
private pinnedsectionedheaderadapter madapter;
private view mcurrentheader;
private int mcurrentheaderviewtype = 0;
private float mheaderoffset;
private boolean mshouldpin = true;
private int mcurrentsection = 0;
private int mwidthmode;
private int mheightmode;
public pinnedheaderlistview(context context) {
super(context);
super.setonscrolllistener(this);
public pinnedheaderlistview(context context, attributeset attrs) {
super(context, attrs);
public pinnedheaderlistview(context context, attributeset attrs, int defstyle) {
super(context, attrs, defstyle);
public void setpinheaders(boolean shouldpin) {
mshouldpin = shouldpin;
@override
public void setadapter(listadapter adapter) {
mcurrentheader = null;
madapter = (pinnedsectionedheaderadapter) adapter;
super.setadapter(adapter);
public void onscroll(abslistview view, int firstvisibleitem, int visibleitemcount, int totalitemcount) {
if (monscrolllistener != null) {
monscrolllistener.onscroll(view, firstvisibleitem, visibleitemcount, totalitemcount);
}
if (madapter == null || madapter.getcount() == 0 || !mshouldpin || (firstvisibleitem < getheaderviewscount())) {
mcurrentheader = null;
mheaderoffset = 0.0f;
for (int i = firstvisibleitem; i < firstvisibleitem + visibleitemcount; i++) {
view header = getchildat(i);
if (header != null) {
header.setvisibility(visible);
}
}
return;
firstvisibleitem -= getheaderviewscount();
int section = madapter.getsectionforposition(firstvisibleitem);
int viewtype = madapter.getsectionheaderviewtype(section);
mcurrentheader = getsectionheaderview(section, mcurrentheaderviewtype != viewtype ? null : mcurrentheader);
ensurepinnedheaderlayout(mcurrentheader);
mcurrentheaderviewtype = viewtype;
mheaderoffset = 0.0f;
for (int i = firstvisibleitem; i < firstvisibleitem + visibleitemcount; i++) {
if (madapter.issectionheader(i)) {
view header = getchildat(i - firstvisibleitem);
float headertop = header.gettop();
float pinnedheaderheight = mcurrentheader.getmeasuredheight();
header.setvisibility(visible);
if (pinnedheaderheight >= headertop && headertop > 0) {
mheaderoffset = headertop - header.getheight();
} else if (headertop <= 0) {
header.setvisibility(invisible);
invalidate();
public void onscrollstatechanged(abslistview view, int scrollstate) {
monscrolllistener.onscrollstatechanged(view, scrollstate);
private view getsectionheaderview(int section, view oldview) {
boolean shouldlayout = section != mcurrentsection || oldview == null;
view view = madapter.getsectionheaderview(section, oldview, this);
if (shouldlayout) {
// a new section, thus a new header. we should lay it out again
ensurepinnedheaderlayout(view);
mcurrentsection = section;
return view;
private void ensurepinnedheaderlayout(view header) {
if (header.islayoutrequested()) {
int widthspec = measurespec.makemeasurespec(getmeasuredwidth(), mwidthmode);
int heightspec;
viewgroup.layoutparams layoutparams = header.getlayoutparams();
if (layoutparams != null && layoutparams.height > 0) {
heightspec = measurespec.makemeasurespec(layoutparams.height, measurespec.exactly);
} else {
heightspec = measurespec.makemeasurespec(0, measurespec.unspecified);
header.measure(widthspec, heightspec);
header.layout(0, 0, header.getmeasuredwidth(), header.getmeasuredheight());
protected void dispatchdraw(canvas canvas) {
super.dispatchdraw(canvas);
if (madapter == null || !mshouldpin || mcurrentheader == null)
int savecount = canvas.save();
canvas.translate(0, mheaderoffset);
canvas.cliprect(0, 0, getwidth(), mcurrentheader.getmeasuredheight()); // needed
// for
// <
// honeycomb
mcurrentheader.draw(canvas);
canvas.restoretocount(savecount);
public void setonscrolllistener(onscrolllistener l) {
monscrolllistener = l;
protected void onmeasure(int widthmeasurespec, int heightmeasurespec) {
super.onmeasure(widthmeasurespec, heightmeasurespec);
mwidthmode = measurespec.getmode(widthmeasurespec);
mheightmode = measurespec.getmode(heightmeasurespec);
public void setonitemclicklistener(pinnedheaderlistview.onitemclicklistener listener) {
super.setonitemclicklistener(listener);
public static abstract class onitemclicklistener implements adapterview.onitemclicklistener {
@override
public void onitemclick(adapterview<?> adapterview, view view, int rawposition, long id) {
sectionedbaseadapter adapter;
if (adapterview.getadapter().getclass().equals(headerviewlistadapter.class)) {
headerviewlistadapter wrapperadapter = (headerviewlistadapter) adapterview.getadapter();
adapter = (sectionedbaseadapter) wrapperadapter.getwrappedadapter();
adapter = (sectionedbaseadapter) adapterview.getadapter();
int section = adapter.getsectionforposition(rawposition);
int position = adapter.getpositioninsectionforposition(rawposition);
if (position == -1) {
onsectionclick(adapterview, view, section, id);
onitemclick(adapterview, view, section, position, id);
public abstract void onitemclick(adapterview<?> adapterview, view view, int section, int position, long id);
public abstract void onsectionclick(adapterview<?> adapterview, view view, int section, long id);
}
sectionedbaseadapter
import android.util.sparsearray;
import android.widget.baseadapter;
import za.co.immedia.pinnedheaderlistview.pinnedheaderlistview.pinnedsectionedheaderadapter;
public abstract class sectionedbaseadapter extends baseadapter implements pinnedsectionedheaderadapter {
private static int header_view_type = 0;
private static int item_view_type = 0;
/**
* holds the calculated values of @{link getpositioninsectionforposition}
*/
private sparsearray<integer> msectionpositioncache;
* holds the calculated values of @{link getsectionforposition}
private sparsearray<integer> msectioncache;
* holds the calculated values of @{link getcountforsection}
private sparsearray<integer> msectioncountcache;
* caches the item count
private int mcount;
* caches the section count
private int msectioncount;
public sectionedbaseadapter() {
super();
msectioncache = new sparsearray<integer>();
msectionpositioncache = new sparsearray<integer>();
msectioncountcache = new sparsearray<integer>();
mcount = -1;
msectioncount = -1;
public void notifydatasetchanged() {
msectioncache.clear();
msectionpositioncache.clear();
msectioncountcache.clear();
super.notifydatasetchanged();
public void notifydatasetinvalidated() {
super.notifydatasetinvalidated();
public final int getcount() {
if (mcount >= 0) {
return mcount;
int count = 0;
for (int i = 0; i < internalgetsectioncount(); i++) {
count += internalgetcountforsection(i);
count++; // for the header view
mcount = count;
return count;
public final object getitem(int position) {
return getitem(getsectionforposition(position), getpositioninsectionforposition(position));
public final long getitemid(int position) {
return getitemid(getsectionforposition(position), getpositioninsectionforposition(position));
public final view getview(int position, view convertview, viewgroup parent) {
if (issectionheader(position)) {
return getsectionheaderview(getsectionforposition(position), convertview, parent);
return getitemview(getsectionforposition(position), getpositioninsectionforposition(position), convertview, parent);
public final int getitemviewtype(int position) {
return getitemviewtypecount() + getsectionheaderviewtype(getsectionforposition(position));
return getitemviewtype(getsectionforposition(position), getpositioninsectionforposition(position));
public final int getviewtypecount() {
return getitemviewtypecount() + getsectionheaderviewtypecount();
public final int getsectionforposition(int position) {
// first try to retrieve values from cache
integer cachedsection = msectioncache.get(position);
if (cachedsection != null) {
return cachedsection;
int sectionstart = 0;
int sectioncount = internalgetcountforsection(i);
int sectionend = sectionstart + sectioncount + 1;
if (position >= sectionstart && position < sectionend) {
msectioncache.put(position, i);
return i;
sectionstart = sectionend;
return 0;
public int getpositioninsectionforposition(int position) {
integer cachedposition = msectionpositioncache.get(position);
if (cachedposition != null) {
return cachedposition;
int positioninsection = position - sectionstart - 1;
msectionpositioncache.put(position, positioninsection);
return positioninsection;
public final boolean issectionheader(int position) {
if (position == sectionstart) {
return true;
} else if (position < sectionstart) {
return false;
sectionstart += internalgetcountforsection(i) + 1;
return false;
public int getitemviewtype(int section, int position) {
return item_view_type;
public int getitemviewtypecount() {
return 1;
public int getsectionheaderviewtype(int section) {
return header_view_type;
public int getsectionheaderviewtypecount() {
public abstract object getitem(int section, int position);
public abstract long getitemid(int section, int position);
public abstract int getsectioncount();
public abstract int getcountforsection(int section);
public abstract view getitemview(int section, int position, view convertview, viewgroup parent);
public abstract view getsectionheaderview(int section, view convertview, viewgroup parent);
private int internalgetcountforsection(int section) {
integer cachedsectioncount = msectioncountcache.get(section);
if (cachedsectioncount != null) {
return cachedsectioncount;
int sectioncount = getcountforsection(section);
msectioncountcache.put(section, sectioncount);
return sectioncount;
private int internalgetsectioncount() {
if (msectioncount >= 0) {
return msectioncount;
msectioncount = getsectioncount();
return msectioncount;
testsectionedadapter 适配器
import java.util.arraylist;
import java.util.hashmap;
import java.util.list;
import java.util.map;
import za.co.immedia.pinnedheaderlistview.sectionedbaseadapter;
import android.view.layoutinflater;
import android.view.view.onclicklistener;
import android.widget.linearlayout;
import android.widget.relativelayout;
import android.widget.textview;
public class testsectionedadapter extends sectionedbaseadapter{
private list<string> titles;//所有标题
private map<string,list<swwsafearea>> mmap;//根据首字母存放数据
public testsectionedadapter(){
list<swwsafearea> localwifi=new arraylist<swwsafearea>();
for(int i=1;i<=10;i++){
localwifi.add(new swwsafearea(i,"10"+i));
list<swwsafearea> localgeography=new arraylist<swwsafearea>();
for(int i=4;i<=15;i++){
localgeography.add(new swwsafearea(i,"10"+i,i));
list<swwsafearea> serverwifi=new arraylist<swwsafearea>();
// for(int i=7;i<=9;i++){
// serverwifi.add(new swwsafearea(i,"10"+i));
// }
list<swwsafearea> servergeography=new arraylist<swwsafearea>();
for(int i=30;i<=60;i++){
servergeography.add(new swwsafearea(i,"10"+i,i));
titles=new arraylist<string>();
titles.add("1");
titles.add("2");
titles.add("3");
titles.add("4");
mmap = new hashmap<string, list<swwsafearea>>();
mmap.put(titles.get(0),localwifi);
mmap.put(titles.get(1),localgeography);
mmap.put(titles.get(2),serverwifi);
mmap.put(titles.get(3),servergeography);
public object getitem(int section, int position) {
return mmap.get(titles.get(section)).get(position);
public long getitemid(int section, int position) {
public int getsectioncount() {//一共显示多少行标题栏
return titles.size();
//每列显示的行数
public int getcountforsection(int section) {
return mmap.get(titles.get(section)).size();
public view getitemview(final int section,final int position, view convertview, viewgroup parent) {
viewholder holder=null;
if (null==convertview) {
holder=new viewholder();
layoutinflater inflator = (layoutinflater) parent.getcontext().getsystemservice(context.layout_inflater_service);
convertview=(linearlayout)inflator.inflate(r.layout.list_item, null);
holder.name=(textview) convertview.findviewbyid(r.id.name);
holder.radius=(textview) convertview.findviewbyid(r.id.radius);
holder.safeareaitem=(relativelayout) convertview.findviewbyid(r.id.safearea_item);
convertview.settag(holder);
}else{
holder=(viewholder) convertview.gettag();
swwsafearea sf=mmap.get(titles.get(section)).get(position);
if(section==0||section==2){
holder.name.settext(sf.name);
holder.radius.settext("");
holder.radius.settext(""+sf.radius);
holder.safeareaitem.setonclicklistener(new onclicklistener() {
@override
public void onclick(view v) {
mmap.get(titles.get(section)).remove(position);
notifydatasetchanged();
});
return convertview;
private class viewholder{
textview name,radius;
relativelayout safeareaitem;
public view getsectionheaderview(int section, view convertview, viewgroup parent) {
linearlayout layout = null;
if (convertview == null) {
layout = (linearlayout) inflator.inflate(r.layout.header_item, null);
} else {
layout = (linearlayout) convertview;
textview textview=((textview) layout.findviewbyid(r.id.textitem));
if(mmap.get(titles.get(section)).size()>0){
textview.setvisibility(view.visible);
textview.settext(titles.get(section));
textview.setvisibility(view.gone);
return layout;
//实体类
private class swwsafearea{
public int id;
public string name;
public int radius;
public swwsafearea(int id, string name, int radius) {
super();
this.id = id;
this.name = name;
this.radius = radius;
public swwsafearea(int id, string name) {
mainactivity
import android.app.activity;
import android.os.bundle;
import android.view.menu;
import za.co.immedia.pinnedheaderlistview.pinnedheaderlistview;
public class mainactivity extends activity {
public void oncreate(bundle savedinstancestate) {
super.oncreate(savedinstancestate);
setcontentview(r.layout.activity_main);
pinnedheaderlistview listview = (pinnedheaderlistview) findviewbyid(r.id.pinnedlistview);
testsectionedadapter sectionedadapter = new testsectionedadapter();
listview.setadapter(sectionedadapter);
public boolean oncreateoptionsmenu(menu menu) {
getmenuinflater().inflate(r.menu.activity_main, menu);
return true;
效果图如下:
![](https://img.laitimes.com/img/_0nNw4CM6IyYiwiM6ICdiwiIykzMwIjN0kTMyIDMxUTMwIzLcRXZu5ibkN3Yuc2bsJmLn1Wavw1LcpDc0RHaiojIsJye.jpg)
推荐下自己创建的android qq群:202928390 欢迎大家的加入.