ExpandableListView 实现点击某个group的时候再去请求网络动态加载子视图中的数据
2017-05-24 14:34
831 查看
整个项目虽然简单,我还是上传到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()方法:
下面是自定义适配器的getGroupView()方法:
还有一点需要注意,如果使用我说的这种方法来设置指示器,需要取消掉系统的指示器,具体做法如下:
由于代码比较少,在这里贴出所有需要的代码,方便大家引用。
MainActivity.java
ChildModel.java
GroupModel.java
activity_main.xml
item_child.xml
item_group.xml
这是项目中用到的两个图片素材:
最后,整个项目虽然简单,我还是上传到github了,欢迎star
https://github.com/MZCretin/SuperExpandableListView
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() { @Override public 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() { @Override public 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() {
@Override
public 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();
}
@Override
protected 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() {
@Override
public void run() {
Message message = handler.obtainMessage();
message.what = 2;
handler.sendMessage(message);
}
}, 10000);
//分组的点击事件 expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() { @Override public 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() { @Override public 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() {
@Override
public 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;
}
@Override
public int getChildrenCount(int groupPosition) {
// 取得指定分组的子元素数。
return mChildArray.get(groupPosition).size();
}
@Override
public Object getGroup(int groupPosition) {
// 取得与给定分组关联的数据。
return mGroupArray.get(groupPosition);
}
@Override
public int getGroupCount() {
// 取得分组数
return mGroupArray.size();
}
@Override
public 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>
这是项目中用到的两个图片素材:
最后,整个项目虽然简单,我还是上传到github了,欢迎star
https://github.com/MZCretin/SuperExpandableListView
相关文章推荐
- 点击侧滑任何一个 菜单项,请求网络数据展示在主界面的xlistview中且实现下拉刷新效果和上拉加载的分页加载数据效果
- ExpandableListView 动态加载 点击一级菜单时再加载二级菜单的内容实现
- ExpandableListView使用,点击条目请求网络数据,箭头在右边
- Listview动态加载网络数据Activity大概实现
- Android通过请求网络数据实现ListView,ListView的优化、图片的缓存、子控件的点击事件。
- ExpandableListView的首次加载全部展开,并且点击Group不收缩、
- Android ListView下拉加载更多,http请求加载数据,Item点击事件
- 一步一步实现ListView加载网络数据,下滑底部加载,顶部下拉刷新。并配有双缓存
- dh: 实现iframe 自适应高度的问题(初始化和动态加载数据的时候)
- 使用ViewSwitcher实现ListView的数据动态加载[学习]
- (原创)使用AsyncTask(带修改线程池方式)+自定义ImageLoader+LRU算法对图片三级缓存及其显示优化(只有在ListView滑动停止的时候才去网络请求获取图片数据)
- Android实现ListView数据动态加载的方法
- ExpandableListView的首次加载全部展开,并且点击Group不收缩、
- [置顶] pull解析请求网络的数据(带分页加载,刷新) xlistview HttpUtils
- 网络连接取数据,并加载到ListView的实现
- 关于加载无网络图与无数据图设计思路,点击重新加载方法实现
- 自定义CursorAdapter,实现ListView中动态加载button,和点击事件
- ListView动态加载网络数据的解决办法
- ExpandableListView的首次加载全部展开,并且点击Group不收缩、
- ExpandableListView的首次加载全部展开,并且点击Group不收缩