創(chuàng)新源于模仿之二:美化ListView的嘗試 收藏 此文于2011-03-16被推薦到CSDN首頁 如何被推薦? 今天繼續(xù),模仿MIUI做那個(gè)Contacts的ListView,如下圖:
其實(shí)可以這樣歸納一下我們要做的事情:
1. 按首字母分組,顯示一個(gè)分組的標(biāo)簽頭。
2. 在右邊做一個(gè)全字母表,可以用手指上下滑動(dòng)快速選擇字母分組。
3. 再做一個(gè)當(dāng)前選中的那個(gè)字母的顯示。
先說第一件事。我們已經(jīng)了解ListView/ListAdapter組合做出一個(gè)列表界面。那么,怎么在列表中顯示一些不可選且模樣不同的行,在SDK提供的例子其實(shí)是有相關(guān)代碼可供參考的。
相關(guān)的代碼是在ListAdapter中這兩個(gè)方法:
+ expand sourceview plaincopy to clipboardprint? @Override public boolean areAllItemsEnabled() { return false; //不是所有項(xiàng)都可選 } @Override public boolean isEnabled(int position) { return !getItem(position).name.startsWith("@section"); //如果名字以@section開頭,則該項(xiàng)不可選 } @Override public boolean areAllItemsEnabled() { return false; //不是所有項(xiàng)都可選 } @Override public boolean isEnabled(int position) { return !getItem(position).name.startsWith("@section"); //如果名字以@section開頭,則該項(xiàng)不可選 }
簡言之,在position這個(gè)項(xiàng)是否可選完全可以由你來控制的。
模樣的問題也容易解決,只是這樣做的效率會降低:
view plaincopy to clipboardprint? public View getView(int position, View convertView, ViewGroup parent) { // 如果每一項(xiàng)都一樣,就可以這樣。 // if(convertView==null){ // convertView=mInflater.inflate(R.layout.friends_list_row, null); // } //但我們每一項(xiàng)的模樣都可能不一樣,只能這樣了 FriendInfo item = (FriendInfo)getItem(position); if(item!=null){ if(!item.name.startsWith("@section")){ convertView=mInflater.inflate(R.layout.friends_list_row, null); //... ... convertView.setTag(item); } else { convertView=mInflater.inflate(R.layout.friends_list_section, null); // ... ... } } return convertView; } public View getView(int position, View convertView, ViewGroup parent) { // 如果每一項(xiàng)都一樣,就可以這樣。 // if(convertView==null){ // convertView=mInflater.inflate(R.layout.friends_list_row, null); // }
//但我們每一項(xiàng)的模樣都可能不一樣,只能這樣了 FriendInfo item = (FriendInfo)getItem(position); if(item!=null){ if(!item.name.startsWith("@section")){ convertView=mInflater.inflate(R.layout.friends_list_row, null); //... ... convertView.setTag(item); } else { convertView=mInflater.inflate(R.layout.friends_list_section, null); // ... ... } } return convertView; }
可以運(yùn)行看看,第一個(gè)問題解決了。
第二個(gè)問題的處理就是找一張圖,放在ListView的右邊即可。先看看我們的Layout文件片斷:
注意這個(gè)QuickAlphabeticBar是我們自定義的一個(gè)View,extends ImageButton,所以你可以先試試用ImageButton放這兒也能很快看到效果。
view plaincopy to clipboardprint? <FrameLayout android:layout_width="fill_parent" android:layout_height="0.0dip" android:layout_weight="1.0"> <ListView android:id="@id/friends_list" android:scrollbars="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginLeft="0.0dip" android:drawSelectorOnTop="false" android:scrollingCache="true" android:layout_weight="1.0" android:fastScrollEnabled="false" android:footerDividersEnabled="true" android:cacheColorHint="#00000000" style="@style/Widget.ListViewGreen" /> <cn.sharetop.xmessenger.ui.QuickAlphabeticBar android:layout_gravity="top|right|center" android:id="@id/fast_scroller" android:background="@null" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10.0px" android:scaleType="centerInside" android:src="@drawable/contact_list_scroll_long" /> <TextView android:id="@id/fast_position" android:textSize="48dip" android:textColor="#99FFFFFF" android:background="@drawable/fast_scroller_overlay" android:layout_gravity="center_horizontal|top" android:padding="2dip" android:layout_margin="24dip" android:layout_width="@dimen/header_width" android:layout_height="@dimen/header_width" android:gravity="center"/> </FrameLayout> <FrameLayout android:layout_width="fill_parent" android:layout_height="0.0dip" android:layout_weight="1.0"> <ListView android:id="@id/friends_list" android:scrollbars="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" android:layout_marginLeft="0.0dip" android:drawSelectorOnTop="false" android:scrollingCache="true" android:layout_weight="1.0" android:fastScrollEnabled="false" android:footerDividersEnabled="true" android:cacheColorHint="#00000000" style="@style/Widget.ListViewGreen" /> <cn.sharetop.xmessenger.ui.QuickAlphabeticBar android:layout_gravity="top|right|center" android:id="@id/fast_scroller" android:background="@null" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="10.0px" android:scaleType="centerInside" android:src="@drawable/contact_list_scroll_long" /> <TextView android:id="@id/fast_position" android:textSize="48dip" android:textColor="#99FFFFFF" android:background="@drawable/fast_scroller_overlay" android:layout_gravity="center_horizontal|top" android:padding="2dip" android:layout_margin="24dip" android:layout_width="@dimen/header_width" android:layout_height="@dimen/header_width" android:gravity="center"/> </FrameLayout>
很好,這個(gè)字母表有了,因?yàn)槲覀冃枰獙κ种冈谏厦婊瑒?dòng)時(shí)的事件進(jìn)行處理,所以我們自定義它,無非就是要處理這個(gè)onTouchEvent(MotionEvent event) 事件罷了,怎么處理?大家應(yīng)該都想到了,就是根據(jù)手指所在位置算一下是哪個(gè)字母。
所以呢,我們先處理一下那個(gè)ListAdapter,讓它implements SectionIndexer。
view plaincopy to clipboardprint? public class FriendsListAdapter extends ArrayAdapter<FriendInfo> implements SectionIndexer{ private Context mContext; private LayoutInflater mInflater; private HashMap<String, Integer> alphaIndexer; private String[] sections = new String[0]; public FriendsListAdapter(Context context, int textViewResourceId, List<BuddyInfo> objects) { super(context, textViewResourceId, objects); mContext=context; mInflater=LayoutInflater.from(mContext); initSections(objects); } //...... @Override public int getPositionForSection(int section) { String letter = sections[section]; return alphaIndexer.get(letter); } @Override public int getSectionForPosition(int position) { int prevIndex = 0; for(int i = 0; i < sections.length; i++) { if(getPositionForSection(i) > position && prevIndex <= position) { prevIndex = i; break; } prevIndex = i; } return prevIndex; } @Override public Object[] getSections() { return sections; } private void initSections(List<BuddyInfo> items){ alphaIndexer = new HashMap<String, Integer>(); for(int i = items.size() - 1; i >= 0; i--) { BuddyInfo element = items.get(i); String firstChar = element.sortKey;//.substring(0, 1).toUpperCase(); if(firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A') firstChar = "#"; alphaIndexer.put(firstChar, i); } Set<String> keys = alphaIndexer.keySet(); Iterator<String> it = keys.iterator(); ArrayList<String> keyList = new ArrayList<String>(); while(it.hasNext()) keyList.add(it.next()); Collections.sort(keyList); sections = new String[keyList.size()]; keyList.toArray(sections); } } public class FriendsListAdapter extends ArrayAdapter<FriendInfo> implements SectionIndexer{ private Context mContext; private LayoutInflater mInflater; private HashMap<String, Integer> alphaIndexer; private String[] sections = new String[0]; public FriendsListAdapter(Context context, int textViewResourceId, List<BuddyInfo> objects) { super(context, textViewResourceId, objects); mContext=context; mInflater=LayoutInflater.from(mContext); initSections(objects); } //...... @Override public int getPositionForSection(int section) { String letter = sections[section]; return alphaIndexer.get(letter); } @Override public int getSectionForPosition(int position) { int prevIndex = 0; for(int i = 0; i < sections.length; i++) { if(getPositionForSection(i) > position && prevIndex <= position) { prevIndex = i; break; } prevIndex = i; } return prevIndex; } @Override public Object[] getSections() { return sections; } private void initSections(List<BuddyInfo> items){ alphaIndexer = new HashMap<String, Integer>(); for(int i = items.size() - 1; i >= 0; i--) { BuddyInfo element = items.get(i); String firstChar = element.sortKey;//.substring(0, 1).toUpperCase(); if(firstChar.charAt(0) > 'Z' || firstChar.charAt(0) < 'A') firstChar = "#"; alphaIndexer.put(firstChar, i); } Set<String> keys = alphaIndexer.keySet(); Iterator<String> it = keys.iterator(); ArrayList<String> keyList = new ArrayList<String>(); while(it.hasNext()) keyList.add(it.next()); Collections.sort(keyList); sections = new String[keyList.size()]; keyList.toArray(sections); } }
然后,在QuickAlphabeticBar里的onTouchEvent里,我們可以快速定位到相應(yīng)的段上:
view plaincopy to clipboardprint? @Override public boolean onTouchEvent(MotionEvent event) { int act=event.getAction(); //...... float y = event.getY(); //算手指位置,找到對應(yīng)的段,讓mList移動(dòng)段開頭的位置上 if(mListAdapter!=null){ int x=getAlphabeticPostion(y); if(x<0)x=0; if(x>=mSections.length) x=mSections.length-1; int pos=((SectionIndexer)mListAdapter).getPositionForSection(x); this.mList.setSelectionFromTop(pos, 0); } else{ this.mList.setSelectionFromTop(getAlphabeticPostion(y),0); } return super.onTouchEvent(event); } @Override public boolean onTouchEvent(MotionEvent event) { int act=event.getAction(); //...... float y = event.getY(); //算手指位置,找到對應(yīng)的段,讓mList移動(dòng)段開頭的位置上 if(mListAdapter!=null){ int x=getAlphabeticPostion(y); if(x<0)x=0; if(x>=mSections.length) x=mSections.length-1; int pos=((SectionIndexer)mListAdapter).getPositionForSection(x); this.mList.setSelectionFromTop(pos, 0); } else{ this.mList.setSelectionFromTop(getAlphabeticPostion(y),0); } return super.onTouchEvent(event); }
OK了,現(xiàn)在我們已經(jīng)解決兩個(gè)問題了,但是如何顯示一個(gè)小的浮動(dòng)窗口提示當(dāng)前選中的段的首字母呢,也不復(fù)雜,大家注意到文章開頭那個(gè)Layout中的ID為fast_position的TextView了吧?就是它了。
view plaincopy to clipboardprint? public class QuickAlphabeticBar extends ImageButton implements OnScrollListener { //... ... public void init(Context ctx){ mDialogText=(TextView)((FriendsActivity)ctx).findViewById(R.id.fast_position); mDialogText.setVisibility(View.INVISIBLE); mReady = true; //... ... } //... ... @Override public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) { if (mReady) { if(mListAdapter!=null && mListAdapter.getCount()>0){ BuddyInfo item = (BuddyInfo)mListAdapter.getItem(firstVisibleItem); char firstLetter = (item.sortKey!=null && item.sortKey.trim().length()>0)?item.sortKey.toUpperCase().charAt(0):' '; if (!mShowing && firstLetter != mPrevLetter) { mShowing = true; mDialogText.setVisibility(View.VISIBLE); } mDialogText.setText(((Character)firstLetter).toString()); mHandler.removeCallbacks(mRemoveWindow); mHandler.postDelayed(mRemoveWindow, 1000); mPrevLetter = firstLetter; } } } //... ... } public class QuickAlphabeticBar extends ImageButton implements OnScrollListener { //... ... public void init(Context ctx){ mDialogText=(TextView)((FriendsActivity)ctx).findViewById(R.id.fast_position); mDialogText.setVisibility(View.INVISIBLE); mReady = true; //... ... } //... ... @Override public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) { if (mReady) { if(mListAdapter!=null && mListAdapter.getCount()>0){ BuddyInfo item = (BuddyInfo)mListAdapter.getItem(firstVisibleItem); char firstLetter = (item.sortKey!=null && item.sortKey.trim().length()>0)?item.sortKey.toUpperCase().charAt(0):' '; if (!mShowing && firstLetter != mPrevLetter) { mShowing = true; mDialogText.setVisibility(View.VISIBLE); } mDialogText.setText(((Character)firstLetter).toString()); mHandler.removeCallbacks(mRemoveWindow); mHandler.postDelayed(mRemoveWindow, 1000); mPrevLetter = firstLetter; } } } //... ... }
這段代碼就是ListView滾動(dòng)時(shí),讓mDialogText顯示出來,并且設(shè)置它的內(nèi)容為首字母即可。關(guān)鍵是那個(gè)mHandler,即1秒后要記得讓它隱藏起來。
就是這么多事情了,大家可以試試。
本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/sharetop/archive/2011/03/13/6246629.aspx
|