ExpandableListView 基础知识
1. ExpandableListView 的总体概述
ExpandableListView 是 android 中可以实现下拉 list 的一个控件,是一个垂直滚动的心事两个级
别列表项手风琴试图,列表项是来自 ExpandableListViewaAdapter,组可以单独展开。
2.重要的方法:
expandGroup (int groupPos) ;//在分组列表视图中 展开一组,
setSelectedGroup (int groupPosition) ;//设置选择指定的组。
setSelectedChild (int groupPosition, int childPosition, boolean shouldExpandGroup);
getPackedPositionGroup (long packedPosition);//返回所选择的组
isGroupExpanded (int groupPosition);//判断此组是否展开
expandableListView.setDivider();这个是设定每个 Group 之间的分割线。
expandableListView.setGroupIndicator();这个是设定每个 Group 之前的那个图标。
expandableListView.collapseGroup(int group); 将第 group 组收起
3. 适配器的介绍
ExpandableListAdapter,一个接口,将基础数据链接到一个 ExpandableListView。 此接
口的实施将提供访问 Child 的数据(由组分类),并实例化的 Child 和 Group。适配器中常用的重
要方法:
getChildId (int groupPosition, int childPosition) 获取与在给定组给予孩子相关的数据。
getChildrenCount (int groupPosition) 返回在指定 Group 的 Child 数目
4. 属性和事件
1) 在 Android 中对子条目的点击事件是通过 onChildClick()来实现
2) 对组的点击事件是通过 onGroupClick()来实现的
Gson 框架:
它是谷歌推出的一个请求网络数据的一个框架,常用的用法如下:
1) GSON 的两个重要方法
在 GSON 的 API 中,提供了两个重要的方法:toJson()和 fromJson()方法。其中,toJson()方法
用来实现将 Java 对象转换为相应的 JSON 数据,fromJson()方法则用来实现将 JSON 数据转换为
相应的 Java 对象。
2) toJson()方法,toJson()方法用于将 Java 对象转换为相应的 JSON 数据,主要有以下几种形式:
String toJson(JsonElement jsonElement);
String toJson(Object src);
String toJson(Object src, Type typeOfSrc);
其中,方法(1.1)用于将 JsonElement 对象(可以是 JsonObject、JsonArray 等)转换成 JSON
数据;方法(1.2)用于将指定的 Object 对象序列化成相应的 JSON 数据;方法(3)用于将指定
的 Object 对象(可以包括泛型类型)序列化成相应的 JSON 数据。
3) 1.2 fromJson()方法
fromJson()方法用于将 JSON 数据转换为相应的 Java 对象,主要有以下几种形式:
(1)<T> T fromJson(JsonElement json, Class<T> classOfT);
(2)<T> T fromJson(JsonElement json, Type typeOfT);
(3)<T> T fromJson(JsonReader reader, Type typeOfT);
(5)<T> T fromJson(Reader reader, Type typeOfT);
(6)<T> T fromJson(String json, Class<T> classOfT);
(7)<T> T fromJson(String json, Type typeOfT);
以上的方法用于将不同形式的 JSON 数据解析成 Java 对象。
所以说 gson 是一个很好的请求网络数据的框架,既可以在服务器端生成一个 json 字符串,然后
客户端通过发送请求向服务器端,进行数据解析。
5. Picasso 框架
这也是本课程中涉及的一个第三方的框架,它主要是用于网络请求图片时的一种框架,它的
代码量少,自带缓存,是一个值得使用的框架。首先 Picasso 也是 Afinal 这个框架的一种,
Afinal 是一个 android 的 ioc,orm 框架,内置了四大模块功能:
FinalAcitivity,FinalBitmap,FinalDb,FinalHttp。通过 finalActivity,我们可以通过注解的方式进
行绑定 ui 和事件。通过 finalBitmap,我们可以方便的加载 bitmap 图片,而无需考虑 oom 等
问题。通过 finalDB 模块,我们一行代码就可以对 android 的 sqlite 数据库进行增删改查。通过
FinalHttp 模块,我们可以以 ajax 形式请求 http 数据。
ExpandableListView 和 ExpandableListActivity
如何获取 ExpandableListView对象
1.可以直接在xml布局中添加 ExpandableListView 在activity中通过id绑定控件
2.activity 继承 ExpandableListActivity 通过 getExpandableListView 方法获取
ExpandableListView的常用属性
android:groupIndicator="" 设置可扩展组图标提示
点击事件
//设置组点击事件 // melv.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() { @Override public boolean onGroupClick(ExpandableListView expandableListView, View view, int i, long l) { return false; } }); //设置字条目点击事件 melv.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView expandableListView, View view, int i, int i1, long l) { return false; } });
下面进入demo环节
1.实现手风琴效果 获取本地数据
效果展示

(1).设置xml布局
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <ExpandableListView android:id="@+id/elv_local" android:layout_width="match_parent" android:layout_height="match_parent" android:groupIndicator="@null" android:divider="@null" android:dividerHeight="10dp" > </ExpandableListView> </LinearLayout>
(2).获取本地数据
//获取列表组的数据 public static List<String> getGroupData(){ List<String> groupList = new ArrayList<>(); groupList.add("Android课程"); groupList.add("JAVA课程"); return groupList; } //获取字条目的数据 public static List<List<String>> getChildData(){ List<List<String>> childList = new ArrayList<>(); List<String> item1 = new ArrayList<>(); item1.add("activity生命周期"); item1.add("Android 属性动画"); childList.add(item1); List<String> item2 = new ArrayList<>(); item2.add("JAVA集合"); item2.add("JAVA 多态"); childList.add(item2); return childList; }
(3)配置适配器
package com.example.squeezebox.Adapter; import android.content.Context; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import java.util.List; /** * 适配器封装 */ public abstract class MyAdapter extends BaseExpandableListAdapter { Context context; LayoutInflater minflater; public MyAdapter(Context context) { this.context = context; minflater = LayoutInflater.from(context); } List<String> group; List<List<String>> child; public void addNewData(List<String> group,List<List<String>> child){ this.group = group; this.child = child; } //获取组的数量 @Override public int getGroupCount() { return group.size(); } //获取字条目的数量 @Override public int getChildrenCount(int i) { return child.get(i).size(); } //获取组的具体的内容 @Override public String getGroup(int i) { return group.get(i); } //获取字条目具体的内容 @Override public String getChild(int i, int i1) { return child.get(i).get(i1); } //获取组的id @Override public long getGroupId(int i) { return i; } //获取字条目的id @Override public long getChildId(int i, int i1) { return i1; } @Override public boolean hasStableIds() { return false; } //可扩展的字条目是否可以被点击 @Override public boolean isChildSelectable(int i, int i1) { return true; } //获取组的视图 @Override public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) { return MyGroupView(i,view); } //获取字条目的视图 @Override public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) { return MyGroupView(i,i1,view); } public abstract View MyGroupView(int i, View groupView); public abstract View MyGroupView(int i, int i1,View childView); }
package com.example.squeezebox.Adapter; import android.annotation.SuppressLint; import android.content.Context; import android.view.View; import android.widget.TextView; import com.example.squeezebox.R; /** * 本地数据适配器 */ public class LocalAdapter extends MyAdapter { public LocalAdapter(Context context) { super(context); } @Override public View MyGroupView(int i, View groupView) { View view = minflater.inflate(R.layout.group_layout,null); TextView textView = view.findViewById(R.id.group_txt); textView.setText(getGroup(i)); textView.setPadding(30,0,0,0); return view; } @Override public View MyGroupView(int i, int i1, View childView) { View view = minflater.inflate(R.layout.item_layout,null); TextView textView = view.findViewById(R.id.item_txt); textView.setTextColor(context.getResources().getColor(R.color.steelblue)); textView.setText(getChild(i,i1)); // textView.setPadding(30,0,0,0); return view; } }
(4)在UI线程中调用
public void loadLocal(){ LocalAdapter adapter = new LocalAdapter(this); adapter.addNewData(LocalData.getGroupData(),LocalData.getChildData()); melv.setAdapter(adapter); }
2.获取数据库数据 使用 CursorTreeAdapter 类
(1) 创建数据库并建表 以及 添加 查询方法
public class DBOpenHelper extends SQLiteOpenHelper { public DBOpenHelper(Context context) { super(context, "hejun.db", null, 1); } public DBOpenHelper(Context context, String name, SQLiteDatabase.CursorFactory factory, int version) { super(context, "hejun.db", null, 1); } @Override public void onCreate(SQLiteDatabase sqLiteDatabase) { sqLiteDatabase.execSQL("create table group1(_id integer primary key autoIncrement," + "kind text,type text )"); sqLiteDatabase.execSQL("create table child(_id integer primary key autoIncrement," + "kind text,name text,type text )"); } @Override public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) { } }
package com.example.squeezebox.DBOpenHelper; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; public class DBHelp { private DBOpenHelper dbOpenHelper; public DBHelp(Context context) { dbOpenHelper = new DBOpenHelper(context); } //向组视图添加数据 public void insertGroup(String kind, String type) { SQLiteDatabase sqLiteDatabase = dbOpenHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("kind",kind); values.put("type",type); sqLiteDatabase.insert("group1", null, values); } //查询组数据 public Cursor selectGroup() { SQLiteDatabase sqLiteDatabase = dbOpenHelper.getReadableDatabase(); return sqLiteDatabase.query("group1", null, null,null , null, null, null); } //向字条目添加数据 public void insertChild(String kind, String leaner,String type) { SQLiteDatabase sqLiteDatabase = dbOpenHelper.getWritableDatabase(); ContentValues values = new ContentValues(); values.put("kind", kind); values.put("name", leaner); values.put("type",type); sqLiteDatabase.insert("child", null, values); } //查询组数据 public Cursor selectChild(String type) { SQLiteDatabase sqLiteDatabase = dbOpenHelper.getReadableDatabase(); return sqLiteDatabase.query("child", null, "type=?", new String[]{type}, null, null, null); } }
(2)设置适配器
package com.example.squeezebox.Adapter; import android.content.Context; import android.database.Cursor; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.CursorTreeAdapter; import android.widget.TextView; import com.example.squeezebox.DBOpenHelper.DBHelp; import com.example.squeezebox.R; /** * 数据库适配器 * cursor group and child */ public class DBAdapter extends CursorTreeAdapter { /** * * @param cursor 组的游标 * @param context */ DBHelp dbHelp; public DBAdapter(Cursor cursor, Context context) { super(cursor, context); dbHelp = new DBHelp(context); } //获取字条目的游标 @Override protected Cursor getChildrenCursor(Cursor cursor) { return dbHelp.selectChild(cursor.getString(cursor.getColumnIndex("type"))); } //创建组的视图 @Override protected View newGroupView(Context context, Cursor cursor, boolean b, ViewGroup viewGroup) { View view = LayoutInflater.from(context).inflate(R.layout.group_layout,null); return view; } //绑定组的视图 @Override protected void bindGroupView(View view, Context context, Cursor cursor, boolean b) { TextView textView = view.findViewById(R.id.group_txt); textView.setText(cursor.getString(cursor.getColumnIndex("kind"))); } //创建字条目的视图 @Override protected View newChildView(Context context, Cursor cursor, boolean b, ViewGroup viewGroup) { return LayoutInflater.from(context).inflate(R.layout.item2_layout,null); } //绑定字条目的视图 @Override protected void bindChildView(View view, Context context, Cursor cursor, boolean b) { TextView textView1 = view.findViewById(R.id.item_txt2); TextView textView2 = view.findViewById(R.id.item_leaner); textView1.setText(cursor.getString(cursor.getColumnIndex("kind"))); textView2.setText(cursor.getString(cursor.getColumnIndex("name"))); } }
(4) 在UI线程调用,插入数据 并为 ExpandableListView 添加Adapter
public void loadDB(){ DBHelp db = new DBHelp(this); db.insertGroup("Android课程","android"); db.insertGroup("JAVA课程","java"); db.insertChild("activity生命周期","1564156","android"); db.insertChild("Android 属性动画","89798","android"); db.insertChild("JAVA集合","98789","java"); db.insertChild("JAVA 多态","7988","java"); Cursor cursor = db.selectGroup(); DBAdapter dbAdapter = new DBAdapter(cursor,this); melv.setAdapter(dbAdapter); }
3.获取网络数据实现手风琴效果
效果展示:
(1) 异步下载网络数据,并使用接口回调,传出数据
package com.example.squeezebox.util; import android.os.AsyncTask; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; public class HttpRequest { public static void request(String path,onResponseLister onResponseLister){ new MyAsycnTask(onResponseLister).execute(path); } static class MyAsycnTask extends AsyncTask<String,Void,String>{ private onResponseLister onResponseLister; public MyAsycnTask(onResponseLister onResponseLister) { this.onResponseLister = onResponseLister; } @Override protected String doInBackground(String... strings) { try { URL url = new URL(strings[0]); HttpURLConnection connection = (HttpURLConnection) url.openConnection(); connection.setRequestMethod("GET"); connection.setConnectTimeout(5000); if (connection.getResponseCode() == HttpURLConnection.HTTP_OK){ InputStream in = connection.getInputStream(); byte[] bytes = new byte[1024]; int len = 0; StringBuilder builder = new StringBuilder(); while ((len = in.read(bytes))!= -1){ String str = new String(bytes,0,len); builder.append(str); } return builder.toString(); } } catch (MalformedURLException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(String s) { super.onPostExecute(s); if (s != null){ onResponseLister.onResponseListerSuccess(s); }else { onResponseLister.onResponseListerSFail("网络请求错误"); } } } public interface onResponseLister{ void onResponseListerSuccess(String result); void onResponseListerSFail(String result); } }
(2) 解析json数据 创建两个实体类
public class BaseEntity<T> { public String msg; public int status; public T data; }
public class Cuesor { public String name; public String picSmall; public String learner; }
/** * 解析数据 */ public class Parser { public static BaseEntity<List<Cuesor>> parserData(String json){ Gson gson = new Gson(); return gson.fromJson(json,new TypeToken<BaseEntity<List<Cuesor>>>(){}.getType()); } }
(3)配置适配器
package com.example.squeezebox.Adapter; import android.content.Context; import android.database.Cursor; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.BaseExpandableListAdapter; import java.util.List; public abstract class MyBaseAdapter<T> extends BaseExpandableListAdapter { Context context; LayoutInflater layoutInflater; public MyBaseAdapter(Context context) { this.context = context; layoutInflater = LayoutInflater.from(context); } private List<String> group; private List<List<T>> child; public void addNewData(List<String> group, List<List<T>> child){ this.group =group; this.child =child; } @Override public int getGroupCount() { return group.size(); } @Override public int getChildrenCount(int i) { return child.get(i).size(); } @Override public String getGroup(int i) { return group.get(i); } @Override public T getChild(int i, int i1) { return child.get(i).get(i1); } @Override public long getGroupId(int i) { return i; } @Override public long getChildId(int i, int i1) { return i1; } @Override public boolean hasStableIds() { return false; } @Override public View getGroupView(int i, boolean b, View view, ViewGroup viewGroup) { return addGroupView(i,view); } @Override public View getChildView(int i, int i1, boolean b, View view, ViewGroup viewGroup) { return addChildView(i,i1,view); } @Override public boolean isChildSelectable(int i, int i1) { return true; } public abstract View addGroupView(int i ,View groupView); public abstract View addChildView(int i ,int i1,View groupView); }
package com.example.squeezebox.Adapter; import android.content.Context; import android.view.View; import android.widget.ImageView; import android.widget.TextView; import com.example.squeezebox.R; import com.example.squeezebox.util.Cuesor; import it.sephiroth.android.library.picasso.Picasso; public class NetAdapter extends MyBaseAdapter<Cuesor> { public NetAdapter(Context context) { super(context); } @Override public View addGroupView(int i, View groupView) { TextView textView = new TextView(context); textView.setText(getGroup(i)); return textView; } @Override public View addChildView(int i, int i1, View groupView) { View view = layoutInflater.inflate(R.layout.net_item,null); ImageView imageView =view.findViewById(R.id.net_img); TextView textView1 = view.findViewById(R.id.net_txt1); TextView textView2 = view.findViewById(R.id.net_txt2); Cuesor cuesor = getChild(i,i1); Picasso.with(context).load(cuesor.picSmall).into(imageView); textView1.setText(cuesor.name); textView2.setText("学习人数:"+cuesor.learner); return view; } }
(4) 在UI线程中调用方法并实现接口
private void loadNet() { HttpRequest.request(PATH, new HttpRequest.onResponseLister() { @Override public void onResponseListerSuccess(String result) { BaseEntity<List<Cuesor>> baseEntity = Parser.parserData(result); //获取课程信息 List<Cuesor> cuesors = baseEntity.data; NetAdapter netAdapter = new NetAdapter(MainActivity.this); netAdapter.addNewData(LocalData.getGroupData(),LocalData.getNet(cuesors)); melv.setAdapter(netAdapter); } @Override public void onResponseListerSFail(String result) { } }); }