前兩天電腦壞掉了,導致這一兩天沒有更新。發現之前寫的幾篇博客瀏覽量已經接近1000,有種受寵若驚的感覺,寫這個系列的博客主要是假期想干一些平時沒有時間做的興趣愛好,並把自己的一點一滴記錄下來。這一段時間由於各種原因以致於更新速度很慢,之前的打算是先把QQ2013的Activity的布局搭建出來,然后再設計其中的核心代碼,如網絡編程,搭建一個小型服務器,進而實現QQ基本的功能,從現在的進度來看這個計划可能完不成了。不過我仍然會繼續做這個項目,相信就算不會徹底完成也能夠搭出漂亮的QQ框架來。
進入正題。上一節結束時提到QQ聯系人列表界面還有一個效果沒有實現,這個效果是當聯系人一級條目滑動到屏幕的頂端時,該一級條目就會懸停在屏幕的頂端,當往下拉時又會隨着ExpandableListView的Item正常移動,如官方效果圖圖所示:
注意觀察“我的好友”那一欄,本節實現的即上述效果。
首先講一下思路:
1、不能使用onScroll()方法,這一方法會在ExpandableListView展開的時候調用,至於其調用的周期並不是太明確,但可以確定的是onScroll並不能直接實現上述效果。
2、這里應該獲得ScrollView的實例(findViewById方法),然后調用setOnTouch()方法,在setOnTouch()方法中進一步進行設置。
3、這里的基本思路是:當拖動屏幕時,setOnTouch方法被調用,在該方法中開辟一個子線程,這個子線程用於檢測屏幕中的ScrollView對象是否仍在運動,直到不再拖動屏幕或者屏幕上的控件在慣性的作用下運動至停止時,該線程才會自動終結。在該線程中判斷一級條目(這里用groupItem表示)是否向上滾出了屏幕。
4、在該Activity的主布局文件中ScrollView的后面添加一個LinearLayout控件,該控件的布局和GroupItem的布局文件的內容是一樣的,將其位置設置為至於屏幕(父控件)的頂端,平時設置其Visibility為View.GONE 不可見不可操作狀態,當某一個已經展開(expanded)的groupItem滾出了屏幕時,就將該隱藏的控件設置為可見,並將其內容設置為和剛剛滾出屏幕的groupItem的內容一摸一樣。若所有的groupItem都為滑出屏幕,那么將該控件重新設置為不可見(GONE);
5、本節中遇見的最大的障礙就是我沒有找到獲得每一個groupItem在屏幕上的實時位置的方法,所以這里對原來的groupData對象做了修改,在HashMap對象中新添加了一個鍵值對 key="location" value= 該groupItem在ExpandableListView中的相對縱坐標。在后在上面創建的坐標檢測子線程中獲得groupData中存放的groupItem的相對坐標再加上ExpandableListView的絕對縱坐標,然后判斷groupItem是否滑出了屏幕,進而對“隱藏控件”執行相應的操作。注意這里應該先判斷groupPosition靠后的groupItem的坐標,懸停的控件顯示的是最后滑出屏幕的groupItem的信息。
6、本節將Activity的標題去掉了,去掉的方法是在onCreate()方法中 setContentView之前(必須是之前)調用requestWindowFeature(Window.FEATURE_NO_TITLE);
7、本節中需要在子線程中修改MainActivity 安卓UI,而直接修改是不允許的,所以需要使用一個自定義的Handler對象,調用父類的構造方法
public MyHandler(Looper looper){
super(looper);
}
並復寫handleMessage()方法修改MainActivity的UI。
關於Android消息處理機制我目前不想再多講,做開發的話夠用就行,沒必要掌握的太徹底,如果想對Handler有一個比較好的了解,可以參考:
http://blog.csdn.net/zkdemon/article/details/6914161
8、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的代碼:
代碼太多折疊起來了:

package com.dragon.android_qq; import android.location.Location; import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.os.Message; import android.app.Activity; import android.content.Context; import android.view.DragEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MotionEvent; import android.view.View; import android.view.View.OnDragListener; import android.view.View.OnTouchListener; import android.view.ViewGroup; import android.view.View.OnClickListener; import android.view.ViewGroup.LayoutParams; import android.view.Window; import android.view.WindowManager; import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.BaseAdapter; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.ExpandableListView.OnGroupClickListener; import android.widget.ImageButton; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import java.util.*; import com.dragon.persondata.ContactsInfo; public class ContactsListExpandable extends Activity { private int height=0; private int GROUP_HEIGHT=0; private int CHILD_HEIGHT=52; private int VISIBILITY_GONE=0; private int VISIBILITY_VISIBLE=1; int[] photoRes={R.drawable.contact_0,R.drawable.contact_1,R.drawable.contact_2,R.drawable.contact_3}; String[] groupFrom={"groupImage","groupName","childCount"}; int[] groupTo={R.id.groupImage,R.id.groupName,R.id.childCount}; String[] childFrom={"userImage","userName","userSign"}; int[] childTo={R.id.ct_photo,R.id.ct_name,R.id.ct_sign}; ArrayList<HashMap<String,Object>> groupData=null; ArrayList<ArrayList<HashMap<String,Object>>> childData=null; ArrayList<HashMap<String,Object>> groupItenformation=null; //用於存放groupItem的相對位置 int[] groupIndicator={R.drawable.toright,R.drawable.todown}; ExpandableListView exListView=null; ListView listViewTop=null,listViewDown=null; ScrollView qqScroll=null; LinearLayout titleGroupView=null; ImageView titleImage=null; TextView titleGroupName=null; TextView titleChildCount=null; MyExpandableListViewAdapter adapter=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.layout_qqlist_expandable); groupData=new ArrayList<HashMap<String,Object>>(); childData=new ArrayList<ArrayList<HashMap<String,Object>>> (); //創建groupLocation對象 //groupItemInformation=new ArrayList<HashMap<String,Object>>(); ContactsInfo user1=new ContactsInfo("暗夜之殤","總有一天會尋找到自己的幸福",R.drawable.contact_0,"我的好友"); ContactsInfo user2=new ContactsInfo("街角的幸福","有些事終於想開了",R.drawable.contact_1,"我的好友"); ContactsInfo user3=new ContactsInfo("憤怒的小胖","誰再叫我小胖我跟誰急!",R.drawable.contact_3,"朋友"); ContactsInfo user4=new ContactsInfo("放羊的星星","What ever",R.drawable.contact_2,"陌生人"); ContactsInfo user5=new ContactsInfo("都市麗人","我要我的完美",R.drawable.contact_4,"我的好友"); addUser(user1); addUser(user2); addUser(user3); addUser(user4); addUser(user5); addUser(user1); addUser(user2); addUser(user3); addUser(user4); addUser(user5); addUser(user3); addUser(user4); addUser(user5); addUser(user3); addUser(user4); addUser(user5); //不能用HashMap的實參賦給Map形參,只能new一個HashMap對象賦給Map的引用! exListView=(ExpandableListView)findViewById(R.id.qq_list); adapter=new MyExpandableListViewAdapter(this,groupData,R.layout.layout_qqlist_group,groupFrom,groupTo,childData,R.layout.layout_qqlist_child,childFrom,childTo ); exListView.setAdapter(adapter); exListView.setGroupIndicator(null); qqScroll=(ScrollView)findViewById(R.id.qqlist_scroll); View exGroupListItem= exListView.getExpandableListAdapter().getGroupView(0,false, null, exListView ); exGroupListItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)); exGroupListItem.measure(0, 0); GROUP_HEIGHT=exGroupListItem.getMeasuredHeight(); View exChildListItem=exListView.getExpandableListAdapter().getChildView(0, 0, false, null, exListView); exChildListItem.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,LayoutParams.WRAP_CONTENT)); exChildListItem.measure(0, 0); CHILD_HEIGHT=exChildListItem.getMeasuredHeight(); ViewGroup.LayoutParams params= exListView.getLayoutParams(); height=groupData.size()*GROUP_HEIGHT-2; params.height=height; exListView.setLayoutParams(params); for(int i=0; i<groupData.size() ;i++){ groupData.get(i).put("location",i*GROUP_HEIGHT); } /* for(int i=0; i<childData.size(); i++){ HashMap<String,Object> map1=new HashMap<String,Object>(); HashMap<String,Object> map2=new HashMap<String,Object>(); map1.put("location", i*GROUP_HEIGHT); map1.put("expanded", false); groupItemInformation.add(map1); } */ listViewTop=(ListView)findViewById(R.id.qqlist_classify); ArrayList<HashMap<String,Object>> listTop=new ArrayList<HashMap<String,Object>>(); HashMap<String,Object> mapTop1=new HashMap<String,Object>(); HashMap<String,Object> mapTop2=new HashMap<String,Object>(); HashMap<String,Object> mapTop3=new HashMap<String,Object>(); mapTop1.put("listTopImage",R.drawable.address_list); mapTop1.put("listTopText", "通訊錄"); mapTop2.put("listTopImage",R.drawable.chat_flock); mapTop2.put("listTopText", "群"); mapTop3.put("listTopImage",R.drawable.forum); mapTop3.put("listTopText", "討論組"); listTop.add(mapTop1); listTop.add(mapTop2); listTop.add(mapTop3); MyListViewAdapter adapterTop=new MyListViewAdapter(this, listTop, R.layout.qqlist_classify_item, new String[]{"listTopImage","listTopText"}, new int[]{R.id.qqlist_classify_image,R.id.qqlist_classify_name}); listViewTop.setAdapter(adapterTop); ViewGroup.LayoutParams paramsTop=listViewTop.getLayoutParams(); View listItem=listViewTop.getAdapter().getView(0, null, listViewTop); listItem.measure(0, 0); paramsTop.height=3*listItem.getMeasuredHeight()-2; //ListView屬於一種ViewGroup類型 listViewTop.setLayoutParams(paramsTop); /** * 創建一個String數組對象不能直接寫String[]{} 必須在前面加上new,即寫成 new String[]{"listTopImage","listTopText"} * */ listViewDown=(ListView)findViewById(R.id.qqlist_classify_down); ArrayList<HashMap<String,Object>> listDown=new ArrayList<HashMap<String,Object>>(); HashMap<String,Object> mapDown1=new HashMap<String,Object>(); mapDown1.put("listTopImage",R.drawable.life_service); mapDown1.put("listTopText", "生活服務"); listDown.add(mapDown1); listTop.add(mapTop3); MyListViewAdapter adapterDown=new MyListViewAdapter(this, listDown, R.layout.qqlist_classify_item, new String[]{"listTopImage","listTopText"}, new int[]{R.id.qqlist_classify_image,R.id.qqlist_classify_name}); listViewDown.setAdapter(adapterDown); titleGroupView=(LinearLayout)findViewById(R.id.titleGroupView); titleGroupName=(TextView)findViewById(R.id.title_groupName); titleChildCount=(TextView)findViewById(R.id.title_childCount); 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(); } }); 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(); } } } } 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型!! */ } } } protected void addUser(ContactsInfo user) { int i; for(i=0; i< groupData.size(); i++){ if(groupData.get(i).get("groupName").toString().equals(user.groupInfo)){ break; } } if(i>=groupData.size()){ HashMap<String,Object> map=new HashMap<String,Object>(); map.put("groupImage", groupIndicator); map.put("groupName",user.groupInfo ); map.put("childCount", 0); groupData.add(map); ArrayList<HashMap<String,Object>> list=new ArrayList<HashMap<String,Object>>(); childData.add(list); } HashMap<String,Object> userData=new HashMap<String,Object>(); userData.put("userImage",user.userImage ); userData.put("userName", user.userName); userData.put("userSign", user.userSign); childData.get(i).add(userData); Integer count=(Integer)groupData.get(i).get("childCount")+1; groupData.get(i).put("childCount", count); groupData.get(i).put("expanded", false); } /** * ExpandableListView對應的適配器 * @author DragonGN * */ public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{ private Context context=null; private ArrayList<HashMap<String,Object>> groupData=null; int groupLayout=0; private String[] groupFrom=null; private int[] groupTo=null; private ArrayList<ArrayList<HashMap<String,Object>>> childData=null; int childLayout=0; private String[] childFrom=null; private int[] childTo=null; public MyExpandableListViewAdapter(Context context, ArrayList<HashMap<String, Object>> groupData, int groupLayout, String[] groupFrom, int[] groupTo, ArrayList<ArrayList<HashMap<String, Object>>> childData, int childLayout, String[] childFrom, int[] childTo) { super(); this.context = context; this.groupData = groupData; this.groupLayout = groupLayout; this.groupFrom = groupFrom; this.groupTo = groupTo; this.childData = childData; this.childLayout = childLayout; this.childFrom = childFrom; this.childTo = childTo; } @Override public Object getChild(int arg0, int arg1) { // TODO Auto-generated method stub return null; } /** * position與id一樣,都是從0開始計數的, * 這里返回的id也是從0開始計數的 */ @Override public long getChildId(int groupPosition, int childPosition) { // TODO Auto-generated method stub long id=0; for(int i=0;i<groupPosition; i++){ id+=childData.size(); } id+=childPosition; return id; } /**ChildViewHolder內部類**/ class ChildViewHolder{ ImageButton userImage=null; TextView userName=null; TextView userSign=null; } /**頭像點擊事件監聽類**/ class ImageClickListener implements OnClickListener{ ChildViewHolder holder=null; public ImageClickListener(ChildViewHolder holder){ this.holder=holder; } @Override public void onClick(View v) { // TODO Auto-generated method stub Toast.makeText(context, holder.userName.getText()+" is clicked", Toast.LENGTH_SHORT).show(); } } @Override public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) { // TODO Auto-generated method stub /** * 這里isLastChild目前沒用到,如果出現異常再說 */ ChildViewHolder holder=null; if(convertView==null){ convertView= LayoutInflater.from(context).inflate(childLayout,null); //感覺這里需要把root設置成ViewGroup 對象 /** * ERROR!!這里不能把null換成parent,否則會出現異常退出,原因不太確定,可能是inflate方法獲得的這個item的View * 並不屬於某個控件組,所以使用默認值null即可 */ holder=new ChildViewHolder(); holder.userImage=(ImageButton)convertView.findViewById(childTo[0]); holder.userName=(TextView)convertView.findViewById(childTo[1]); holder.userSign=(TextView)convertView.findViewById(childTo[2]); convertView.setTag(holder); } else{ holder=(ChildViewHolder)convertView.getTag(); } holder.userImage.setBackgroundResource((Integer)(childData.get(groupPosition).get(childPosition).get(childFrom[0]))); holder.userName.setText(childData.get(groupPosition).get(childPosition).get(childFrom[1]).toString()); holder.userSign.setText(childData.get(groupPosition).get(childPosition).get(childFrom[2]).toString()); holder.userImage.setOnClickListener(new ImageClickListener(holder)); return convertView; } @Override public int getChildrenCount(int groupPosition) { // TODO Auto-generated method stub return childData.get(groupPosition).size(); } @Override public Object getGroup(int groupPosition) { // TODO Auto-generated method stub return null; } @Override public int getGroupCount() { // TODO Auto-generated method stub return groupData.size(); } @Override public long getGroupId(int groupPosition) { // TODO Auto-generated method stub return groupPosition; } class GroupViewHolder{ ImageView image=null; TextView groupName=null; TextView childCount=null; } @Override public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) { // TODO Auto-generated method stub GroupViewHolder holder=null; if(convertView==null){ convertView=LayoutInflater.from(context).inflate(groupLayout, null); holder=new GroupViewHolder(); holder.image=(ImageView)convertView.findViewById(groupTo[0]); holder.groupName=(TextView)convertView.findViewById(groupTo[1]); holder.childCount=(TextView)convertView.findViewById(groupTo[2]); convertView.setTag(holder); } else{ holder=(GroupViewHolder)convertView.getTag(); } int[] groupIndicator=(int[])groupData.get(groupPosition).get(groupFrom[0]); holder.image.setBackgroundResource(groupIndicator[isExpanded?1:0]); holder.groupName.setText(groupData.get(groupPosition).get(groupFrom[1]).toString()); holder.childCount.setText(groupData.get(groupPosition).get(groupFrom[2]).toString()); if(groupPosition==groupData.size()-1){ convertView.setBackgroundResource(R.drawable.list_lastitem_border); } else{ convertView.setBackgroundResource(R.drawable.list_item_border); } //else的情況也要考慮,否則在繪制時出現錯位現象 /** * 將剛剛創建的groupItem的相對坐標計算出來放在groupLocation中,這個是初始相對坐標 * 當點擊打開一級菜單和關閉一級菜單時重新更新每一個group的相對坐標 */ return convertView; /** * 不要在適配器中調用適配器的內部方法,不然會出現奇怪的異常 * */ } @Override public boolean hasStableIds() { // TODO Auto-generated method stub return false; } @Override public boolean isChildSelectable(int groupPosition, int childPosition) { // TODO Auto-generated method stub return true; } /** * 在設置ExpandableListView的寬度的時候,要注意每次點擊展開或者關閉時,各個Group和所要顯示的Item都會重繪 * 因此在每次繪制完畢之后都需要對height進行更新 */ @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 ); } } private int floatToInt(double f){ if(f>0){ return (int)(f+0.5); } else{ return (int)(f-0.5); } } } /** * 這個是ListView的適配器 * @author DragonGN * */ public class MyListViewAdapter extends BaseAdapter{ Context context=null; ArrayList<HashMap<String,Object>> list=null; int layout; String[] from=null; int[] to; public MyListViewAdapter(Context context, ArrayList<HashMap<String, Object>> list, int layout, String[] from, int[] to) { super(); this.context = context; this.list = list; this.layout = layout; this.from = from; this.to = to; } @Override public int getCount() { // TODO Auto-generated method stub return list.size(); } @Override public Object getItem(int arg0) { // TODO Auto-generated method stub return null; } @Override public long getItemId(int arg0) { // TODO Auto-generated method stub return arg0; } @Override public View getView(int position, View convertView, ViewGroup parent) { // TODO Auto-generated method stub View listItemView= LayoutInflater.from(context).inflate(layout, null); ImageView image=(ImageView)listItemView.findViewById(to[0]); TextView text=(TextView)listItemView.findViewById(to[1]); image.setImageResource((Integer)list.get(position).get(from[0])); text.setText((String)list.get(position).get(from[1])); return listItemView; } } }
最后附上效果圖:
頭像重復並不是代碼錯誤,我只是重復添加了幾個聯系人,詳情展開代碼參考。