写这篇的原因说起来很惭愧,面试的时候被问怎么实现二级list,我没接触过expandablelistview支支吾吾的回答用listview扩展getcount(),面试官说expandablelistview很基础的东西你都没用过,接着便是一些我也回答不上了的问题。。。
回归正题,ExpandableListView是用于实现二级listview的控件,其用法几乎和listview一样,在xml中添加,代码中获取id,实现适配器,设置适配器。不同的是,使用自定义ExpandableListView时所需设置的适配器的一些方法需要重写。这里以实现3级list作为一个例子。
首先是数据的准备,这里简单准备一下数据
<pre style="background-color:#1e1e1a;color:#a9b7c6;font-family:'Consolas';font-size:9.0pt;"><span style="color:#fd9720;">private </span><span style="color:#f63ce6;">Map</span><span style="color:#fd9720;"><</span><span style="color:#f4005d;">String</span><span style="color:#ea882f;">, </span><span style="color:#f63ce6;">Map</span><span style="color:#fd9720;"><</span><span style="color:#f4005d;">String</span><span style="color:#ea882f;">, </span><span style="color:#f63ce6;">List</span><span style="color:#fd9720;"><</span><span style="color:#f4005d;">String</span><span style="color:#fd9720;">>>> </span><span style="color:#58c5e7;">dataMap </span><span style="color:#fd9720;">= new </span><span style="color:#b4dc27;">HashMap</span><span style="color:#fd9720;"><></span><span style="color:#efefe8;">()</span><span style="color:#fd9720;">;
</span><span style="color:#fd9720;">private </span><span style="color:#f4005d;">String</span><span style="color:#efefe8;">[] </span><span style="color:#58c5e7;">str1 </span><span style="color:#fd9720;">= </span><span style="color:#f6f6ef;">{</span><span style="color:#e0d067;">"first"</span><span style="color:#ea882f;">, </span><span style="color:#e0d067;">"second"</span><span style="color:#ea882f;">, </span><span style="color:#e0d067;">"third"</span><span style="color:#f6f6ef;">}</span><span style="color:#fd9720;">;
</span><span style="color:#fd9720;">private </span><span style="color:#f4005d;">String</span><span style="color:#efefe8;">[] </span><span style="color:#58c5e7;">str2 </span><span style="color:#fd9720;">= </span><span style="color:#f6f6ef;">{</span><span style="color:#e0d067;">"1"</span><span style="color:#ea882f;">, </span><span style="color:#e0d067;">"2"</span><span style="color:#ea882f;">, </span><span style="color:#e0d067;">"3"</span><span style="color:#ea882f;">, </span><span style="color:#e0d067;">"4"</span><span style="color:#f6f6ef;">}</span><span style="color:#fd9720;">;
</span><span style="color:#fd9720;">private </span><span style="color:#f63ce6;">Map</span><span style="color:#fd9720;"><</span><span style="color:#f4005d;">String</span><span style="color:#ea882f;">, </span><span style="color:#f63ce6;">List</span><span style="color:#fd9720;"><</span><span style="color:#f4005d;">String</span><span style="color:#fd9720;">>> </span><span style="color:#58c5e7;">childMap </span><span style="color:#fd9720;">= new </span><span style="color:#b4dc27;">HashMap</span><span style="color:#fd9720;"><></span><span style="color:#efefe8;">()</span><span style="color:#fd9720;">;
</span><span style="color:#fd9720;">private </span><span style="color:#f63ce6;">List</span><span style="color:#fd9720;"><</span><span style="color:#f4005d;">String</span><span style="color:#fd9720;">> </span><span style="color:#58c5e7;">childList </span><span style="color:#fd9720;">= new </span><span style="color:#b4dc27;">ArrayList</span><span style="color:#fd9720;"><></span><span style="color:#efefe8;">()</span><span style="color:#fd9720;">;</span>
Map嵌套,简单的放一下数据,然后是初始化数据
private void initData(){ childList.add("f1"); childList.add("f2"); childList.add("f3"); childMap.put(str2[0], childList); childMap.put(str2[1], childList); childMap.put(str2[2], childList); childMap.put(str2[3], childList); dataMap.put(str1[0], childMap); dataMap.put(str1[1], childMap); dataMap.put(str1[2], childMap); }
最重要的来了,我们需要为自己的ExpandableListView写适配器,继承自BaseExpandableListAdapter
private class GroupExpandableListViewAdapter extends BaseExpandableListAdapter{ @Override public Object getGroup(int parentPos){ return dataMap.get(str1[parentPos]); } @Override public Object getChild(int parentPos, int childPos){ return dataMap.get(str1[parentPos]).get(childPos); } @Override public int getGroupCount(){ return dataMap.size(); } @Override public int getChildrenCount(int parentPos){ return 1; } @Override public long getGroupId(int parentPos){ return parentPos; } @Override public long getChildId(int parentPos, int childPos){ return childPos; } @Override public boolean hasStableIds(){ return false; } @Override public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup){ if(view == null){ LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.group_item, null); } view.setTag(R.layout.group_item, parentPos); view.setTag(R.layout.child_item, -1); TextView groupText = (TextView) view.findViewById(R.id.group_item); groupText.setText(str1[parentPos]); return view; } @Override public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup){ if(view == null){ LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.child_listview, null); } view.setTag(R.layout.group_item, parentPos); view.setTag(R.layout.child_item, childPos); ChildExpandableListView childExpandableListView = (ChildExpandableListView) view.findViewById(R.id.child_listview); ChildExpandableListViewAdapter childExpandableLVAdapter = new ChildExpandableListViewAdapter(); childExpandableListView.setAdapter(childExpandableLVAdapter); return view; } @Override public boolean isChildSelectable(int i, int i1){ return true; } }这里解释一下,最重要的两个:getGroupView是获取list中父级内容的,第一层的父级很简单,只有一个TextView;
getChildView是获取list中子项内容的。想一下,由于要实现3级的list,第一季的list子项自然也是ExpandableListView,而且每一个父项里只有一个ExpandableListView。
在getChildView中获取到第二级的ExpandableListView,并且实现它的适配器(注意第一级和第二级的适配器类型是不一样的),并设置好适配器
需要注意的是,第一级中isChildSelectable的返回值一定要设为true,该方法是用于判断子项能否被点击的,我们实现3级菜单,所以必然要设为true。
所以这里getChildCount返回的子项个数是1,我们只需要一个ExpandableListView来表示子项内容,子项里具体的东西再交给下一级的getGroupView来实现。
这里我用了自定义的ChildExpandableListView,之所以自定义是因为,双层的ExpandableListView嵌套会使第一层子项的内容显示不全,百度后得知需要重绘他的高度,这里是ChildExpandableListView的代码:
public class ChildExpandableListView extends ExpandableListView { public ChildExpandableListView(Context context) { super(context); } public ChildExpandableListView(Context context, AttributeSet attrs) { super(context, attrs); } public ChildExpandableListView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec){ int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2, MeasureSpec.AT_MOST); super.onMeasure(widthMeasureSpec, expandSpec); } }
最后是第二级ExpandableListView,很简单,是普通的ExpandableListView的用法,父项只有一个TExtView,子项也只有一个TextView。
private class ChildExpandableListViewAdapter extends BaseExpandableListAdapter{ @Override public Object getGroup(int parentPos){ return childMap.get(str2[parentPos]); } @Override public Object getChild(int parentPos, int childPos){ return childMap.get(str2[parentPos]).get(childPos); } @Override public int getGroupCount(){ return childMap.size(); } @Override public int getChildrenCount(int parentPos){ return childMap.get(str2[parentPos]).size(); } @Override public long getGroupId(int parentPos){ return parentPos; } @Override public long getChildId(int parentPos, int childPos){ return childPos; } @Override public boolean hasStableIds(){ return false; } @Override public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup){ if(view == null){ LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.child_item, null); } view.setTag(R.layout.group_item, parentPos); view.setTag(R.layout.child_item, -1); TextView childText = (TextView) view.findViewById(R.id.child_item); childText.setText(str2[parentPos]); return view; } @Override public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup){ if(view == null){ LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.child_child_item, null); } view.setTag(R.layout.group_item, parentPos); view.setTag(R.layout.child_item, childPos); TextView child_child_text = (TextView) view.findViewById(R.id.child_child_item); child_child_text.setText(childMap.get(str2[parentPos]).get(childPos)); return view; } @Override public boolean isChildSelectable(int i, int i1){ return true; } }第二级中只需要加载父项和子项的内容即可。
最后实现效果:
以下是全部代码:
package com.example.yxc.expandlistviewtest; import android.content.Context; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.ArrayAdapter; import android.widget.BaseExpandableListAdapter; import android.widget.ExpandableListView; import android.widget.ListView; import android.widget.TextView; import android.widget.Toast; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; public class MainActivity extends AppCompatActivity { private ExpandableListView expandableListView; private Map<String, Map<String, List<String>>> dataMap = new HashMap<>(); private String[] str1 = {"first", "second", "third"}; private String[] str2 = {"1", "2", "3", "4"}; private Map<String, List<String>> childMap = new HashMap<>(); private List<String> childList = new ArrayList<>(); private GroupExpandableListViewAdapter mExpandableListAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); initData(); expandableListView = (ExpandableListView) findViewById(R.id.listview); mExpandableListAdapter = new GroupExpandableListViewAdapter(); expandableListView.setAdapter(mExpandableListAdapter); expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() { @Override public boolean onChildClick(ExpandableListView expandableListView, View view, int parentPos, int childPos, long l) { Toast.makeText(MainActivity.this, "you press the childitem " + childMap.get(str2[parentPos]).get(childPos), Toast.LENGTH_SHORT).show(); return false; } }); } private void initData(){ childList.add("f1"); childList.add("f2"); childList.add("f3"); childMap.put(str2[0], childList); childMap.put(str2[1], childList); childMap.put(str2[2], childList); childMap.put(str2[3], childList); dataMap.put(str1[0], childMap); dataMap.put(str1[1], childMap); dataMap.put(str1[2], childMap); } private class GroupExpandableListViewAdapter extends BaseExpandableListAdapter{ @Override public Object getGroup(int parentPos){ return dataMap.get(str1[parentPos]); } @Override public Object getChild(int parentPos, int childPos){ return dataMap.get(str1[parentPos]).get(childPos); } @Override public int getGroupCount(){ return dataMap.size(); } @Override public int getChildrenCount(int parentPos){ return 1; } @Override public long getGroupId(int parentPos){ return parentPos; } @Override public long getChildId(int parentPos, int childPos){ return childPos; } @Override public boolean hasStableIds(){ return false; } @Override public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup){ if(view == null){ LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.group_item, null); } view.setTag(R.layout.group_item, parentPos); view.setTag(R.layout.child_item, -1); TextView groupText = (TextView) view.findViewById(R.id.group_item); groupText.setText(str1[parentPos]); return view; } @Override public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup){ if(view == null){ LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.child_listview, null); } view.setTag(R.layout.group_item, parentPos); view.setTag(R.layout.child_item, childPos); ChildExpandableListView childExpandableListView = (ChildExpandableListView) view.findViewById(R.id.child_listview); ChildExpandableListViewAdapter childExpandableLVAdapter = new ChildExpandableListViewAdapter(); childExpandableListView.setAdapter(childExpandableLVAdapter); return view; } @Override public boolean isChildSelectable(int i, int i1){ return true; } } private class ChildExpandableListViewAdapter extends BaseExpandableListAdapter{ @Override public Object getGroup(int parentPos){ return childMap.get(str2[parentPos]); } @Override public Object getChild(int parentPos, int childPos){ return childMap.get(str2[parentPos]).get(childPos); } @Override public int getGroupCount(){ return childMap.size(); } @Override public int getChildrenCount(int parentPos){ return childMap.get(str2[parentPos]).size(); } @Override public long getGroupId(int parentPos){ return parentPos; } @Override public long getChildId(int parentPos, int childPos){ return childPos; } @Override public boolean hasStableIds(){ return false; } @Override public View getGroupView(int parentPos, boolean b, View view, ViewGroup viewGroup){ if(view == null){ LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.child_item, null); } view.setTag(R.layout.group_item, parentPos); view.setTag(R.layout.child_item, -1); TextView childText = (TextView) view.findViewById(R.id.child_item); childText.setText(str2[parentPos]); return view; } @Override public View getChildView(int parentPos, int childPos, boolean b, View view, ViewGroup viewGroup){ if(view == null){ LayoutInflater inflater = (LayoutInflater) MainActivity.this.getSystemService(Context.LAYOUT_INFLATER_SERVICE); view = inflater.inflate(R.layout.child_child_item, null); } view.setTag(R.layout.group_item, parentPos); view.setTag(R.layout.child_item, childPos); TextView child_child_text = (TextView) view.findViewById(R.id.child_child_item); child_child_text.setText(childMap.get(str2[parentPos]).get(childPos)); return view; } @Override public boolean isChildSelectable(int i, int i1){ return true; } } }