(转)可勾选的ExpandableListView

  • 由于项目的保密性,暂时不公开源码,但是基于该博主源码,可以完美实现勾选的多级列表。
    下面是模拟最终效果图。


    最终效果图.png

加上checkbox,需要自己维护一个数据结构. 开发上要求点group list item的大多数区域是expand/collapse,仅点checkbox区域是勾选,而点child list item的任意区域都是勾选效果, 可以让列表setOnChildClickListener()。
注意ExpandableListAdapter的isChildSelectable()方法一定返回true。
编辑自http://www.allenj.net/?p=3644. 给出干货,该博主好像404了,还好保留了code精华,与他人分享。


下面是code演示。
EListAdapter.java

package com.example.checkableexpandablelistview;  
  
import java.util.ArrayList;  
  
import android.content.Context;  
import android.view.LayoutInflater;  
import android.view.View;  
import android.view.View.OnClickListener;  
import android.view.ViewGroup;  
import android.widget.BaseExpandableListAdapter;  
import android.widget.CheckBox;  
import android.widget.ExpandableListView;  
import android.widget.TextView;  
  
import com.example.aexpandablelist.R;  
  
public class EListAdapter extends BaseExpandableListAdapter implements ExpandableListView.OnChildClickListener {  
      
    private Context context;  
    private ArrayList<Group> groups;  
   
    public EListAdapter(Context context, ArrayList<Group> groups) {  
        this.context = context;  
        this.groups = groups;  
    }  
   
    public Object getChild(int groupPosition, int childPosition) {  
        return groups.get(groupPosition).getChildItem(childPosition);  
    }  
   
    public long getChildId(int groupPosition, int childPosition) {  
        return childPosition;  
    }  
   
    public int getChildrenCount(int groupPosition) {  
        return groups.get(groupPosition).getChildrenCount();  
    }  
   
    public Object getGroup(int groupPosition) {  
        return groups.get(groupPosition);  
    }  
   
    public int getGroupCount() {  
        return groups.size();  
    }  
   
    public long getGroupId(int groupPosition) {  
        return groupPosition;  
    }  
   
    public boolean hasStableIds() {  
        return true;  
    }  
   
    public boolean isChildSelectable(int groupPosition, int childPosition) {  
        return true;  
    }  
   
    /** 設定 Group 資料 */  
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {  
        Group group = (Group) getGroup(groupPosition);  
   
        if (convertView == null) {  
            LayoutInflater infalInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
            convertView = infalInflater.inflate(R.layout.group_layout, null);  
        }  
   
        TextView tv = (TextView) convertView.findViewById(R.id.tvGroup);  
        tv.setText(group.getTitle());  
   
        // 重新產生 CheckBox 時,將存起來的 isChecked 狀態重新設定  
        CheckBox checkBox = (CheckBox) convertView.findViewById(R.id.chbGroup);  
        checkBox.setChecked(group.getChecked());  
   
        // 點擊 CheckBox 時,將狀態存起來  
        checkBox.setOnClickListener(new Group_CheckBox_Click(groupPosition));  
   
        return convertView;  
    }  
   
    /** 勾選 Group CheckBox 時,存 Group CheckBox 的狀態,以及改變 Child CheckBox 的狀態 */  
    class Group_CheckBox_Click implements OnClickListener {  
        private int groupPosition;  
   
        Group_CheckBox_Click(int groupPosition) {  
            this.groupPosition = groupPosition;  
        }  
   
        public void onClick(View v) {  
            groups.get(groupPosition).toggle();  
   
            // 將 Children 的 isChecked 全面設成跟 Group 一樣  
            int childrenCount = groups.get(groupPosition).getChildrenCount();  
            boolean groupIsChecked = groups.get(groupPosition).getChecked();  
            for (int i = 0; i < childrenCount; i++)  
                groups.get(groupPosition).getChildItem(i).setChecked(groupIsChecked);  
   
            // 注意,一定要通知 ExpandableListView 資料已經改變,ExpandableListView 會重新產生畫面  
            notifyDataSetChanged();  
        }  
    }  
   
    /** 設定 Children 資料 */  
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {  
        Child child = groups.get(groupPosition).getChildItem(childPosition);  
   
        if (convertView == null) {  
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);  
            convertView = inflater.inflate(R.layout.child_layout, null);  
        }  
   
        TextView tv = (TextView) convertView.findViewById(R.id.tvChild);  
        tv.setText(child.getFullname());  
   
        // 重新產生 CheckBox 時,將存起來的 isChecked 狀態重新設定  
        CheckBox checkBox = (CheckBox) convertView.findViewById(R.id.chbChild);  
        checkBox.setChecked(child.getChecked());  
   
        // 點擊 CheckBox 時,將狀態存起來  
        checkBox.setOnClickListener(new Child_CheckBox_Click(groupPosition, childPosition));  
   
        return convertView;  
    }  
   
    /** 勾選 Child CheckBox 時,存 Child CheckBox 的狀態 */  
    class Child_CheckBox_Click implements OnClickListener {  
        private int groupPosition;  
        private int childPosition;  
   
        Child_CheckBox_Click(int groupPosition, int childPosition) {  
            this.groupPosition = groupPosition;  
            this.childPosition = childPosition;  
        }  
   
        public void onClick(View v) {  
            handleClick(childPosition, groupPosition);  
        }  
    }  
      
    public void handleClick(int childPosition, int groupPosition) {  
        groups.get(groupPosition).getChildItem(childPosition).toggle();  
          
        // 檢查 Child CheckBox 是否有全部勾選,以控制 Group CheckBox  
        int childrenCount = groups.get(groupPosition).getChildrenCount();  
        boolean childrenAllIsChecked = true;  
        for (int i = 0; i < childrenCount; i++) {  
            if (!groups.get(groupPosition).getChildItem(i).getChecked())  
                childrenAllIsChecked = false;  
        }  
  
        groups.get(groupPosition).setChecked(childrenAllIsChecked);  
  
        // 注意,一定要通知 ExpandableListView 資料已經改變,ExpandableListView 會重新產生畫面  
        notifyDataSetChanged();  
    }  
  
    @Override  
    public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {  
        handleClick(childPosition, groupPosition);  
        return true;  
    }  
      
}  

Group.java

package com.example.checkableexpandablelistview;  
  
import java.util.ArrayList;  
  
public class Group {  
    private String id;  
    private String title;  
    private ArrayList<Child> children;  
    private boolean isChecked;  
   
    public Group(String id, String title) {  
        this.title = title;  
        children = new ArrayList<Child>();  
        this.isChecked = false;  
    }  
   
    public void setChecked(boolean isChecked) {  
        this.isChecked = isChecked;  
    }  
   
    public void toggle() {  
        this.isChecked = !this.isChecked;  
    }  
   
    public boolean getChecked() {  
        return this.isChecked;  
    }  
   
    public String getId() {  
        return id;  
    }  
   
    public String getTitle() {  
        return title;  
    }  
   
    public void addChildrenItem(Child child) {  
        children.add(child);  
    }  
   
    public int getChildrenCount() {  
        return children.size();  
    }  
   
    public Child getChildItem(int index) {  
        return children.get(index);  
    }  
}  

Child.java

package com.example.checkableexpandablelistview;  
  
public class Child {  
    private String userid;  
    private String fullname;  
    private String username;  
    private boolean isChecked;  
   
    public Child(String userid, String fullname, String username) {  
        this.userid = userid;  
        this.fullname = fullname;  
        this.username = username;  
        this.isChecked = false;  
    }  
   
    public void setChecked(boolean isChecked) {  
        this.isChecked = isChecked;  
    }  
   
    public void toggle() {  
        this.isChecked = !this.isChecked;  
    }  
   
    public boolean getChecked() {  
        return this.isChecked;  
    }  
   
    public String getUserid() {  
        return userid;  
    }  
   
    public String getFullname() {  
        return fullname;  
    }  
   
    public String getUsername() {  
        return username;  
    }  
}  

MainActivity.java

package com.example.checkableexpandablelistview;  
  
import java.util.ArrayList;  
  
import org.json.JSONArray;  
import org.json.JSONException;  
import org.json.JSONObject;  
  
import android.app.Activity;  
import android.os.Bundle;  
import android.util.Log;  
import android.view.Menu;  
import android.widget.ExpandableListView;  
  
import com.example.aexpandablelist.R;  
  
  
public class MainActivity extends Activity {  
  
    ArrayList<Group> groups;  
    ExpandableListView listView;  
    EListAdapter adapter;  
      
    @Override  
    public void onCreate(Bundle savedInstanceState) {  
        super.onCreate(savedInstanceState);  
        setContentView(R.layout.activity_main);  
  
        groups = new ArrayList<Group>();  
        getJSONObject();  
        listView = (ExpandableListView) findViewById(R.id.listView);  
        adapter = new EListAdapter(this, groups);  
        listView.setAdapter(adapter);  
        listView.setOnChildClickListener(adapter);  
    }  
  
    /** 解悉 JSON 字串 */  
    private void getJSONObject() {  
        String jsonStr = "{'CommunityUsersResult':[{'CommunityUsersList':[{'fullname':'a111','userid':11,'username':'a1'}"  
                + ",{'fullname':'b222','userid':12,'username':'b2'}],'id':1,'title':'人事部'},{'CommunityUsersList':[{'fullname':"  
                + "'c333','userid':13,'username':'c3'},{'fullname':'d444','userid':14,'username':'d4'},{'fullname':'e555','userid':"  
                + "15,'username':'e5'}],'id':2,'title':'開發部'}]}";  
  
        try {  
            JSONObject CommunityUsersResultObj = new JSONObject(jsonStr);  
            JSONArray groupList = CommunityUsersResultObj.getJSONArray("CommunityUsersResult");  
  
            for (int i = 0; i < groupList.length(); i++) {  
                JSONObject groupObj = (JSONObject) groupList.get(i);  
                Group group = new Group(groupObj.getString("id"), groupObj.getString("title"));  
                JSONArray childrenList = groupObj.getJSONArray("CommunityUsersList");  
  
                for (int j = 0; j < childrenList.length(); j++) {  
                    JSONObject childObj = (JSONObject) childrenList.get(j);  
                    Child child = new Child(childObj.getString("userid"), childObj.getString("fullname"),  
                            childObj.getString("username"));  
                    group.addChildrenItem(child);  
                }  
  
                groups.add(group);  
            }  
        } catch (JSONException e) {  
            Log.d("allenj", e.toString());  
        }  
    }  
  
    @Override  
    public boolean onCreateOptionsMenu(Menu menu) {  
        getMenuInflater().inflate(R.menu.activity_main, menu);  
        return true;  
    }  
}  

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" >  
  
    <ExpandableListView  
        android:id="@+id/listView"  
        android:layout_width="match_parent"  
        android:layout_height="wrap_content" >  
    </ExpandableListView>  
  
</RelativeLayout> 

group_layout.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="match_parent"  
    android:orientation="horizontal" >  
  
    <TextView  
        android:id="@+id/tvGroup"  
        android:layout_width="280dp"  
        android:layout_height="45dip"  
        android:gravity="center_vertical|left"  
        android:paddingLeft="45dp"  
        android:textSize="17dip"  
        android:textStyle="bold" >  
    </TextView>  
  
    <CheckBox  
        android:id="@+id/chbGroup"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:focusable="false" />  
  
</LinearLayout>  

child_layout.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="match_parent"  
    android:orientation="horizontal" >  
  
    <TextView  
        android:id="@+id/tvChild"  
        android:layout_width="280dp"  
        android:layout_height="45dip"  
        android:gravity="center_vertical"  
        android:paddingLeft="45dp"  
        android:textSize="17dip"  
        android:textStyle="bold" >  
    </TextView>  
  
    <CheckBox  
        android:id="@+id/chbChild"  
        android:layout_width="wrap_content"  
        android:layout_height="wrap_content"  
        android:clickable="false"  
        android:focusable="false" />  
  
</LinearLayout> 
  • Google于2013开源了:Gson,更快更轻松解析Json的利器,用过几次表示比Java自带的JsonObject要好得多。
  • Android Studio的用户可以安装插件GsonFormat。一键构造实体类。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,219评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,363评论 1 293
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,933评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,020评论 0 206
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,400评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,640评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,896评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,597评论 0 199
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,327评论 1 244
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,581评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,072评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,399评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,054评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,083评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,849评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,672评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,585评论 2 270

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 170,569评论 25 707
  • /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home...
    禅与计算机程序设计艺术阅读 3,760评论 2 8
  • 当面对两个选择时抛硬币总能奏效 并不是因为它能给出对的答案 而是在你把它抛向空中的那一秒 你突然知道你希望它是什么...
    繁岛阅读 238评论 0 0
  • 麦蓝M阅读 1,022评论 16 55
  • 作者:全冠晶食用菌调理\潘映航 今天天气很好 阳光明媚,送完女儿上幼儿园,回到家里面,小女儿还没有起床。想抓紧时间...
    短视频制作启航阅读 456评论 0 0