本節繼續講ExpandableListView的使用,與系列(六)中類似,這里需要寫一個自定義的適配器類繼承BaseExpandableListAdapter, 並且對QQ聯系人列表進行升級,使之具有對聯系人分類的功能,即可以分成"我的好友","朋友","陌生人"等等類別。如果對第六節的內容給比較熟悉,可以完全根據那個思路構造出自己的適配器類,實際上我在寫這節內容之前並沒有參考網上的資料慢慢摸索出來的。
首先是布局文件,
activity_main.xml 主布局文件
<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:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" android:background="#00aaff" tools:context=".MainActivity" > <TextView android:id="@+id/myText" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="聯系人" android:textSize="7pt" android:layout_centerHorizontal="true" android:textColor="#ffffff" android:textStyle="bold" /> <ExpandableListView android:id="@+id/qq_list" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@id/myText" android:divider="#888888" android:dividerHeight="0.15px"/> </RelativeLayout>
一級條目的布局文件 layout_group.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingLeft="5dip" android:orientation="horizontal" android:background="#eeeeee"> <ImageView android:id="@+id/groupImage" android:layout_width="match_parent" android:layout_height="15dip" android:layout_weight="1.8" android:layout_gravity="center" /> <TextView android:id="@+id/groupName" android:layout_width="match_parent" android:layout_height="30dip" android:layout_weight="1" android:paddingLeft="15dip" android:paddingTop="5dip" android:textSize="7pt" android:text="fdg" /> <TextView android:id="@+id/childCount" android:layout_width="match_parent" android:layout_height="30dip" android:layout_weight="1" android:gravity="right" android:paddingRight="10dip" android:paddingTop="5dip" android:text="ewsf"/> </LinearLayout>
二級條目的布局文件layout_child.xml
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="#efefef" > <!-- LinerLayout有比較奇怪的性質:當布局中的控件可以超出布局規定的大小 ,所以這里一行的行寬改成由內部的幾個控件 控制,而LinerLayout的layout_height改成wrap_content .. --> <ImageButton android:id="@+id/ct_photo" android:layout_height="70dip" android:layout_width="70dip" android:layout_margin="5dip"/> <TextView android:id="@+id/ct_name" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dip" android:layout_toRightOf="@id/ct_photo" android:layout_alignTop="@id/ct_photo" android:text="為你我受冷風吹" android:textSize="8pt" android:textStyle="bold" android:maxLength="7"/> <TextView android:id="@+id/ct_sign" android:layout_width="wrap_content" android:layout_height="wrap_content" android:padding="5dip" android:layout_toRightOf="@id/ct_photo" android:layout_alignBottom="@id/ct_photo" android:text="為什么受傷的總是我" android:textColor="#888888"/> <!-- 注意不是layout_padding --> </RelativeLayout>
下面就是自定義的適配器類,為了和Simple適配器參數相同,這里采用了類似的構造函數。
package com.example.android_baseexpandablelistview; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.widget.*; import java.util.*; public class MyAdapter 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 MyAdapter(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()); 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; } }
由上一節的內容可知為適配器添加數據非常麻煩,因此這里創建了一個 QQ用戶類,用來存放一個QQ用戶的各種信息,然后在MainActivity中設計了一個將新QQ用戶對象添加進List數據鏈表中的方法,見下面:
package com.qqlist.contactor; public class UserInfo { public String userName=null; public String userSign=null; public int userImage=0; public String groupInfo=null; public UserInfo(String userName, String userSign, int userImage, String groupInfo) { super(); this.userName = userName; this.userSign = userSign; this.userImage = userImage; this.groupInfo = groupInfo; } }
然后是MainActivity
package com.example.android_baseexpandablelistview; import android.os.Bundle; import android.app.Activity; import android.view.Menu; import android.widget.ExpandableListView; import java.util.*; import com.qqlist.contactor.UserInfo; public class MainActivity extends Activity { 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; int[] groupIndicator={R.drawable.toright,R.drawable.todown}; ExpandableListView list=null; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); groupData=new ArrayList<HashMap<String,Object>>(); childData=new ArrayList<ArrayList<HashMap<String,Object>>> (); UserInfo user1=new UserInfo("暗夜之殤","總有一天會尋找到自己的幸福",R.drawable.contact_0,"我的好友"); UserInfo user2=new UserInfo("街角的幸福","有些事終於想開了",R.drawable.contact_1,"我的好友"); UserInfo user3=new UserInfo("憤怒的小胖","誰再叫我小胖我跟誰急!",R.drawable.contact_3,"朋友"); UserInfo user4=new UserInfo("放羊的星星","What ever",R.drawable.contact_2,"陌生人"); addUser(user1); addUser(user2); addUser(user3); addUser(user4); //不能用HashMap的實參賦給Map形參,只能new一個HashMap對象賦給Map的引用! MyAdapter adapter=new MyAdapter(this,groupData,R.layout.layout_group,groupFrom,groupTo,childData,R.layout.layout_child,childFrom,childTo ); list=(ExpandableListView)findViewById(R.id.qq_list); list.setAdapter(adapter); list.setGroupIndicator(null); } protected void addUser(UserInfo 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); } }
有些代碼是為了美化界面用的不必深究。
下面附上效果圖:
幾點總結:
1、在getChildView方法中,要使用inflate()方法,注意該方法的第二個參數一般為null,不用考慮為parent的情況
2、在適配器類中最好不要調用適配器的類方法,那些方法是系統在繪制View時使用的,亂用會導致異常
3、Java語法:可以使用new 方法創建一個普通類(HashMap)賦值給接口類(Map),但普通類對象不能作為實參傳遞給接口類形參。
4、如果想去掉ListView的分割線,必須同時設定
android:divider="#aaaaaa" <!--顏色-->
android:dividerHeight="0px"
前者可以讓分割線變成無邊框的矩形,然后調節成0像素使分割線消失。