源码http://www.cnblogs.com/xiaoQLu/archive/2011/12/20/2293732.html
首先需要一个类,完全不用改动,直接加入
/* * Copyright (C) 2010 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.seven.view; import android.content.Context; import android.graphics.Canvas; import android.util.AttributeSet; import android.view.View; import android.widget.ListAdapter; import android.widget.ListView; /** * A ListView that maintains a header pinned at the top of the list. The * pinned header can be pushed up and dissolved as needed. */ public class PinnedHeaderListView extends ListView { /** * Adapter interface. The list adapter must implement this interface. */ public interface PinnedHeaderAdapter { /** * Pinned header state: don't show the header. */ public static final int PINNED_HEADER_GONE = 0; /** * Pinned header state: show the header at the top of the list. */ public static final int PINNED_HEADER_VISIBLE = 1; /** * Pinned header state: show the header. If the header extends beyond * the bottom of the first shown element, push it up and clip. */ public static final int PINNED_HEADER_PUSHED_UP = 2; /** * Computes the desired state of the pinned header for the given * position of the first visible list item. Allowed return values are * {@link #PINNED_HEADER_GONE}, {@link #PINNED_HEADER_VISIBLE} or * {@link #PINNED_HEADER_PUSHED_UP}. */ int getPinnedHeaderState(int position); /** * Configures the pinned header view to match the first visible list item. * * @param header pinned header view. * @param position position of the first visible list item. * @param alpha fading of the header view, between 0 and 255. */ void configurePinnedHeader(View header, int position, int alpha); } private static final int MAX_ALPHA = 255; private PinnedHeaderAdapter mAdapter; private View mHeaderView; private boolean mHeaderViewVisible; private int mHeaderViewWidth; private int mHeaderViewHeight; public PinnedHeaderListView(Context context) { super(context); } public PinnedHeaderListView(Context context, AttributeSet attrs) { super(context, attrs); } public PinnedHeaderListView(Context context, AttributeSet attrs, int defStyle) { super(context, attrs, defStyle); } public void setPinnedHeaderView(View view) { mHeaderView = view; // Disable vertical fading when the pinned header is present // TODO change ListView to allow separate measures for top and bottom fading edge; // in this particular case we would like to disable the top, but not the bottom edge. if (mHeaderView != null) { setFadingEdgeLength(0); } requestLayout(); } @Override public void setAdapter(ListAdapter adapter) { super.setAdapter(adapter); mAdapter = (PinnedHeaderAdapter)adapter; } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); if (mHeaderView != null) { measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec); mHeaderViewWidth = mHeaderView.getMeasuredWidth(); mHeaderViewHeight = mHeaderView.getMeasuredHeight(); } } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); if (mHeaderView != null) { mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight); configureHeaderView(getFirstVisiblePosition()); } } public void configureHeaderView(int position) { if (mHeaderView == null) { return; } int state = mAdapter.getPinnedHeaderState(position); switch (state) { case PinnedHeaderAdapter.PINNED_HEADER_GONE: { mHeaderViewVisible = false; break; } case PinnedHeaderAdapter.PINNED_HEADER_VISIBLE: { mAdapter.configurePinnedHeader(mHeaderView, position, MAX_ALPHA); if (mHeaderView.getTop() != 0) { mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight); } mHeaderViewVisible = true; break; } case PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP: { View firstView = getChildAt(0); int bottom = firstView.getBottom(); // int itemHeight = firstView.getHeight(); int headerHeight = mHeaderView.getHeight(); int y; int alpha; if (bottom < headerHeight) { y = (bottom - headerHeight); alpha = MAX_ALPHA * (headerHeight + y) / headerHeight; } else { y = 0; alpha = MAX_ALPHA; } mAdapter.configurePinnedHeader(mHeaderView, position, alpha); if (mHeaderView.getTop() != y) { mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight + y); } mHeaderViewVisible = true; break; } } } @Override protected void dispatchDraw(Canvas canvas) { super.dispatchDraw(canvas); if (mHeaderViewVisible) { drawChild(canvas, mHeaderView, getDrawingTime()); } } }
既然是分组,当然要用listview了。
在你需要潜入的地方,贴入以下
<com.seven.view.PinnedHeaderListView android:id="@+id/contact_listview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:fadingEdge="none" />其中com.seven.view是你放上面那个类的地方,也就是包
然后就是item样式了,就是listview里面的内容样式,这都和原生的Listview是一样的用法
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="wrap_content" android:orientation="vertical" android:background="@drawable/mm_listitem" > <LinearLayout android:id="@+id/sort_key_layout" //这里是显示分组的样式 android:layout_width="fill_parent" android:layout_height="18dip" android:background="#dcdcdc" > <TextView android:id="@+id/sort_key" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal" android:layout_marginLeft="10dip" android:textColor="#8b4513" android:textSize="13sp" /> </LinearLayout> <LinearLayout android:id="@+id/name_layout" ///这下面是显示内容的样式 android:layout_width="fill_parent" android:layout_height="50dip" > <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:layout_marginLeft="10dip" android:layout_marginRight="10dip" android:src="@drawable/icon_contact" /> <TextView android:id="@+id/listview_title" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:textColor="#000" android:text="Seven" android:textSize="22sp" /> <TextView android:id="@+id/listview_phone" android:layout_width="wrap_content" android:layout_height="wrap_content" android:paddingLeft="17dp" android:text="10086" android:layout_gravity="center" android:textColor="#666" /> </LinearLayout> </LinearLayout>
package com.seven.tools; import java.util.ArrayList; import java.util.List; import com.seven.bean.ContactBean; import com.seven.quickphone.R; import com.seven.view.PinnedHeaderListView.PinnedHeaderAdapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.BaseAdapter; import android.widget.LinearLayout; import android.widget.SectionIndexer; import android.widget.TextView; public class SortAdapter extends BaseAdapter implements SectionIndexer,PinnedHeaderAdapter,OnScrollListener{ private List<XXX> list = null; //这里是你listview里面要用到的内容的list 可以自定义 private Context mContext; private ArrayList<String> sections=new ArrayList<String>(); //这里是分组的名字也是在外面自定义,然后传进来就是,这和普通的Listview是一样的 public SortAdapter(Context mContext, List<ContactBean> list) { this.mContext = mContext; this.list = list; this.setSections(list); } public void setSections(List<ContactBean> list) { //for(ContactBean c:list) //{ // if(!sections.contains(c.getSortKey())) 这个函数是我自己把list和section的数据绑定了某种关系。当然你们大可不必
// sections.add(c.getSortKey()); 你们可以在外面填充好一个list内容,和一个分组内容就行,当然list里面需要有个 //}<span style="white-space:pre"> </span> 字段,来区分你是哪个分组 } public int getCount() { //都是默认的 return this.list.size(); } public Object getItem(int position) {<pre name="code" class="java">//都是默认的
<pre name="code" class="java">public int getCount() { return this.list.size(); } public Object getItem(int position) { return list.get(position); } public long getItemId(int position) { return position; } public View getView(final int position, View view, ViewGroup arg2) { ViewHolder viewHolder = null; final ContactBean mContent = list.get(position); if (view == null) { viewHolder = new ViewHolder(); view = LayoutInflater.from(mContext).inflate(R.layout.main_contact_listitem, null); viewHolder.tvTitle = (TextView) view.findViewById(R.id.listview_title); viewHolder.tvKey=(TextView) view.findViewById(R.id.sort_key); viewHolder.tvPhone=(TextView) view.findViewById(R.id.listview_phone); viewHolder.sortKeyLayout = (LinearLayout) view.findViewById(R.id.sort_key_layout); view.setTag(viewHolder); } else { viewHolder = (ViewHolder) view.getTag(); } //根据position获取分类的首字母的Char ascii值 int section = getSectionForPosition(position); viewHolder.tvTitle.setText(mContent.getDisplayName()); String t="";int len=mContent.getPhoneNum().size(); for(int i=0;i<len-1;i++) t+=mContent.getPhoneNum().get(i)+"\n"; t+=mContent.getPhoneNum().get(len-1); viewHolder.tvPhone.setText(t); if (position == getPositionForSection(section)) { viewHolder.tvKey.setText(mContent.getSortKey()); viewHolder.sortKeyLayout.setVisibility(View.VISIBLE); } else { viewHolder.sortKeyLayout.setVisibility(View.GONE); } return view; } final static class ViewHolder { TextView tvKey; TextView tvTitle; TextView tvPhone; LinearLayout sortKeyLayout; } public int getNextSection(int section) { for(int i=0,len=sections.size();i<len;i++) { if(sections.get(i).charAt(0)==section &&i+1<len) return sections.get(i+1).charAt(0); } return -1; } /** * 根据ListView的当前位置获取分类的首字母的Char ascii值 */ public int getSectionForPosition(int position) { if(list.size()==0) return -1; return list.get(position).getSortKey().charAt(0); } /** * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置 */ public int getPositionForSection(int section) { for (int i = 0; i < getCount(); i++) { String sortStr = list.get(i).getSortKey(); char firstChar = sortStr.toUpperCase().charAt(0); if (firstChar == section) { return i; } } return -1; } /** * 提取英文的首字母,非英文字母用#代替。 * * @param str * @return */ private String getAlpha(String str) { String sortStr = str.trim().substring(0, 1).toUpperCase(); // 正则表达式,判断首字母是否是英文字母 if (sortStr.matches("[A-Z]")) { return sortStr; } else { return "#"; } } @Override public Object[] getSections() { return null; } @Override public void onScroll(AbsListView view, int firstVisibleItem, int arg2, int arg3) { if (view instanceof com.seven.view.PinnedHeaderListView) { ((com.seven.view.PinnedHeaderListView) view).configureHeaderView(firstVisibleItem); } } @Override public void onScrollStateChanged(AbsListView arg0, int arg1) { // TODO Auto-generated method stub } @Override public int getPinnedHeaderState(int position) { int realPosition = position; if (realPosition < 0) { return PINNED_HEADER_GONE; } int section = getSectionForPosition(realPosition);// 得到此item所在的分组位置 int nextSectionPosition = getPositionForSection(getNextSection(section));// 得到下一个分组的位置 //当前position正好是当前分组的最后一个position,也就是下一个分组的第一个position的前面一个 if (nextSectionPosition != -1 && realPosition == nextSectionPosition - 1) { return PINNED_HEADER_PUSHED_UP; } return PINNED_HEADER_VISIBLE; } @Override public void configurePinnedHeader(View header, int position, int alpha) { int realPosition = position; int section = getSectionForPosition(realPosition); String title = ""+(char)section; ((TextView) header.findViewById(R.id.sort_keyTitle)).setText(title); }
adapter = new SortAdapter(this, list); sortListView.setAdapter(adapter); sortListView.setOnScrollListener(adapter); sortListView.setPinnedHeaderView(getLayoutInflater().inflate(R.layout.main_contact_title, sortListView, false));
如果实在看不懂,先自己看看自定义adapter的实现。
上个效果图吧。O.O
宣传下本人的小制作: 单机斗地主-wifi版 希望大家多多支持。能下载下来玩一玩。
豌豆荚市场: 搜索 "联机斗地主" "单机斗地主-wifi版"都能搜到。
直达连接 http://www.wandoujia.com/apps/com.hj.singlejoker