项目中用到四个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);
- super.setOnScrollListener(this);
- }
- public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) {
- super(context, attrs, defStyle);
- super.setOnScrollListener(this);
- }
- public void setPinHeaders(boolean shouldPin) {
- mShouldPin = shouldPin;
- }
- @Override
- public void setAdapter(ListAdapter adapter) {
- mCurrentHeader = null;
- mAdapter = (PinnedSectionedHeaderAdapter) adapter;
- super.setAdapter(adapter);
- }
- @Override
- 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();
- }
- @Override
- public void onScrollStateChanged(AbsListView view, int scrollState) {
- if (mOnScrollListener != null) {
- 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());
- }
- }
- @Override
- protected void dispatchDraw(Canvas canvas) {
- super.dispatchDraw(canvas);
- if (mAdapter == null || !mShouldPin || mCurrentHeader == null)
- return;
- int saveCount = canvas.save();
- canvas.translate(0, mHeaderOffset);
- canvas.clipRect(0, 0, getWidth(), mCurrentHeader.getMeasuredHeight()); // needed
- // for
- // <
- // HONEYCOMB
- mCurrentHeader.draw(canvas);
- canvas.restoreToCount(saveCount);
- }
- @Override
- public void setOnScrollListener(OnScrollListener l) {
- mOnScrollListener = l;
- }
- @Override
- 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();
- } else {
- adapter = (SectionedBaseAdapter) adapterView.getAdapter();
- }
- int section = adapter.getSectionForPosition(rawPosition);
- int position = adapter.getPositionInSectionForPosition(rawPosition);
- if (position == -1) {
- onSectionClick(adapterView, view, section, id);
- } else {
- 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.view.View;
- import android.view.ViewGroup;
- 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;
- }
- @Override
- public void notifyDataSetChanged() {
- mSectionCache.clear();
- mSectionPositionCache.clear();
- mSectionCountCache.clear();
- mCount = -1;
- mSectionCount = -1;
- super.notifyDataSetChanged();
- }
- @Override
- public void notifyDataSetInvalidated() {
- mSectionCache.clear();
- mSectionPositionCache.clear();
- mSectionCountCache.clear();
- mCount = -1;
- mSectionCount = -1;
- super.notifyDataSetInvalidated();
- }
- @Override
- 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;
- }
- @Override
- public final Object getItem(int position) {
- return getItem(getSectionForPosition(position), getPositionInSectionForPosition(position));
- }
- @Override
- public final long getItemId(int position) {
- return getItemId(getSectionForPosition(position), getPositionInSectionForPosition(position));
- }
- @Override
- 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);
- }
- @Override
- public final int getItemViewType(int position) {
- if (isSectionHeader(position)) {
- return getItemViewTypeCount() + getSectionHeaderViewType(getSectionForPosition(position));
- }
- return getItemViewType(getSectionForPosition(position), getPositionInSectionForPosition(position));
- }
- @Override
- 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;
- for (int i = 0; i < internalGetSectionCount(); i++) {
- 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) {
- // first try to retrieve values from cache
- Integer cachedPosition = mSectionPositionCache.get(position);
- if (cachedPosition != null) {
- return cachedPosition;
- }
- int sectionStart = 0;
- for (int i = 0; i < internalGetSectionCount(); i++) {
- int sectionCount = internalGetCountForSection(i);
- int sectionEnd = sectionStart + sectionCount + 1;
- if (position >= sectionStart && position < sectionEnd) {
- int positionInSection = position - sectionStart - 1;
- mSectionPositionCache.put(position, positionInSection);
- return positionInSection;
- }
- sectionStart = sectionEnd;
- }
- return 0;
- }
- public final boolean isSectionHeader(int position) {
- int sectionStart = 0;
- for (int i = 0; i < internalGetSectionCount(); i++) {
- 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() {
- return 1;
- }
- 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.content.Context;
- import android.view.LayoutInflater;
- import android.view.View;
- import android.view.View.OnClickListener;
- import android.view.ViewGroup;
- 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);
- }
- @Override
- public Object getItem(int section, int position) {
- return mMap.get(titles.get(section)).get(position);
- }
- @Override
- public long getItemId(int section, int position) {
- return 0;
- }
- @Override
- public int getSectionCount() {//一共显示多少行标题栏
- return titles.size();
- }
- //每列显示的行数
- @Override
- public int getCountForSection(int section) {
- return mMap.get(titles.get(section)).size();
- }
- @Override
- 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("");
- }else{
- holder.name.setText(sf.name);
- 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;
- }
- @Override
- public View getSectionHeaderView(int section, View convertView, ViewGroup parent) {
- LinearLayout layout = null;
- if (convertView == null) {
- LayoutInflater inflator = (LayoutInflater) parent.getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- 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));
- }else{
- 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) {
- super();
- this.id = id;
- this.name = 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 {
- @Override
- 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);
- }
- @Override
- public boolean onCreateOptionsMenu(Menu menu) {
- getMenuInflater().inflate(R.menu.activity_main, menu);
- return true;
- }
- }
效果图如下:
推荐下自己创建的android QQ群:202928390 欢迎大家的加入.