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) { } }); }