ExpandableListView 实现点击某个group的时候再去请求网络动态加载子视图中的数据

本文主要是介绍ExpandableListView 实现点击某个group的时候再去请求网络动态加载子视图中的数据,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!


  整个项目虽然简单,我还是上传到github了,欢迎star
https://github.com/MZCretin/SuperExpandableListView


  昨天有个朋友问我有没有用过ExpandableListView,他说他们要做个类似QQ的联系人的页面,需要用到ExpandableListView,但是他们的group和child里面的数据是分开的,他想的是,刚进来的时候,加载group的数据,然后点击每个group的时候,请求child的数据,然后加载完再展开。现在网上找的一些demo都是直接加载全部的,所以让我写个demo玩玩。因为没有设计稿,我就模仿QQ的联系人做了一次demo。
下面是效果图:

效果展示


  刚进来的时候,没有数据,模拟请求网络加载数据,等到数据加载完的时候,显示第一层group数据;点击任意group层,然后再次模拟请求child层的数据,等到数据加载完的时候,展开child层;再次点击,收起child层,再次点击,会再次模拟请求child层的数据,然后展开child层,会多一条数据。

  其实实现这个效果的关键点有两点,第一点就是setOnGroupClickListener()方法,我们要自己实现该方法,返回true才行,当返回true的时候,系统才会将group的点击事件交个我们自己处理,返回false的时候,系统会自动处理我们的展开和收回动作;第二点就是group的布局layout里面,我们要加上前面那个三角形的图片,当然如果你使用默认的那个指示器也可以。如果需要自定义指示器,我建议在group的布局layout里面加上指示器,然后在public View getGroupView(int groupPosition, boolean isExpanded,View convertView, ViewGroup parent) {}方法里面,根据isExpanded是否展开来设置指示器的resource。虽然说系统提供了一个方法让我们自定义指示器:expandableListView.setGroupIndicator(this.getResources().getDrawable(R.drawable.shape_expendable_listview));但是经验证,使用该方法设置的指示器,会放大图片,展示出来的效果不好(可能我设置的方法不对)。

下面是重写的onGroupClick()方法:

//分组的点击事件expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {@Overridepublic boolean onGroupClick(ExpandableListView parent, View v, final int groupPosition, long id) {//如果分组被打开 直接关闭if ( expandableListView.isGroupExpanded(groupPosition) ) {expandableListView.collapseGroup(groupPosition);}//否则模拟请求数据 1000 后自动添加一条数据else {//显示对话框showDialog("加载Child数据");//模拟加载数据 1000后通知handler新增一条数据new Timer().schedule(new TimerTask() {@Overridepublic void run() {Message message = handler.obtainMessage();message.what = 1;message.arg1 = groupPosition;handler.sendMessage(message);}}, 1000);}//返回false表示系统自己处理展开和关闭事件 返回true表示调用者自己处理展开和关闭事件return true;}});

下面是自定义适配器的getGroupView()方法:

public View getGroupView(int groupPosition, boolean isExpanded,View convertView, ViewGroup parent) {// 取得用于显示给定分组的视图。 这个方法仅返回分组的视图对象, 要想获取子元素的视图对象,// 就需要调用 getChildView(int, int, boolean, View, ViewGroup)。View v;if ( convertView == null ) {v = newGroupView(parent);} else {v = convertView;}bindGroupView(v, mGroupArray.get(groupPosition), isExpanded);return v;}/*** 绑定组数据** @param view* @param data* @param isExpanded*/private void bindGroupView(View view, GroupModel data, boolean isExpanded) {// 绑定组视图的数据 当然这些都是模拟的TextView tv_title = ( TextView ) view.findViewById(R.id.tv_title);TextView tv_online = ( TextView ) view.findViewById(R.id.tv_online);tv_title.setText(data.getTitle());tv_online.setText(data.getOnline());if ( !use_default_indicator ) {ImageView iv_tip = ( ImageView ) view.findViewById(R.id.iv_tip);if ( isExpanded ) {iv_tip.setImageResource(R.mipmap.down);} else {iv_tip.setImageResource(R.mipmap.right);}}}

还有一点需要注意,如果使用我说的这种方法来设置指示器,需要取消掉系统的指示器,具体做法如下:

//设置为空则代表不显示指示器 使用者自己处理
expandableListView.setGroupIndicator(null);

由于代码比较少,在这里贴出所有需要的代码,方便大家引用。

MainActivity.java

package com.cretin.www.superexpandablelistview;import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ExpandableListView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;public class MainActivity extends AppCompatActivity {private ExpandableListView expandableListView;//最外面一层 分组名private List<GroupModel> groupArray;//最外面一层 分组下面的详情private List<List<ChildModel>> childArray;//自定义的适配器private ExpandableAdapter expandableAdapter;//模拟加载数据的对话框private ProgressDialog progressDialog;//是否使用默认的指示器 默认true 使用者可以在这里通过改变这个值观察默认指示器和自定义指示器的区别private boolean use_default_indicator = false;private Handler handler = new Handler() {@Overridepublic void handleMessage(Message msg) {//取消对话框progressDialog.dismiss();if ( msg.what == 1 ) {//往对应分组的详情里面添加一条数据List<ChildModel> childModels = childArray.get(msg.arg1);childModels.add(new ChildModel("测试" + System.currentTimeMillis(), "[WIFI在线]"));
//            expandableAdapter.notifyDataSetChanged();//打开分组expandableListView.expandGroup(msg.arg1, true);} else {//第一次加载数据的时候 添加分组信息groupArray.add(new GroupModel("特别关心", "2/2"));groupArray.add(new GroupModel("就这样吧", "36/70"));groupArray.add(new GroupModel("曾经    启明星", "6/7"));groupArray.add(new GroupModel("青春    独家", "58/82"));//这里根据分组来创建对应的详情信息 创建好集合就行 具体数据等点击的时候再添加//特别关心List<ChildModel> tempArray0 = new ArrayList<>();childArray.add(tempArray0);//就这样吧List<ChildModel> tempArray1 = new ArrayList<>();childArray.add(tempArray1);//曾经    启明星List<ChildModel> tempArray2 = new ArrayList<>();childArray.add(tempArray2);//青春    独家List<ChildModel> tempArray3 = new ArrayList<>();childArray.add(tempArray3);expandableAdapter.notifyDataSetChanged();}}};//展示对话框private void showDialog(String msg) {if ( progressDialog == null ) {//创建ProgressDialog对象progressDialog = new ProgressDialog(this);//设置进度条风格,风格为圆形,旋转的progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);//设置ProgressDialog 标题图标progressDialog.setIcon(android.R.drawable.btn_star);//设置ProgressDialog 的进度条是否不明确progressDialog.setIndeterminate(false);//设置ProgressDialog 是否可以按退回按键取消progressDialog.setCancelable(false);}//设置ProgressDialog 提示信息progressDialog.setMessage("正在" + msg + "...");// 让ProgressDialog显示progressDialog.show();}@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);//初始化控件expandableListView = ( ExpandableListView ) findViewById(R.id.expandable_listview);if ( use_default_indicator ) {//不做处理就是默认} else {expandableListView.setGroupIndicator(null);}//这里是通过改变默认的setGroupIndicator方式实现自定义指示器 但是效果不好 图标会被拉伸的很难看 不信你可以自己试试
//        expandableListView.setGroupIndicator(this.getResources().getDrawable(R.drawable.shape_expendable_listview));groupArray = new ArrayList<>();childArray = new ArrayList<>();//创建适配器expandableAdapter = new ExpandableAdapter(this, groupArray, R.layout.item_group, childArray, R.layout.item_child);expandableListView.setAdapter(expandableAdapter);//第一次加载数据showDialog("加载Group的数据");//模拟加载数据 1000后通知handler添加group的数据new Timer().schedule(new TimerTask() {@Overridepublic void run() {Message message = handler.obtainMessage();message.what = 2;handler.sendMessage(message);}}, 10000);//分组的点击事件expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {@Overridepublic boolean onGroupClick(ExpandableListView parent, View v, final int groupPosition, long id) {//如果分组被打开 直接关闭if ( expandableListView.isGroupExpanded(groupPosition) ) {expandableListView.collapseGroup(groupPosition);}//否则模拟请求数据 1000 后自动添加一条数据else {//显示对话框showDialog("加载Child数据");//模拟加载数据 1000后通知handler新增一条数据new Timer().schedule(new TimerTask() {@Overridepublic void run() {Message message = handler.obtainMessage();message.what = 1;message.arg1 = groupPosition;handler.sendMessage(message);}}, 1000);}//返回false表示系统自己处理展开和关闭事件 返回true表示调用者自己处理展开和关闭事件return true;}});//详情的点击事件expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {@Overridepublic boolean onChildClick(ExpandableListView parent, View v, int groupPosition,int childPosition, long id) {//在Kotlin正式加入使用之前 在使用任何数据之前 判断下是比较好的习惯List<ChildModel> childModels = childArray.get(groupPosition);if ( childModels != null ) {ChildModel childModel = childModels.get(childPosition);if ( childModel != null ) {String name = childModel.getName();if ( !TextUtils.isEmpty(name) ) {Toast.makeText(MainActivity.this, name + "说:你点个屁哦", Toast.LENGTH_SHORT).show();}}}return false;}});}class ExpandableAdapter extends BaseExpandableListAdapter {//视图加载器private LayoutInflater mInflater;private Context mContext;private int mExpandedGroupLayout;private int mChildLayout;private List<GroupModel> mGroupArray;private List<List<ChildModel>> mChildArray;/*** 构造函数** @param context* @param groupData* @param expandedGroupLayout 分组视图布局* @param childData* @param childLayout         详情视图布局*/public ExpandableAdapter(Context context, List<GroupModel> groupData, int expandedGroupLayout,List<List<ChildModel>> childData, int childLayout) {mContext = context;mExpandedGroupLayout = expandedGroupLayout;mChildLayout = childLayout;mGroupArray = groupData;mChildArray = childData;mInflater = ( LayoutInflater ) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);}public Object getChild(int groupPosition, int childPosition) {return childArray.get(groupPosition).get(childPosition);}public long getChildId(int groupPosition, int childPosition) {return childPosition;}public View getChildView(int groupPosition, int childPosition,boolean isLastChild, View convertView, ViewGroup parent) {// 取得显示给定分组给定子位置的数据用的视图。View v;if ( convertView == null ) {v = newChildView(parent);} else {v = convertView;}bindChildView(v, mChildArray.get(groupPosition).get(childPosition));return v;}@Overridepublic int getChildrenCount(int groupPosition) {// 取得指定分组的子元素数。return mChildArray.get(groupPosition).size();}@Overridepublic Object getGroup(int groupPosition) {// 取得与给定分组关联的数据。return mGroupArray.get(groupPosition);}@Overridepublic int getGroupCount() {// 取得分组数return mGroupArray.size();}@Overridepublic long getGroupId(int groupPosition) {// 取得指定分组的ID。该组ID必须在组中是唯一的。组合的ID (参见getCombinedGroupId(long))// 必须不同于其他所有ID(分组及子项目的ID)。return groupPosition;}public View getGroupView(int groupPosition, boolean isExpanded,View convertView, ViewGroup parent) {// 取得用于显示给定分组的视图。 这个方法仅返回分组的视图对象, 要想获取子元素的视图对象,// 就需要调用 getChildView(int, int, boolean, View, ViewGroup)。View v;if ( convertView == null ) {v = newGroupView(parent);} else {v = convertView;}bindGroupView(v, mGroupArray.get(groupPosition), isExpanded);return v;}/*** 绑定组数据** @param view* @param data* @param isExpanded*/private void bindGroupView(View view, GroupModel data, boolean isExpanded) {// 绑定组视图的数据 当然这些都是模拟的TextView tv_title = ( TextView ) view.findViewById(R.id.tv_title);TextView tv_online = ( TextView ) view.findViewById(R.id.tv_online);tv_title.setText(data.getTitle());tv_online.setText(data.getOnline());if ( !use_default_indicator ) {ImageView iv_tip = ( ImageView ) view.findViewById(R.id.iv_tip);if ( isExpanded ) {iv_tip.setImageResource(R.mipmap.down);} else {iv_tip.setImageResource(R.mipmap.right);}}}/*** 绑定子数据** @param view* @param data*/private void bindChildView(View view, ChildModel data) {// 绑定组视图的数据 当然这些都是模拟的TextView tv_name = ( TextView ) view.findViewById(R.id.tv_name);TextView tv_sig = ( TextView ) view.findViewById(R.id.tv_sig);tv_name.setText(data.getName());tv_sig.setText(data.getSig());}/*** 创建新的组视图** @param parent* @return*/public View newGroupView(ViewGroup parent) {return mInflater.inflate(mExpandedGroupLayout, parent, false);}/*** 创建新的子视图** @param parent* @return*/public View newChildView(ViewGroup parent) {return mInflater.inflate(mChildLayout, parent, false);}public boolean hasStableIds() {// 是否指定分组视图及其子视图的ID对应的后台数据改变也会保持该ID。return true;}public boolean isChildSelectable(int groupPosition, int childPosition) {// 指定位置的子视图是否可选择。return true;}}
}

ChildModel.java

package com.cretin.www.superexpandablelistview;/*** Created by cretin on 2017/5/23.*/public class ChildModel {private String name;private String sig;public ChildModel(String name, String sig) {this.name = name;this.sig = sig;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getSig() {return sig;}public void setSig(String sig) {this.sig = sig;}
}

GroupModel.java

package com.cretin.www.superexpandablelistview;/*** Created by cretin on 2017/5/23.*/public class GroupModel {private String title;private String online;public GroupModel(String title, String online) {this.title = title;this.online = online;}public String getTitle() {return title;}public void setTitle(String title) {this.title = title;}public String getOnline() {return online;}public void setOnline(String online) {this.online = online;}
}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    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"android:background="#ffffff"tools:context="com.cretin.www.superexpandablelistview.MainActivity"><ExpandableListView
        android:id="@+id/expandable_listview"android:layout_width="match_parent"android:layout_height="match_parent"/></RelativeLayout>

item_child.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"><ImageView
        android:layout_width="60dp"android:layout_height="60dp"android:layout_gravity="center"android:padding="10dp"android:src="@mipmap/timg"/><LinearLayout
        android:layout_width="match_parent"android:layout_height="40dp"android:layout_gravity="center"android:layout_weight="1"android:orientation="vertical"><TextView
            android:id="@+id/tv_name"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"android:text="穆仙念"android:textColor="#4a4a4a"android:textSize="14sp"/><TextView
            android:id="@+id/tv_sig"android:layout_width="match_parent"android:layout_height="match_parent"android:layout_gravity="center"android:layout_weight="1"android:gravity="bottom"android:paddingRight="16dp"android:singleLine="true"android:text="[4G在线]所有人都在奋不顾身,不是只有你受尽委屈"android:textColor="#9b9b9b"android:textSize="12sp"/></LinearLayout></LinearLayout>

item_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="match_parent"android:orientation="horizontal"><ImageView
        android:id="@+id/iv_tip"android:layout_width="40dp"android:layout_height="wrap_content"android:layout_gravity="center"/><TextView
        android:id="@+id/tv_title"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_gravity="center"android:layout_weight="1"android:paddingBottom="10dp"android:paddingTop="10dp"android:text="特别关心"android:textColor="#4a4a4a"android:textSize="16sp"/><TextView
        android:id="@+id/tv_online"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="center"android:padding="10dp"android:text="2/2"android:textColor="#9b9b9b"android:textSize="12sp"/></LinearLayout>

这是项目中用到的两个图片素材:

down.png

right.png

最后,整个项目虽然简单,我还是上传到github了,欢迎star
https://github.com/MZCretin/SuperExpandableListView

这篇关于ExpandableListView 实现点击某个group的时候再去请求网络动态加载子视图中的数据的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1006563

相关文章

详谈redis跟数据库的数据同步问题

《详谈redis跟数据库的数据同步问题》文章讨论了在Redis和数据库数据一致性问题上的解决方案,主要比较了先更新Redis缓存再更新数据库和先更新数据库再更新Redis缓存两种方案,文章指出,删除R... 目录一、Redis 数据库数据一致性的解决方案1.1、更新Redis缓存、删除Redis缓存的区别二

Redis事务与数据持久化方式

《Redis事务与数据持久化方式》该文档主要介绍了Redis事务和持久化机制,事务通过将多个命令打包执行,而持久化则通过快照(RDB)和追加式文件(AOF)两种方式将内存数据保存到磁盘,以防止数据丢失... 目录一、Redis 事务1.1 事务本质1.2 数据库事务与redis事务1.2.1 数据库事务1.

python使用watchdog实现文件资源监控

《python使用watchdog实现文件资源监控》watchdog支持跨平台文件资源监控,可以检测指定文件夹下文件及文件夹变动,下面我们来看看Python如何使用watchdog实现文件资源监控吧... python文件监控库watchdogs简介随着Python在各种应用领域中的广泛使用,其生态环境也

el-select下拉选择缓存的实现

《el-select下拉选择缓存的实现》本文主要介绍了在使用el-select实现下拉选择缓存时遇到的问题及解决方案,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的... 目录项目场景:问题描述解决方案:项目场景:从左侧列表中选取字段填入右侧下拉多选框,用户可以对右侧

最好用的WPF加载动画功能

《最好用的WPF加载动画功能》当开发应用程序时,提供良好的用户体验(UX)是至关重要的,加载动画作为一种有效的沟通工具,它不仅能告知用户系统正在工作,还能够通过视觉上的吸引力来增强整体用户体验,本文给... 目录前言需求分析高级用法综合案例总结最后前言当开发应用程序时,提供良好的用户体验(UX)是至关重要

Python pyinstaller实现图形化打包工具

《Pythonpyinstaller实现图形化打包工具》:本文主要介绍一个使用PythonPYQT5制作的关于pyinstaller打包工具,代替传统的cmd黑窗口模式打包页面,实现更快捷方便的... 目录1.简介2.运行效果3.相关源码1.简介一个使用python PYQT5制作的关于pyinstall

使用Python实现大文件切片上传及断点续传的方法

《使用Python实现大文件切片上传及断点续传的方法》本文介绍了使用Python实现大文件切片上传及断点续传的方法,包括功能模块划分(获取上传文件接口状态、临时文件夹状态信息、切片上传、切片合并)、整... 目录概要整体架构流程技术细节获取上传文件状态接口获取临时文件夹状态信息接口切片上传功能文件合并功能小

python实现自动登录12306自动抢票功能

《python实现自动登录12306自动抢票功能》随着互联网技术的发展,越来越多的人选择通过网络平台购票,特别是在中国,12306作为官方火车票预订平台,承担了巨大的访问量,对于热门线路或者节假日出行... 目录一、遇到的问题?二、改进三、进阶–展望总结一、遇到的问题?1.url-正确的表头:就是首先ur

C#实现文件读写到SQLite数据库

《C#实现文件读写到SQLite数据库》这篇文章主要为大家详细介绍了使用C#将文件读写到SQLite数据库的几种方法,文中的示例代码讲解详细,感兴趣的小伙伴可以参考一下... 目录1. 使用 BLOB 存储文件2. 存储文件路径3. 分块存储文件《文件读写到SQLite数据库China编程的方法》博客中,介绍了文

Oracle Expdp按条件导出指定表数据的方法实例

《OracleExpdp按条件导出指定表数据的方法实例》:本文主要介绍Oracle的expdp数据泵方式导出特定机构和时间范围的数据,并通过parfile文件进行条件限制和配置,文中通过代码介绍... 目录1.场景描述 2.方案分析3.实验验证 3.1 parfile文件3.2 expdp命令导出4.总结