Android开发系列(九) ExpandableListView进阶使用——QQ联系人列表(升级版)

  本节继续讲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像素使分割线消失。

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。