目录
- 一、RecyclerView
- 二、解决RecyclerView的Item复用导致的问题
- 三、给RecyclerView添加Head和Foot
- 四、RecyclerView添加FOOT并实现上拉加载更多
- 五、利用SwipeRefreshLayout实现下拉刷新
- 六、ExpandableListView
- 效果图
- 代码实现
- 1.修改布局
- 2.初始化数据
- 3.创建ExpandableListView的适配器
- 4.传入数据
- 5.修改继承BaseExpandableListAdapter时实现的十个方法
- 1.int getGroupCount()
- 2.int getChildrenCount(int groupPosition)
- 3.Object getGroup(int groupPosition)
- 4.Object getChild(int groupPosition, int childPosition)
- 5.long getGroupId(int groupPosition)
- 6.long getChildId(int groupPosition, int childPosition)
- 7.boolean hasStableIds()
- 8.View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)
- 9.View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)
- 10.boolean isChildSelectable(int groupPosition, int childPosition)
- 6.给ExpandableListView添加适配器
- 7.给ExpandableListView添加点击事件
- 附:
一、RecyclerView
效果图
代码实现
1.引入recyclerview包
implementation 'com.android.support:design:27.1.0'
2.布局中添加RecyclerView
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_apps"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.constraint.ConstraintLayout>
3.MainActivity中设置LayoutManager
public class MainActivity extends AppCompatActivity {
private RecyclerView rvApps;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvApps = findViewById(R.id.rv_apps);
rvApps.setLayoutManager(new LinearLayoutManager(this));
}
}
LayoutManager的作用是表明RecyclerView的展示方式,LinearLayoutManager表示是线性垂直垂直展示。
除了LinearLayout还有GridLayoutManager,StaggeredGridLayoutManager,分别代表网格展示和瀑布流展示。
4.创建RecyclerView适配器
新建MyAdapter,继承自RecyclerView.Adapter:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.mViewHolder>{
private ArrayList<App> apps;
//构造方法,传入数据
public MyAdapter(ArrayList<App> apps) {
this.apps = apps;
}
//初始化控件
public class mViewHolder extends RecyclerView.ViewHolder {
private ImageView ivIcon;
private TextView tvName;
public mViewHolder(View itemView) {
super(itemView);
ivIcon = itemView.findViewById(R.id.iv_icon);
tvName = itemView.findViewById(R.id.tv_name);
}
}
//绑定布局
@NonNull
@Override
public mViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.app_item,parent,false);
mViewHolder holder = new mViewHolder(view);
return holder;
}
//对应位置绑定数据,这里可以添加点击事件
@Override
public void onBindViewHolder(@NonNull mViewHolder holder, int position) {
holder.tvName.setText(apps.get(position).getName());
holder.ivIcon.setImageDrawable(apps.get(position).getIcon());
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//这里添加点击事件
}
});
}
//获取列表长度
@Override
public int getItemCount() {
return apps.size();
}
}
里面用到了App类,贴出App类的代码:
public class App {
private String name;//app名称
private Drawable icon;//app图标
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Drawable getIcon() {
return icon;
}
public void setIcon(Drawable icon) {
this.icon = icon;
}
}
用到的app_item布局:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto">
<ImageView
android:id="@+id/iv_icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp"
android:src="@mipmap/ic_launcher"/>
<TextView
android:id="@+id/tv_name"
app:layout_constraintLeft_toRightOf="@+id/iv_icon"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:layout_margin="16dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/app_name"/>
</android.support.constraint.ConstraintLayout>
5.为RecyclerView设置适配器
在MainActivity中初始化数据,并为RecyclerView设置adapter,MainActivity最终的代码为:
public class MainActivity extends AppCompatActivity {
private RecyclerView rvApps;
private ArrayList<App> apps = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvApps = findViewById(R.id.rv_apps);
rvApps.setLayoutManager(new LinearLayoutManager(this));
//初始化数据
for (int i = 0;i<50;i++){
App app = new App();
app.setIcon(getResources().getDrawable(R.mipmap.ic_launcher));
app.setName(getString(R.string.app_name)+i);
apps.add(app);
}
rvApps.setAdapter(new MyAdapter(apps));
}
}
以上,便是RecyclerView的基本使用。
二、解决RecyclerView的Item复用导致的问题
1.复用导致的问题
如果RecyclerView的Item中有Checkbox,或者其他的具有记录状态功能的控件。那么很容易引发Item的复用问题
如上图,本来一个很正常的列表,每个item都没被选中,当我选中第一个Checkbox的时候,后面item的Checkbox也变成了选中状态。这就是item的复用导致的错误。
2.问题的原因
由于RecyclerView会自动回收移出屏幕的item,然后把回收回来的item让新进入屏幕的item使用。所以导致了新进入的item保留了之前的选中状态。
3.解决思路
解决item复用问题的思路是:在滑动时虽然checkbox的状态被复用了,但是数据却没有被复用,仍然是1,2,3,4…,所以我们可以通过一个checkStatus列表来保存checkbox的状态,每次选中checkbox时,把该位置加入checkStatus中,每次取消选中checkbox时,把该位置从checkStatus中移除。然后在每次设置checkbox状态的时候根据列表来判断是否选中。
4.代码实现
将MyAdapter修改如下:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.mViewHolder>{
private List<String> DataList;
//记录checkbox状态的列表
private List<Integer> checkStatus;
//构造方法,传入数据
public MyAdapter(List<String> DataList) {
this.DataList = DataList;
}
//初始化控件
public class mViewHolder extends RecyclerView.ViewHolder {
private TextView text;
private CheckBox checkbox;
public mViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.text);
checkbox = itemView.findViewById(R.id.checkbox);
}
}
//绑定布局
@NonNull
@Override
public mViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
mViewHolder holder = new mViewHolder(view);
return holder;
}
//对应位置绑定数据,这里可以添加点击事件
@Override
public void onBindViewHolder(@NonNull final mViewHolder holder, int position) {
if(checkStatus == null){
checkStatus = new ArrayList<>();
}
holder.text.setText(DataList.get(position));
holder.checkbox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
Integer position = holder.getLayoutPosition();
if(isChecked&&!checkStatus.contains(position)){
//如果checkbox选中了,而且checkStatus中不包含此位置,将该位置加入checkStatus中
checkStatus.add(position);
}else if(!isChecked&&checkStatus.contains(position)){
//如果checkbox未选中,而且checkStatus中包含此位置,将该位置从checkStatus移除
checkStatus.remove(position);
}
}
});
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
holder.checkbox.setChecked(!holder.checkbox.isChecked());
}
});
//根据checkStatus中是否包含此位置来设置checkbox状态
holder.checkbox.setChecked(checkStatus.contains(position));
}
//获取列表长度
@Override
public int getItemCount() {
return null == DataList ? 0:DataList.size();
}
}
效果图如下:
三、给RecyclerView添加Head和Foot
效果图
我们先写一个Adapter,然后在此基础上添加foot和head:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private List<String> DataList;
//构造方法,传入数据
public MyAdapter(List<String> DataList) {
this.DataList = DataList;
}
//初始化ItemView
public class ItemViewHolder extends RecyclerView.ViewHolder {
private TextView text;
public ItemViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.text);
}
}
//绑定布局
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
RecyclerView.ViewHolder holder;
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
holder = new ItemViewHolder(view);
return holder;
}
//对应位置绑定数据,这里可以添加点击事件
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
itemViewHolder.text.setText(DataList.get(position-1));
}
//获取列表长度
@Override
public int getItemCount() {
return null == DataList ? 0:DataList.size();
}
}
添加了Head和Foot的RecyclerView的Adapter:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final int ITEM = 0;
private static final int HEAD = 1;
private static final int FOOT = 2;
private List<String> DataList;
//构造方法,传入数据
public MyAdapter(List<String> DataList) {
this.DataList = DataList;
}
//初始化ItemView
public class ItemViewHolder extends RecyclerView.ViewHolder {
private TextView text;
public ItemViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.text);
}
}
//初始化HeadView
public class HeadViewHolder extends RecyclerView.ViewHolder {
private TextView text;
public HeadViewHolder(View headView) {
super(headView);
text = headView.findViewById(R.id.text);
}
}
//初始化FootView
public class FootViewHolder extends RecyclerView.ViewHolder {
private TextView text;
public FootViewHolder(View footView) {
super(footView);
text = footView.findViewById(R.id.text);
}
}
//绑定布局
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
RecyclerView.ViewHolder holder;
switch (viewType){
case HEAD:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.head,parent,false);
holder = new HeadViewHolder(view);
break;
case FOOT:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.foot,parent,false);
holder = new FootViewHolder(view);
break;
default:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
holder = new ItemViewHolder(view);
break;
}
return holder;
}
//对应位置绑定数据,这里可以添加点击事件
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if(holder instanceof HeadViewHolder){
HeadViewHolder headViewHolder = (HeadViewHolder) holder;
headViewHolder.text.setText("HEAD");
}else if(holder instanceof FootViewHolder){
FootViewHolder footViewHolder = (FootViewHolder) holder;
footViewHolder.text.setText("FOOT");
}else if(holder instanceof ItemViewHolder){
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
//比如有3条数据,0是head,1,2,3是item,4是foot。数据下标是0,1,2,所以item绑定position-1对应的数据
itemViewHolder.text.setText(DataList.get(position-1));
}
}
@Override
public int getItemViewType(int position) {
if(position == 0){
return HEAD;
} else if(position == getItemCount()-1){
return FOOT;
}else{
return ITEM;
}
}
//获取列表长度
@Override
public int getItemCount() {
return (null == DataList ? 0:DataList.size())+2;
}
}
原理
添加HEAD和FOOT的原理是:在onCreateViewHolder的参数中,第二个参数ViewType代表View的类型。重写Adapter的getViewType()方法,根据位置返回对应的ViewType。如果位置是0,返回HEAD类型,如果位置是最后一位(getItemCount()-1),返回FOOT类型。其他则返回普通ITEM类型。
代码实现
1.修改getItemCount()
getItemCount()是用来获取列表长度的,在原有基础上+2,表示添加HEAD和FOOT之后的长度
2.修改getViewType()
getViewType()方法中,第一个位置返回HEAD,最后一个位置返回FOOT,其他的返回普通ITEM。
3.添加HeadViewHolder和FootViewHolder
在holder中,绑定Head和Foot的布局并找到控件。这里的布局很简单,只有一个TextView,故不再给出foot和head的布局代码。
4.修改onCreateViewHolder()
在onCreateViewHolder中,根据ViewType创建相应的ViewHolder
5.在onBindViewHolder中绑定数据
使用instance of判断ViewHolder的类型,并给对应的类型绑定数据。这里只是将HeadViewHolder和FootViewHolder中的TextView设置了一个固定的字符串。(复杂的数据可以通过构造方法传进来,类似itemViewHolder绑定数据的写法)
注:RecyclerView的ViewType是我们自己定义的,只要根据getViewType返回不同的ViewType,在创建ViewHolder时候根据ViewType创建不同的ViewHolder,在绑定数据时根据不同的ViewHolder绑定相应数据即可,并不局限于HEAD和FOOT。
例如,可以给奇数位置的Item返回一个ViewType,为其绑定不同的布局。也可以为position%3==0的位置返回一个ViewType,为其绑定不同的布局,非常的灵活。
四、RecyclerView添加FOOT并实现上拉加载更多
效果图
原理
要实现上拉加载更多,需要监听RecyclerView是否滑动到了底部。使用RecyclerView的addOnScrollListener监听RecyclerView的滑动。利用LayoutManager的findLastCompletelyVisibleItemPosition方法找到最后一个完全显示的item的位置。如果显示的是最后一个item,则表示滑动到foot了,执行添加新数据操作。
代码实现
1.先写一个添加了FOOT的Adapter
利用第二节的方法添加的FOOT:
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final int ITEM = 0;
private static final int FOOT = 2;
private List<String> DataList;
//构造方法,传入数据
public MyAdapter(List<String> DataList) {
this.DataList = DataList;
}
//初始化ItemView
public class ItemViewHolder extends RecyclerView.ViewHolder {
private TextView text;
public ItemViewHolder(View itemView) {
super(itemView);
text = itemView.findViewById(R.id.text);
}
}
//初始化FootView
public class FootViewHolder extends RecyclerView.ViewHolder {
private TextView text;
public FootViewHolder(View footView) {
super(footView);
text = footView.findViewById(R.id.text);
}
}
//绑定布局
@NonNull
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
View view;
RecyclerView.ViewHolder holder;
switch (viewType){
case FOOT:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.foot,parent,false);
holder = new FootViewHolder(view);
break;
default:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,parent,false);
holder = new ItemViewHolder(view);
break;
}
return holder;
}
//对应位置绑定数据,这里可以添加点击事件
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
if(holder instanceof FootViewHolder){
FootViewHolder footViewHolder = (FootViewHolder) holder;
footViewHolder.text.setText("上拉加载更多");
}else if(holder instanceof ItemViewHolder){
ItemViewHolder itemViewHolder = (ItemViewHolder) holder;
//比如有3条数据,0,1,2是item,3是foot。数据下标是0,1,2,所以item绑定position对应的数据
itemViewHolder.text.setText(DataList.get(position));
}
}
@Override
public int getItemViewType(int position) {
if(position == getItemCount()-1){
//比如有3条数据,0,1,2是item,3是foot。数据条数为3+1=4,所以用getItemCount()-1
return FOOT;
}else{
return ITEM;
}
}
//获取列表长度
@Override
public int getItemCount() {
return (null == DataList ? 0:DataList.size())+1;
}
}
2.给RecyclerView添加滑动监听器
MainActivity中的代码:
public class MainActivity extends AppCompatActivity{
private RecyclerView rvTest;
private List<String> datas;
private LinearLayoutManager linearLayoutManager;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
rvTest = (RecyclerView) findViewById(R.id.rv_test);
//初始化数据
datas = new ArrayList<>();
for (int i = 0; i < 50; i++) {
datas.add(String.valueOf(i));
}
//设置layoutManager
linearLayoutManager = new LinearLayoutManager(this);
//设置adapter
adapter = new MyAdapter(datas);
//设置滑动监听
rvTest.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
//如果滑动到foot了,增加新数据
if(linearLayoutManager.findLastCompletelyVisibleItemPosition() == adapter.getItemCount()-1){
for (int i = 0; i < 5; i++) {
datas.add(String.valueOf(i));
adapter.notifyDataSetChanged();
}
}
}
});
rvTest.setLayoutManager(linearLayoutManager);
rvTest.setAdapter(adapter);
}
}
五、利用SwipeRefreshLayout实现下拉刷新
简介
SwipeRefreshLayout是Google官方推出的下拉刷新控件,非常的简单好用。
效果图
代码实现
1.修改布局
在布局中,将RecyclerView用SwipeRefreshLayout包裹起来:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
tools:context=".MainActivity">
<android.support.v4.widget.SwipeRefreshLayout
android:id="@+id/swipe_refresh"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/rv_test"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
</android.support.v4.widget.SwipeRefreshLayout>
</android.support.constraint.ConstraintLayout>
2.设置OnRefreshListener
在MainActivity中,找到SwipeRefreshLayout控件,并设置OnRefreshListener即可。
public class MainActivity extends AppCompatActivity{
private SwipeRefreshLayout swipeRefresh;
private RecyclerView rvTest;
private List<String> datas;
private LinearLayoutManager linearLayoutManager;
private MyAdapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
swipeRefresh = (SwipeRefreshLayout) findViewById(R.id.swipe_refresh);
rvTest = (RecyclerView) findViewById(R.id.rv_test);
//初始化数据
datas = new ArrayList<>();
for (int i = 0; i < 50; i++) {
datas.add(String.valueOf(i));
}
//设置layoutManager
linearLayoutManager = new LinearLayoutManager(this);
//设置adapter
adapter = new MyAdapter(datas);
rvTest.setLayoutManager(linearLayoutManager);
rvTest.setAdapter(adapter);
swipeRefresh.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
@Override
public void onRefresh() {
datas.clear();
for (int i = 0; i < 50; i++) {
datas.add(String.valueOf((int)(Math.random()*100)));
}
adapter.notifyDataSetChanged();
swipeRefresh.setRefreshing(false);
}
});
}
}
在OnRefreshListener中执行刷新操作,这里我的刷新操作是将datas重新赋50个随机数值。执行完后调用SwipeRefreshLayout的setRefreshing(false)方法停止刷新。
3.SwipeRefresh可以自定义刷新图标的颜色,通过以下两个方法均可
swipeRefresh.setColorSchemeColors(Color.BLUE,Color.YELLOW,Color.GREEN);
//或者
swipeRefresh.setColorSchemeResources(R.color.colorAccent,R.color.colorPrimary,R.color.colorPrimaryDark);
传入颜色或者颜色资源,数量不限。刷新时将按照设置的多种颜色,每转一圈换一种颜色。
六、ExpandableListView
效果图
代码实现
1.修改布局
在布局中申明ExpandableListView
<ExpandableListView
android:id="@+id/list"
android:groupIndicator="@null"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
由于ExpandableListView自带的指示器不是很美观,在布局中设置android:groupIndicator="@null"可以不显示自带的指示器。
另外,ExpandableListView自带的分割线也是可以不显示的,通过属性android:divider="@null"设置。
2.初始化数据
在MainActivity中找到控件并初始化数据:
public class MainActivity extends AppCompatActivity {
private ExpandableListView list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = (ExpandableListView) findViewById(R.id.list);
//组数据,是一个List<T>
List<String> groupData = new ArrayList<>();
groupData.add("小学");
groupData.add("初中");
groupData.add("高中");
//子数据,每一组的子数据是一个List<T>;所以所有组的子数据是一个List<List<T>>
List<List<String>> childrenData = new ArrayList<>();
List<String> child1Data = new ArrayList<>();
child1Data.add("小学一年级");
child1Data.add("小学二年级");
child1Data.add("小学三年级");
child1Data.add("小学四年级");
child1Data.add("小学五年级");
child1Data.add("小学六年级");
List<String> child2Data = new ArrayList<>();
child2Data.add("初一");
child2Data.add("初二");
child2Data.add("初三");
List<String> child3Data = new ArrayList<>();
child3Data.add("高一");
child3Data.add("高二");
child3Data.add("高三");
childrenData.add(child1Data);
childrenData.add(child2Data);
childrenData.add(child3Data);
}
}
组数据是一个数据列:
{"小学","初中","高中"}
所以所有的子数据是一列数据列List<List>
{
{"小学一年级","小学二年级","小学三年级","小学四年级","小学五年级","小学六年级"},
{"初一","初二","初三"},
{"高一","高二","高三"}
}
3.创建ExpandableListView的适配器
新建MyExpandableListViewAdapter继承自BaseExpandableListAdapter,并实现需要实现的方法,需要实现十个方法:
代码如下:
public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
@Override
public int getGroupCount() {
return 0;
}
@Override
public int getChildrenCount(int groupPosition) {
return 0;
}
@Override
public Object getGroup(int groupPosition) {
return null;
}
@Override
public Object getChild(int groupPosition, int childPosition) {
return null;
}
@Override
public long getGroupId(int groupPosition) {
return 0;
}
@Override
public long getChildId(int groupPosition, int childPosition) {
return 0;
}
@Override
public boolean hasStableIds() {
return false;
}
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
return null;
}
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
return null;
}
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return false;
}
}
4.传入数据
创建MyExpandableListViewAdapter的构造方法,将数据传入,并根据组布局和子布局创建GroupViewHolder和ChildViewHolder,本例的组布局和子布局中只有一个TextView:
public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
private List<String> groupData;//组数据
private List<List<String>> childrenData;//子数据
private Context context;
public MyExpandableListViewAdapter(List<String> groupData,List<List<String>> childrenData){
this.groupData = groupData;
this.childrenData = childrenData;
}
public class GroupViewHolder{
TextView textView;
}
public class ChildViewHolder{
TextView textView;
}
...
}
5.修改继承BaseExpandableListAdapter时实现的十个方法
public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
private List<String> groupData;//组数据
private List<List<String>> childrenData;//子数据
private Context context;
public MyExpandableListViewAdapter(List<String> groupData,List<List<String>> childrenData){
this.groupData = groupData;
this.childrenData = childrenData;
}
public class GroupViewHolder{
TextView textView;
}
public class ChildViewHolder{
TextView textView;
}
//获取组数
@Override
public int getGroupCount() {
return groupData.size();
}
//获取指定组的子元素数
@Override
public int getChildrenCount(int groupPosition) {
return childrenData.get(groupPosition).size();
}
//获取指定组
@Override
public Object getGroup(int groupPosition) {
return groupData.get(groupPosition);
}
//获取指定子元素
@Override
public Object getChild(int groupPosition, int childPosition) {
return childrenData.get(groupPosition).get(childPosition);
}
//获取组的id
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
//获取子元素的id
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
//id是否固定,即数据变化后,数据的id是否改变
@Override
public boolean hasStableIds() {
return false;
}
//绑定组布局和组数据
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
context = parent.getContext();
convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null,false);
GroupViewHolder groupViewHolder = new GroupViewHolder();
groupViewHolder.textView = (TextView)convertView.findViewById(android.R.id.text1);
groupViewHolder.textView.setText(groupData.get(groupPosition));
return convertView;
}
//绑定子布局和子数据
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null,false);
ChildViewHolder childViewHolder = new ChildViewHolder();
childViewHolder.textView = (TextView)convertView.findViewById(android.R.id.text1);
childViewHolder.textView.setText(childrenData.get(groupPosition).get(childPosition));
return convertView;
}
//子数据是否可选择
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
方法看似很多,其实非常简单。
1.int getGroupCount()
获取一共有多少组。返回groupData.size()即可
2.int getChildrenCount(int groupPosition)
传入组的位置,获取这一组的子元素数量,返回childrenData.get(groupPosition).size()即可。
3.Object getGroup(int groupPosition)
传入组的位置,获取组数据中这个位置的值。返回groupData.get(groupPosition)即可,注意这里默认返回的类型是Object,使用时可以将返回类型修改成实际的类型,如本例中,组数据中这个位置的值是String类型,所以可以将Object改为String
4.Object getChild(int groupPosition, int childPosition)
传入组的位置和子元素的位置,获取指定组的指定位置的子数据。返回childrenData.get(groupPosition).get(childPosition)即可,与getGroup类似,这里也可以将Object改为String
5.long getGroupId(int groupPosition)
获取组的id,通常返回groupPosition即可
6.long getChildId(int groupPosition, int childPosition)
获取子元素的id,通常返回childPosition即可
7.boolean hasStableIds()
id是否固定,即数据变化后,数据的id是否改变
8.View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent)
这个方法用来绑定组的布局和组的数据,使用LayoutInflater将布局绑定到convertView,并返回convertView。使用前面定义的GroupViewHolder为布局中的控件绑定数据。绑定组数据可以使用:
groupViewHolder.textView.setText(groupData.get(groupPosition));
或者是
groupViewHolder.textView.setText(getGroup(groupPosition));
注意这里还有一个参数isExpanded,它代表本组是否是展开状态,可以使用这个参数实现自定义展开和关闭状态的indicator
9.View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent)
这个方法用来绑定子元素的布局和子元素的数据,使用LayoutInflater将布局绑定到convertView,并返回convertView。使用前面定义的ChildViewHolder为布局中的控件绑定数据。绑定组数据可以使用:
childViewHolder.textView.setText(childrenData.get(groupPosition).get(childPosition));
或者是
childViewHolder.textView.setText(getChild(groupPosition, childPosition));
10.boolean isChildSelectable(int groupPosition, int childPosition)
子元素是否可选择,需要给子元素添加点击事件的话,这里必须返回true
6.给ExpandableListView添加适配器
MainActivity中,创建适配器,并给ExpandableListView添加适配器。
public class MainActivity extends AppCompatActivity {
private ExpandableListView list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
//传入数据,设置适配器
MyExpandableListViewAdapter adapter = new MyExpandableListViewAdapter(groupData,childrenData);
list.setAdapter(adapter);
}
}
7.给ExpandableListView添加点击事件
public class MainActivity extends AppCompatActivity {
private ExpandableListView list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
list.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
Toast.makeText(MainActivity.this, "点击了"+adapter.getGroup(groupPosition), Toast.LENGTH_SHORT).show();
return false;
}
});
list.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
Toast.makeText(MainActivity.this, "点击了"+adapter.getChild(groupPosition,childPosition), Toast.LENGTH_SHORT).show();
return true;
}
});
}
}
这样就实现了效果图中的效果
附:
1.MainActivity完整代码
public class MainActivity extends AppCompatActivity {
private ExpandableListView list;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
list = (ExpandableListView) findViewById(R.id.list);
//组数据,是一个List<T>
List<String> groupData = new ArrayList<>();
groupData.add("小学");
groupData.add("初中");
groupData.add("高中");
//子数据,每一组的子数据是一个List<T>;所以所有组的子数据是一个List<List<T>>
List<List<String>> childrenData = new ArrayList<>();
List<String> child1Data = new ArrayList<>();
child1Data.add("小学一年级");
child1Data.add("小学二年级");
child1Data.add("小学三年级");
child1Data.add("小学四年级");
child1Data.add("小学五年级");
child1Data.add("小学六年级");
List<String> child2Data = new ArrayList<>();
child2Data.add("初一");
child2Data.add("初二");
child2Data.add("初三");
List<String> child3Data = new ArrayList<>();
child3Data.add("高一");
child3Data.add("高二");
child3Data.add("高三");
childrenData.add(child1Data);
childrenData.add(child2Data);
childrenData.add(child3Data);
//传入数据,设置适配器
final MyExpandableListViewAdapter adapter = new MyExpandableListViewAdapter(groupData,childrenData);
list.setAdapter(adapter);
list.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
@Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
Toast.makeText(MainActivity.this, "点击了"+adapter.getGroup(groupPosition), Toast.LENGTH_SHORT).show();
return false;
}
});
list.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
@Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
Toast.makeText(MainActivity.this, "点击了"+adapter.getChild(groupPosition,childPosition), Toast.LENGTH_SHORT).show();
return true;
}
});
}
}
2.MyExpandableListViewAdapter完整代码
public class MyExpandableListViewAdapter extends BaseExpandableListAdapter{
private List<String> groupData;//组数据
private List<List<String>> childrenData;//子数据
private Context context;
public MyExpandableListViewAdapter(List<String> groupData,List<List<String>> childrenData){
this.groupData = groupData;
this.childrenData = childrenData;
}
public class GroupViewHolder{
TextView textView;
}
public class ChildViewHolder{
TextView textView;
}
//获取组数
@Override
public int getGroupCount() {
return groupData.size();
}
//获取指定组的子元素数
@Override
public int getChildrenCount(int groupPosition) {
return childrenData.get(groupPosition).size();
}
//获取指定组
@Override
public String getGroup(int groupPosition) {
return groupData.get(groupPosition);
}
//获取指定子元素
@Override
public String getChild(int groupPosition, int childPosition) {
return childrenData.get(groupPosition).get(childPosition);
}
//获取组的id
@Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
//获取子元素的id
@Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
//id是否固定,即数据变化后,数据的id是否改变
@Override
public boolean hasStableIds() {
return false;
}
//绑定组布局和组数据
@Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
context = parent.getContext();
convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null,false);
GroupViewHolder groupViewHolder = new GroupViewHolder();
groupViewHolder.textView = (TextView)convertView.findViewById(android.R.id.text1);
groupViewHolder.textView.setText(groupData.get(groupPosition));
groupViewHolder.textView.setText(getGroup(groupPosition));
return convertView;
}
//绑定子布局和子数据
@Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
convertView = LayoutInflater.from(context).inflate(android.R.layout.simple_list_item_1,null,false);
ChildViewHolder childViewHolder = new ChildViewHolder();
childViewHolder.textView = (TextView)convertView.findViewById(android.R.id.text1);
childViewHolder.textView.setText(childrenData.get(groupPosition).get(childPosition));
return convertView;
}
//子数据是否可选择
@Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}