QQ聯繫人,ExpandableListView

前兩天電腦壞掉了,致使這一兩天沒有更新。發現以前寫的幾篇博客瀏覽量已經接近1000,有種受寵若驚的感受,寫這個系列的博客主要是假期想幹一些平時沒有時間作的興趣愛好,並把本身的一點一滴記錄下來。這一段時間因爲各類緣由以至於更新速度很慢,以前的打算是先把QQ2013的Activity的佈局搭建出來,而後再設計其中的核心代碼,如網絡編程,搭建一個小型服務器,進而實現QQ基本的功能,從如今的進度來看這個計劃可能完不成了。不過我仍然會繼續作這個項目,相信就算不會完全完成也可以搭出漂亮的QQ框架來。 android

  進入正題。上一節結束時提到QQ聯繫人列表界面還有一個效果沒有實現,這個效果是當聯繫人一級條目滑動到屏幕的頂端時,該一級條目就會懸停在屏幕的頂端,當往下拉時又會隨着ExpandableListView的Item正常移動,如官方效果圖圖所示: 編程

 

  注意觀察「個人好友」那一欄,本節實現的即上述效果。 服務器

首先講一下思路: 網絡

  一、不能使用onScroll()方法,這一方法會在ExpandableListView展開的時候調用,至於其調用的週期並非太明確,但能夠肯定的是onScroll並不能直接實現上述效果。               框架

  二、這裏應該得到ScrollView的實例(findViewById方法),而後調用setOnTouch()方法,在setOnTouch()方法中進一步進行設置。 ide

  三、這裏的基本思路是:當拖動屏幕時,setOnTouch方法被調用,在該方法中開闢一個子線程,這個子線程用於檢測屏幕中的ScrollView對象是否仍在運動,直到再也不拖動屏幕或者屏幕上的控件在慣性的做用下運動至中止時,該線程纔會自動終結。在該線程中判斷一級條目(這裏用groupItem表示)是否向上滾出了屏幕。 oop

  四、在該Activity的主佈局文件中ScrollView的後面添加一個LinearLayout控件,該控件的佈局和GroupItem的佈局文件的內容是同樣的,將其位置設置爲至於屏幕(父控件)的頂端,平時設置其Visibility爲View.GONE 不可見不可操做狀態,當某一個已經展開(expanded)的groupItem滾出了屏幕時,就將該隱藏的控件設置爲可見,並將其內容設置爲和剛剛滾出屏幕的groupItem的內容一摸同樣。若全部的groupItem都爲滑出屏幕,那麼將該控件從新設置爲不可見(GONE); 佈局

  五、本節中碰見的最大的障礙就是我沒有找到得到每個groupItem在屏幕上的實時位置的方法,因此這裏對原來的groupData對象作了修改,在HashMap對象中新添加了一個鍵值對 key="location"  value= 該groupItem在ExpandableListView中的相對縱座標。在後在上面建立的座標檢測子線程中得到groupData中存放的groupItem的相對座標再加上ExpandableListView的絕對縱座標,而後判斷groupItem是否滑出了屏幕,進而對「隱藏控件」執行相應的操做。注意這裏應該先判斷groupPosition靠後的groupItem的座標,懸停的控件顯示的是最後滑出屏幕的groupItem的信息。 this

  六、本節將Activity的標題去掉了,去掉的方法是在onCreate()方法中 setContentView以前(必須是以前)調用requestWindowFeature(Window.FEATURE_NO_TITLE); spa

  七、本節中須要在子線程中修改MainActivity 安卓UI,而直接修改是不容許的,因此須要使用一個自定義的Handler對象,調用父類的構造方法

      public MyHandler(Looper looper){

                         super(looper);

      }

          並複寫handleMessage()方法修改MainActivity的UI。

          關於Android消息處理機制我目前不想再多講,作開發的話夠用就行,不必掌握的太完全,若是想對Handler有一個比較好的瞭解,能夠參考:

          http://blog.csdn.net/zkdemon/article/details/6914161

   八、ExpandableListView能夠調用collapseGroup(int groupPosition)或者ExpandGroup(int groupPosition)強制的關閉或者展開一個Group。這對於實現懸浮控件的點擊事件具備重要做用!

  鑑於代碼量愈來愈多了,這裏先把修改的地方列出來:

主佈局文件:

複製代碼
<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" android:background="#f1f1f1" tools:context=".MainActivity" android:scrollbars="vertical" android:scrollbarAlwaysDrawVerticalTrack="true" > <ScrollView android:id="@+id/qqlist_scroll" android:layout_width="match_parent" android:layout_height="wrap_content" android:fadingEdge="none"> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ListView android:id="@+id/qqlist_classify" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="#000000" android:dividerHeight="0px" android:fadingEdge="none"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/qqlist_classify_text" android:text="好友分組" android:textSize="6pt" android:textColor="#666666" android:padding="4dip"/> <ExpandableListView android:id="@+id/qq_list" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="#888888" android:dividerHeight="0px" android:fadingEdge="none"/> <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@drawable/qqlist_classify_text" android:text="生活服務" android:textSize="6pt" android:textColor="#666666" android:padding="4dip"/> <ListView android:id="@+id/qqlist_classify_down" android:layout_width="match_parent" android:layout_height="wrap_content" android:divider="#000000" android:dividerHeight="0px"/> </LinearLayout> </ScrollView> <LinearLayout android:layout_width="match_parent" android:layout_height="40dip" android:id="@+id/titleGroupView" android:orientation="horizontal" android:visibility="gone" android:background="@drawable/list_item_border"> <ImageView android:id="@+id/title_groupImage" android:layout_width="match_parent" android:layout_height="16dip" android:layout_weight="1.8" android:layout_gravity="center" android:background="@drawable/todown" /> <TextView android:id="@+id/title_groupName" android:layout_width="match_parent" android:layout_height="42dip" android:layout_weight="1" android:paddingLeft="15dip" android:paddingTop="11dip" android:textSize="7pt" android:text="fdg" /> <TextView android:id="@+id/title_childCount" android:layout_width="match_parent" android:layout_height="42dip" android:layout_weight="1" android:gravity="right" android:paddingRight="10dip" android:paddingTop="11dip" android:text="ewsf"/> </LinearLayout> </RelativeLayout>
複製代碼


展開或者關閉group時都會對每個groupItem的縱座標進行更新。

複製代碼
@Override public void onGroupExpanded(int groupPosition) { // TODO Auto-generated method stub super.onGroupExpanded(groupPosition); /** * 更新groupItem的相對座標 */ groupData.get(groupPosition).put("expanded", true); for(int i=groupPosition+1; i<groupData.size(); i++){ groupData.get(i).put("location",(Integer)groupData.get(i).get("location")+childData.get(groupPosition).size()*CHILD_HEIGHT ); } float deviationFix=0; //對ExpandableListView高度的偏差修正 for(int i=0;i<groupData.size() ;i++){ deviationFix +=(Integer)groupData.get(i).get("location")/CHILD_HEIGHT*0.5; } height+=childData.get(groupPosition).size()*CHILD_HEIGHT; ViewGroup.LayoutParams params= exListView.getLayoutParams(); params.height=height-floatToInt(deviationFix); exListView.setLayoutParams(params); } @Override public void onGroupCollapsed(int groupPosition) { // TODO Auto-generated method stub super.onGroupCollapsed(groupPosition); height=height-childData.get(groupPosition).size()*CHILD_HEIGHT; ViewGroup.LayoutParams params= exListView.getLayoutParams(); params.height=height; exListView.setLayoutParams(params); /** * 更新groupItem的相對座標 */ groupData.get(groupPosition).put("expanded", false); for(int i=groupPosition+1; i<groupData.size(); i++){ groupData.get(i).put("location",(Integer)groupData.get(i).get("location")-childData.get(groupPosition).size()*CHILD_HEIGHT ); } }
複製代碼


對ScrollView設置監聽器: 

複製代碼
qqScroll.setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { // TODO Auto-generated method stub if(event.getAction()==MotionEvent.ACTION_MOVE && isChecking==false){ new LocationCheckThread().start(); } return false; } });
複製代碼

座標檢測線程:

複製代碼
/** * 實現這個Activity的最後一個效果,即滑動到某一個Group那麼改GroupItem就在屏幕頂端懸停的效果 * 方法:須要使用onTouchEvent方法,這個比用onScrollView方法靠譜 */ private boolean isChecking=false; private class LocationCheckThread extends Thread{ @Override public void run() { // TODO Auto-generated method stub super.run(); isChecking=true; int[] location=new int[2]; int beforeY=0; int i; ExpandableHandler mHandler=new ExpandableHandler(Looper.getMainLooper()); while(true){ //exListView.getLocationOnScreen(location);  exListView.getLocationOnScreen(location); if(beforeY==location[1]){ //控件中止運動,線程關閉 isChecking=false; return; } else{ beforeY=location[1]; for(i=groupData.size()-1; i>=0; i--){ if((Boolean)groupData.get(i).get("expanded")&&(Integer)groupData.get(i).get("location")+location[1]<=24){ Message msg=new Message(); msg.arg1=childData.get(i).size(); msg.arg2=i; msg.obj=groupData.get(i).get("groupName"); msg.what=VISIBILITY_VISIBLE; mHandler.sendMessage(msg); break; } } if(i<0){ Message msg=new Message(); msg.what=VISIBILITY_GONE; msg.obj=""; //必須加這一步,不然會有空指針異常  mHandler.sendMessage(msg); } } try { this.sleep(80); //sleep的時間不能太短!! } catch (InterruptedException e) { // TODO Auto-generated catch block  e.printStackTrace(); } } } }
複製代碼


在線程中判斷GroupItem是否有滑動出屏幕的,建立Message對象,綁定響應的groupItem的信息(position,Name,childCount,是否顯示Visibility),而後由Handler對象發出[sendMessage();] 注意sleep的時間不能太短,由於當手指再也不脫離屏幕而ListView 自由滑動時,因爲滑動的速度較慢,致使兩次檢測座標是結果同樣,從而意外的終止線程。

isChecking是boolean型數據,標記線程工做的狀態,避免在滑動的過程當中同時開闢多個子線程產生異常。

 

而後是MyHandler類:

複製代碼
public class ExpandableHandler extends Handler{ public ExpandableHandler(Looper looper){ super(looper); } @Override public void handleMessage(Message msg) { // TODO Auto-generated method stub super.handleMessage(msg); int listNum=msg.arg1; int visibility=msg.what; int groupPos=msg.arg2; String groupName=msg.obj.toString(); if(visibility==VISIBILITY_GONE){ titleGroupView.setVisibility(View.GONE); return; } else{ titleGroupView.setVisibility(View.VISIBLE); titleGroupName.setText(groupName); titleChildCount.setText(""+listNum); titleGroupView.setTag(groupPos); //給這個View控件設置一個標籤屬性,用於存放groupPosition /** * setText()中的參數不能使int型!! */ } } }
複製代碼

  建立MyHandler對象,在其中複寫handleMessage();方法,在該方法中設置懸浮控件的顯示效果(更新系統UI)

最後還須要實現一個效果,當懸浮控件出現時,點擊懸浮控件,會把該控件對應的groupItem關掉(collapseGroup),所以須要設置一個點擊監聽器:

複製代碼
titleGroupView.setOnClickListener(new OnClickListener(){ @Override public void onClick(View v) { // TODO Auto-generated method stub int groupPosition=(Integer)titleGroupView.getTag(); exListView.collapseGroup(groupPosition); new LocationCheckThread().start(); } });
複製代碼


注意這裏又一次啓動了座標檢測線程,是由於須要在調用collapseGroup以後對系統的UI作一次刷新,避免產生顯示異常。

 

以上就是主要須要修改的地方,而後附上整個Activity的代碼:

代碼太多摺疊起來了:

  View Code


最後附上效果圖:

 頭像重複並非代碼錯誤,我只是重複添加了幾個聯繫人,詳情展開代碼參考。

相關文章
相關標籤/搜索