閱讀161 返回首頁    go 阿裏雲 go 技術社區[雲棲]


創新源於模仿之二:美化ListView的嚐試

 

今天繼續,模仿MIUI做那個Contacts的ListView,如下圖:

 

 

 

其實可以這樣歸納一下我們要做的事情:


1. 按首字母分組,顯示一個分組的標簽頭。

2. 在右邊做一個全字母表,可以用手指上下滑動快速選擇字母分組。

3. 再做一個當前選中的那個字母的顯示。


先說第一件事。我們已經了解ListView/ListAdapter組合做出一個列表界麵。那麼,怎麼在列表中顯示一些不可選且模樣不同的行,在SDK提供的例子其實是有相關代碼可供參考的。

相關的代碼是在ListAdapter中這兩個方法:

 

@Override
public boolean areAllItemsEnabled() {
   return false; //不是所有項都可選
}
@Override
public boolean isEnabled(int position) {
   return !getItem(position).name.startsWith("@section"); //如果名字以@section開頭,則該項不可選
}


 

簡言之,在position這個項是否可選完全可以由你來控製的。

模樣的問題也容易解決,隻是這樣做的效率會降低:

 
public View getView(int position, View convertView, ViewGroup parent) {
// 如果每一項都一樣,就可以這樣。
//	 if(convertView==null){
//	 convertView=mInflater.inflate(R.layout.friends_list_row, null);
//	 }

//但我們每一項的模樣都可能不一樣,隻能這樣了
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;
}


 

可以運行看看,第一個問題解決了。

第二個問題的處理就是找一張圖,放在ListView的右邊即可。先看看我們的Layout文件片斷:

 

注意這個QuickAlphabeticBar是我們自定義的一個View,extends ImageButton,所以你可以先試試用ImageButton放這兒也能很快看到效果。

 

<FrameLayout
        	android:layout_width="fill_parent" 
        	android:layout_height="0.0dip" 
        	android:layout_weight="1.0">
       
       <ListView 
android:
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"

/>
 
<cn.sharetop.xmessenger.ui.QuickAlphabeticBar
android:layout_gravity="top|right|center" 
android: 
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:
   	 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>


 

很好,這個字母表有了,因為我們需要對手指在上麵滑動時的事件進行處理,所以我們自定義它,無非就是要處理這個onTouchEvent(MotionEvent event) 事件罷了,怎麼處理?大家應該都想到了,就是根據手指所在位置算一下是哪個字母。

 

所以呢,我們先處理一下那個ListAdapter,讓它implements SectionIndexer。

 

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裏,我們可以快速定位到相應的段上:

 

@Override
public boolean onTouchEvent(MotionEvent event) {
int act=event.getAction();
//......
float y = event.getY();
//算手指位置,找到對應的段,讓mList移動段開頭的位置上
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了,現在我們已經解決兩個問題了,但是如何顯示一個小的浮動窗口提示當前選中的段的首字母呢,也不複雜,大家注意到文章開頭那個Layout中的ID為fast_position的TextView了吧?就是它了。

 

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滾動時,讓mDialogText顯示出來,並且設置它的內容為首字母即可。關鍵是那個mHandler,即1秒後要記得讓它隱藏起來。

 

 

就是這麼多事情了,大家可以試試。

最後更新:2017-04-02 06:51:58

  上一篇:go 創新源於模仿之六:仿iPhone的分組列表做一個配置界麵
  下一篇:go [usaco]5.4.5 Big Barn題解