The ListView In the Android contact is unique, but the source code is relatively copied. It is not easy to extract it from the source code when we want to use it, and it is prone to errors, in the past few days, I have extracted it and written it as a simple example. I will give myself a memo, but I will share it with you. Well, let's take a look:
The first is the encapsulated PinnedHeaderListView with a header:
Copy codeThe Code is as follows: public class PinnedHeaderListView extends ListView {
Public interface PinnedHeaderAdapter {
Public static final int PINNED_HEADER_GONE = 0;
Public static final int PINNED_HEADER_VISIBLE = 1;
Public static final int PINNED_HEADER_PUSHED_UP = 2;
Int getPinnedHeaderState (int position );
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 );
}
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 ());
}
}
Protected void onMeasure (int widthMeasureSpec, int heightMeasureSpec ){
Super. onMeasure (widthMeasureSpec, heightMeasureSpec );
If (mHeaderView! = Null ){
MeasureChild (mHeaderView, widthMeasureSpec, heightMeasureSpec );
MHeaderViewWidth = mHeaderView. getMeasuredWidth ();
MHeaderViewHeight = mHeaderView. getMeasuredHeight ();
}
}
Public void setPinnedHeaderView (View view ){
MHeaderView = view;
If (mHeaderView! = Null ){
SetFadingEdgeLength (0 );
}
RequestLayout ();
}
Public void setAdapter (ListAdapter adapter ){
Super. setAdapter (adapter );
MAdapter = (PinnedHeaderAdapter) adapter;
}
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 headerHeight = mHeaderView. getHeight ();
Int y;
Int alpha;
If (bottom 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;
}
}
}
Protected void dispatchDraw (Canvas canvas ){
Super. dispatchDraw (canvas );
If (mHeaderViewVisible ){
DrawChild (canvas, mHeaderView, getDrawingTime ());
}
}
}
Then there is the quick navigation BladeView (Blade ):Copy codeThe Code is as follows: public class BladeView extends View {
Private OnItemClickListener mOnItemClickListener;
String [] B = {"#", "A", "B", "C", "D", "E", "F", "G ", "H", "I", "J", "K ",
"L", "M", "N", "O", "P", "Q", "R", "S", "T", "U ", "V", "W", "X ",
"Y", "Z "};
Int choose =-1;
Paint paint = new Paint ();
Boolean showBkg = false;
Private PopupWindow mPopupWindow;
Private TextView mPopupText;
Private Handler handler = new Handler ();
Public BladeView (Context context, AttributeSet attrs, int defStyle ){
Super (context, attrs, defStyle );
}
Public BladeView (Context context, AttributeSet attrs ){
Super (context, attrs );
}
Public BladeView (Context context ){
Super (context );
}
@ Override
Protected void onDraw (Canvas canvas ){
Super. onDraw (canvas );
If (showBkg ){
Canvas. drawColor (Color. parseColor ("#00000000 "));
}
Int height = getHeight ();
Int width = getWidth ();
Int singleHeight = height/B. length;
For (int I = 0; I <B. length; I ++ ){
Paint. setColor (Color. BLACK );
Paint. setTypeface (Typeface. DEFAULT_BOLD );
Paint. setFakeBoldText (true );
Paint. setAntiAlias (true );
If (I = choose ){
Paint. setColor (Color. parseColor ("# 3399ff "));
}
Float xPos = width/2-paint. measureText (B [I])/2;
Float yPos = singleHeight * I + singleHeight;
Canvas. drawText (B [I], xPos, yPos, paint );
Paint. reset ();
}
}
@ Override
Public boolean dispatchTouchEvent (MotionEvent event ){
Final int action = event. getAction ();
Final float y = event. getY ();
Final int oldChoose = choose;
Final int c = (int) (y/getHeight () * B. length );
Switch (action ){
Case MotionEvent. ACTION_DOWN:
ShowBkg = true;
If (oldChoose! = C ){
If (c> 0 & c <B. length ){
Receivmitemclicked (c );
Choose = c;
Invalidate ();
}
}
Break;
Case MotionEvent. ACTION_MOVE:
If (oldChoose! = C ){
If (c> 0 & c <B. length ){
Receivmitemclicked (c );
Choose = c;
Invalidate ();
}
}
Break;
Case MotionEvent. ACTION_UP:
ShowBkg = false;
Choose =-1;
DismissPopup ();
Invalidate ();
Break;
}
Return true;
}
Private void showPopup (int item ){
If (mPopupWindow = null ){
Handler. removeCallbacks (dismissRunnable );
MPopupText = new TextView (getContext ());
MPopupText. setBackgroundColor (Color. GRAY );
MPopupText. setTextColor (Color. CYAN );
MPopupText. setTextSize (50 );
MPopupText. setGravity (Gravity. CENTER_HORIZONTAL
| Gravity. CENTER_VERTICAL );
MPopupWindow = new PopupWindow (mPopupText, 100,100 );
}
String text = "";
If (item = 0 ){
Text = "#";
} Else {
Text = Character. toString (char) ('A' + item-1 ));
}
MPopupText. setText (text );
If (mPopupWindow. isShowing ()){
MPopupWindow. update ();
} Else {
MPopupWindow. showAtLocation (getRootView (),
Gravity. CENTER_HORIZONTAL | Gravity. CENTER_VERTICAL, 0, 0 );
}
}
Private void dismissPopup (){
Handler. postDelayed (dismissRunnable, 800 );
}
Runnable dismissRunnable = new Runnable (){
@ Override
Public void run (){
// TODO Auto-generated method stub
If (mPopupWindow! = Null ){
MPopupWindow. dismiss ();
}
}
};
Public boolean onTouchEvent (MotionEvent event ){
Return super. onTouchEvent (event );
}
Public void setOnItemClickListener (OnItemClickListener listener ){
MOnItemClickListener = listener;
}
Private void initialize mitemclicked (int item ){
If (mOnItemClickListener! = Null ){
MOnItemClickListener. onItemClick (B [item]);
ShowPopup (item );
}
}
Public interface OnItemClickListener {
Void onItemClick (String s );
}
}
The next step is to use it. Declare activity_main.xml in the layout file:Copy codeThe Code is as follows: <RelativeLayout xmlns: android = "http://schemas.android.com/apk/res/android"
Xmlns: tools = "http://schemas.android.com/tools"
Android: layout_width = "match_parent"
Android: layout_height = "match_parent"
Tools: context = ". MainActivity">
<Com. way. view. PinnedHeaderListView
Android: id = "@ + id/friends_display"
Android: layout_width = "fill_parent"
Android: layout_height = "fill_parent"
Android: cacheColorHint = "#00000000"
Android: divider = "@ null"
Android: footerDividersEnabled = "false"
Android: headerDividersEnabled = "false"/>
<Com. way. view. BladeView
Android: id = "@ + id/friends_myletterlistview"
Android: layout_width = "30dip"
Android: layout_height = "fill_parent"
Android: layout_alignParentRight = "true"
Android: background = "#00000000"/>
</RelativeLayout>
Then there is an independent Adapter. This time I didn't put it in MainActivity as an internal class:Copy codeThe Code is as follows: public class FriendsAdapter extends BaseAdapter implements SectionIndexer,
PinnedHeaderAdapter, OnScrollListener {
Private int mLocationPosition =-1;
Private String [] mDatas;
// Initial set
Private List <String> mFriendsSections;
Private List <Integer> mFriendsPositions;
Private LayoutInflater inflater;
Public FriendsAdapter (Context context, String [] datas, List <String> friendsSections,
List <Integer> friendsPositions ){
// TODO Auto-generated constructor stub
Inflater = LayoutInflater. from (context );
MDatas = datas;
MFriendsSections = friendsSections;
MFriendsPositions = friendsPositions;
}
@ Override
Public int getCount (){
// TODO Auto-generated method stub
Return mDatas. length;
}
@ Override
Public Object getItem (int position ){
// TODO Auto-generated method stub
Return mDatas [position];
}
@ Override
Public long getItemId (int position ){
// TODO Auto-generated method stub
Return position;
}
@ Override
Public View getView (int position, View convertView, ViewGroup parent ){
// TODO Auto-generated method stub
Int section = getSectionForPosition (position );
If (convertView = null ){
ConvertView = inflater. inflate (R. layout. listview_item, null );
}
LinearLayout mHeaderParent = (LinearLayout) convertView
. FindViewById (R. id. friends_item_header_parent );
TextView mHeaderText = (TextView) convertView
. FindViewById (R. id. friends_item_header_text );
If (getPositionForSection (section) = position ){
MHeaderParent. setVisibility (View. VISIBLE );
MHeaderText. setText (mFriendsSections. get (section ));
} Else {
MHeaderParent. setVisibility (View. GONE );
}
TextView textView = (TextView) convertView
. FindViewById (R. id. friends_item );
TextView. setText (mDatas [position]);
Return convertView;
}
@ Override
Public void onScrollStateChanged (AbsListView view, int scrollState ){
// TODO Auto-generated method stub
}
@ Override
Public void onScroll (AbsListView view, int firstVisibleItem,
Int visibleItemCount, int totalItemCount ){
// TODO Auto-generated method stub
If (view instanceof PinnedHeaderListView ){
(PinnedHeaderListView) view). configureHeaderView (firstVisibleItem );
}
}
@ Override
Public int getPinnedHeaderState (int position ){
Int realPosition = position;
If (realPosition <0
| (MLocationPosition! =-1 & mLocationPosition = realPosition )){
Return PINNED_HEADER_GONE;
}
MLocationPosition =-1;
Int section = getSectionForPosition (realPosition );
Int nextSectionPosition = getPositionForSection (section + 1 );
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 ){
// TODO Auto-generated method stub
Int realPosition = position;
Int section = getSectionForPosition (realPosition );
String title = (String) getSections () [section];
(TextView) header. findViewById (R. id. friends_list_header_text ))
. SetText (title );
}
@ Override
Public Object [] getSections (){
// TODO Auto-generated method stub
Return mFriendsSections. toArray ();
}
@ Override
Public int getPositionForSection (int section ){
If (section <0 | section> = mFriendsSections. size ()){
Return-1;
}
Return mFriendsPositions. get (section );
}
@ Override
Public int getSectionForPosition (int position ){
// TODO Auto-generated method stub
If (position <0 | position> = getCount ()){
Return-1;
}
Int index = Arrays. binarySearch (mFriendsPositions. toArray (), position );
Return index> = 0? Index:-index-2;
}
}
Finally, it is processed in MainActivity:Copy codeThe Code is as follows: public class MainActivity extends Activity {
Private static final String FORMAT = "^ [a-z, A-Z]. * $ ";
Private PinnedHeaderListView mListView;
Private BladeView mLetter;
Private FriendsAdapter mAdapter;
Private String [] datas;
// Initial set
Private List <String> mSections;
// Store data according to the first letter
Private Map <String, List <String> mMap;
// Initial position set
Private List <Integer> mPositions;
// Position corresponding to the first letter
Private Map <String, Integer> mIndexer;
@ Override
Protected void onCreate (Bundle savedInstanceState ){
Super. onCreate (savedInstanceState );
SetContentView (R. layout. activity_main );
InitData ();
InitView ();
}
Private void initData (){
Datas = getResources (). getStringArray (R. array. countries );
MSections = new ArrayList <String> ();
MMap = new HashMap <String, List <String> ();
MPositions = new ArrayList <Integer> ();
MIndexer = new HashMap <String, Integer> ();
For (int I = 0; I <datas. length; I ++ ){
String firstName = datas [I]. substring (0, 1 );
If (firstName. matches (FORMAT )){
If (mSections. contains (firstName )){
MMap. get (firstName). add (datas [I]);
} Else {
MSections. add (firstName );
List <String> list = new ArrayList <String> ();
List. add (datas [I]);
MMap. put (firstName, list );
}
} Else {
If (mSections. contains ("#")){
MMap. get ("#"). add (datas [I]);
} Else {
MSections. add ("#");
List <String> list = new ArrayList <String> ();
List. add (datas [I]);
MMap. put ("#", list );
}
}
}
Collections. sort (mSections );
Int position = 0;
For (int I = 0; I <mSections. size (); I ++ ){
MIndexer. put (mSections. get (I), position); // saved to map. The key is the first letter string, and the value is the first letter in listview.
MPositions. add (position); // position where the first letter is in the listview and saved to the list
Position + = mMap. get (mSections. get (I). size (); // calculates the position of the first letter in the listview.
}
}
Private void initView (){
// TODO Auto-generated method stub
MListView = (PinnedHeaderListView) findViewById (R. id. friends_display );
MLetter = (BladeView) findViewById (R. id. friends_myletterlistview );
MLetter. setOnItemClickListener (new OnItemClickListener (){
@ Override
Public void onItemClick (String s ){
If (mIndexer. get (s )! = Null ){
MListView. setSelection (mIndexer. get (s ));
}
}
});
MAdapter = new FriendsAdapter (this, datas, mSections, mPositions );
MListView. setAdapter (mAdapter );
MListView. setOnScrollListener (mAdapter );
MListView. setPinnedHeaderView (LayoutInflater. from (this). inflate (
R. layout. listview_head, mListView, false ));
}
}
There is also a data arrays. xml, so I will not post it. If you are interested, you can download the source code.