ExpandableListView使用解析(三级列表的实现)

Posted Alex_MaHao

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ExpandableListView使用解析(三级列表的实现)相关的知识,希望对你有一定的参考价值。

ExpandableListView 使用解析(三级列表的实现)

在往常的设计中,往往有类似于QQ的二级列表的样式,而使用ExpandableListView变可以实现这种类似的效果。

当然,如果ExpandableListView嵌套ExpandableListView便可以实现三级列表,甚至多级条目,而本博客最终的实现效果便是三级条目。

首先看一下效果:

当然样式比较丑,这里只实现基本的逻辑。具体的样式可以根据自己的需要进行修改

ExpendableListView 的基本使用

万丈高楼平地起,想要实现三级列表肯定要从最简单的二级列表实现。下面就开始实现他的二级列表。

ExpendableListView的使用和ListView几乎一样:

  • 编写布局文件并查找ExpendableListView
  • 自定义适配器继承BaseExpandableListAdapter
  • setAdapter设置适配器。

下面开始进行实现:

  • 编写布局文件并查找控件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical" android:layout_width="match_parent"
    android:layout_height="match_parent">


    <ExpandableListView
        android:id="@+id/tree_view_simple"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
         >
    </ExpandableListView>


</LinearLayout>
  • 自定义ClassesExpandableListViewAdapter继承BaseExpandableListAdapter

在编写该类之前,我们必须要有我们的实体类。看一下我们定义的班级实体类Classes

/**
 *
 * 班级实体类
 * Created by MH on 2016/6/16.
 */
public class Classes 


    // 班级名
    public String name;


    // 班级中的学生列表
    public List<String> students;


ok,直接上ClassesExpandableListViewAdapter的代码,后面解释。

/**
 *
 * 班级的适配器
 * Created by MH on 2016/6/16.
 */
public class ClassesExpandableListViewAdapter extends BaseExpandableListAdapter 


    // 班级的集合
    private List<Classes> classes;

    // 创建布局使用
    private Activity activity;


    public ClassesExpandableListViewAdapter(List<Classes> classes, Activity activity) 
        this.classes = classes;
        this.activity = activity;
    

    @Override
    public int getGroupCount() 
        // 获取一级条目的数量  就是班级的大小
        return classes.size();
    

    @Override
    public int getChildrenCount(int groupPosition) 
        // 获取对应一级条目下二级条目的数量,就是各个班学生的数量
        return classes.get(groupPosition).students.size();
    

    @Override
    public Object getGroup(int groupPosition) 
        // 获取一级条目的对应数据  ,感觉没什么用
        return classes.get(groupPosition);
    

    @Override
    public Object getChild(int groupPosition, int childPosition) 
        // 获取对应一级条目下二级条目的对应数据  感觉没什么用
        return classes.get(groupPosition).students.get(childPosition);
    

    @Override
    public long getGroupId(int groupPosition) 
        // 直接返回,没什么用
        return groupPosition;
    

    @Override
    public long getChildId(int groupPosition, int childPosition) 
        // 直接返回,没什么用
        return childPosition;
    

    @Override
    public boolean hasStableIds() 
        // 谁知道这个是干什么。。。。
        return false;
    

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) 

        // 获取对应一级条目的View  和ListView 的getView相似

        return getGenericView(classes.get(groupPosition).name);
    

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) 

        // 获取对应二级条目的View  和ListView 的getView相似
        return getGenericView(classes.get(groupPosition).students.get(childPosition));
    

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) 
        // 根据方法名,此处应该表示二级条目是否可以被点击   先返回true 再讲
        return true;
    


    /**
     * 根据字符串生成布局,,因为我没有写layout.xml 所以用java 代码生成
     *
     *      实际中可以通过Inflate加载自己的自定义布局文件,设置数据之后并返回
     * @param string
     * @return
     */
    private TextView getGenericView(String string) 

        AbsListView.LayoutParams layoutParams = new AbsListView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);

        TextView textView = new TextView(activity);
        textView.setLayoutParams(layoutParams);

        textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);

        textView.setPadding(40, 20, 0, 20);
        textView.setText(string);
        textView.setTextColor(Color.RED);
        return textView;
    


代码贴完了,你以为我会解释。。呵呵,我才不呢。

注释很清楚,有几个关键方法

  • getGroupCount():获取一级条目的数量。
  • getChildrenCount():获取二级条目的数量。
  • getGroupView():获取一级条目的对应布局。
  • getChildView():获取二级条目对应的布局。

  • setAdapter()设置适配器

@Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_tree_view_simple);

        initData();

        // 查找控件
        listview = ((ExpandableListView) findViewById(R.id.tree_view_simple));


        //SimpleExpandableListViewAdapter adapter = new SimpleExpandableListViewAdapter(colleges,this);


        // 初始化数据
        List<Classes> classesList = new ArrayList<>();

        for(int i = 1 ;i<3;i++) 
            Classes classes = new Classes();

            classes.name = "计算机"+i+"班";

            List<String> list = new ArrayList<>();

            list.add("mm");
            list.add("dd");
            classes.students = list;

            classesList.add(classes);
        

        // 构造适配器
        ClassesExpandableListViewAdapter adapter = new ClassesExpandableListViewAdapter(classesList,this);


        // 设置适配器
        listview.setAdapter(adapter);


    

看一下效果:

实现三级列表

实现三级列表,从理解上来讲,无非是在返回二级条目时,返回一个ExpandableListView对象即可,但想象往往比实现容易,其中很多坑。。。

首先我们先来搞数据:

在班级之外,在添加一个大学(College

/**
 * 大学实体类
 * Created by MH on 2016/6/16.
 */
public class College 

    // 大学名
    public String name;

    // 班级列表
    public List<Classes> classList;

搞个数据

 /**
     * 初始化数据
     */
    private void initData() 


        College college = new College();

        college.name = "科技大学";

        List<Classes> classesList = new ArrayList<>();

        for(int i = 1 ;i<3;i++) 
            Classes classes = new Classes();

            classes.name = "计算机"+i+"班";

            List<String> list = new ArrayList<>();

            list.add("mm");
            list.add("dd");
            classes.students = list;

            classesList.add(classes);
        

        college.classList = classesList;


        colleges = new ArrayList<>();
        colleges.add(college);

    

那么此时的数据应该是

  • 科技大学
    • 计算机1班
      • mm
      • dd
    • 计算机2班
      • mm
      • dd

数据分析完之后,分析布局

在这里,有一个很关键的一点:

  • 在外层Expand中,他的所有二级条目都是一个,为什么,因为他具体的显示都交给了子ExpandableListView,二级条目的目的是为了把子ExpandableListView显示出来。

关键的一点分析了,上代码

/**
 *
 * 外层ExpandListView 适配器的实现
 * Created by MH on 2016/6/16.
 */
public class SimpleExpandableListViewAdapter extends BaseExpandableListAdapter 


    // 大学的集合
    private List<College> colleges;

    private Activity activity;


    public SimpleExpandableListViewAdapter(List<College> colleges, Activity activity) 
        this.colleges = colleges;
        this.activity = activity;
    

    @Override
    public int getGroupCount() 
        return colleges.size();
    

    @Override
    public int getChildrenCount(int groupPosition) 
        // 很关键,,一定要返回  1
        return 1;
    

    @Override
    public Object getGroup(int groupPosition) 
        return colleges.get(groupPosition);
    

    @Override
    public Object getChild(int groupPosition, int childPosition) 
        return colleges.get(groupPosition).classList.get(childPosition);
    

    @Override
    public long getGroupId(int groupPosition) 
        return groupPosition;
    

    @Override
    public long getChildId(int groupPosition, int childPosition) 
        return childPosition;
    

    @Override
    public boolean hasStableIds() 
        return false;
    

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) 

        return getGenericView(colleges.get(groupPosition).name);
    

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) 

        // 返回子ExpandableListView 的对象  此时传入是该父条目,即大学的对象(有歧义。。)

        return getGenericExpandableListView(colleges.get(groupPosition));
    

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) 
        return true;
    

    private TextView getGenericView(String string) 
        AbsListView.LayoutParams layoutParams = new AbsListView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);

        TextView textView = new TextView(activity);
        textView.setLayoutParams(layoutParams);

        textView.setGravity(Gravity.CENTER_VERTICAL | Gravity.LEFT);

        textView.setPadding(40, 20, 0, 20);
        textView.setText(string);
        textView.setTextColor(Color.RED);
        return textView;
    


    /**
     *  返回子ExpandableListView 的对象  此时传入的是该大学下所有班级的集合。
     * @param college
     * @return
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    public ExpandableListView getGenericExpandableListView(College college)
        AbsListView.LayoutParams layoutParams = new AbsListView.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                ViewGroup.LayoutParams.WRAP_CONTENT);

        CustomExpandableListView view = new CustomExpandableListView(activity);

        // 加载班级的适配器
        ClassesExpandableListViewAdapter adapter = new ClassesExpandableListViewAdapter(college.classList,activity);

        view.setAdapter(adapter);

        view.setPadding(20,0,0,0);
        return view;
    

代码依然一大串,但其实和之前写的ClassExpandableListViewAdapter都差不多一样,主要有以下几点区别:

  • getChildrenCount() 返回1.
  • getChildView() 返回的是ExpandableListView对象。
  • getGenericExpandableListView(): 添加了获取ExpandableListView对象的方法。

可能在这里,观察仔细的会发现getGenericExpandableListView()中创建的是CustomExpandableListView对象,该对象是我自定义的。很多人可能都遇到过,ScrollView嵌套ListViewListView显示不全。同样在这里也出现了,子ExpandableListView同样显示不全,所以自定义解决这个问题。

/**
 *
 * 自定义ExpandableListView  解决嵌套之下显示不全的问题
 * Created by MH on 2016/6/16.
 */
public class CustomExpandableListView extends ExpandableListView 


    public CustomExpandableListView(Context context) 
        super(context);
    

    public CustomExpandableListView(Context context, AttributeSet attrs, int defStyleAttr) 
        super(context, attrs, defStyleAttr);
    

    public CustomExpandableListView(Context context, AttributeSet attrs) 
        super(context, attrs);
    

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) 
        // 解决显示不全的问题
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2
                , MeasureSpec.AT_MOST);
        super.onMeasure(widthMeasureSpec, expandSpec);
    

ok ,告一段落。最后在看一下Main中的操作:

public class SimpleExpandListViewActivity extends AppCompatActivity 


    private ExpandableListView listview;

    private List<College> colleges;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) 
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_tree_view_simple);

        initData();

        // 查找控件
        listview = ((ExpandableListView) findViewById(R.id.tree_view_simple));


        SimpleExpandableListViewAdapter adapter = new SimpleExpandableListViewAdapter(colleges,this);

        // 设置适配器
        listview.setAdapter(adapter);
/*
        // 初始化数据
        List<Classes> classesList = new ArrayList<>();

        for(int i = 1 ;i<3;i++) 
            Classes classes = new Classes();

            classes.name = "计算机"+i+"班";

            List<String> list = new ArrayList<>();

            list.add("mm");
            list.add("dd");
            classes.students = list;

            classesList.add(classes);
        */

        // 构造适配器
       // ClassesExpandableListViewAdapter adapter = new ClassesExpandableListViewAdapter(classesList,this);





    

    /**
     * 初始化数据
     */
    private void initData() 


        College college = new College();

        college.name = "科技大学";

        List<Classes> classesList = new ArrayList<>();

        for(int i = 1 ;i<3;i++) 
            Classes classes = new Classes();

            classes.name = "计算机"+i+"班";

            List<String> list = new ArrayList<>();

            list.add("mm");
            list.add("dd");
            classes.students = list;

            classesList.add(classes);
        

        college.classList = classesList;


        colleges = new ArrayList<>();
        colleges.add(college);
    


该项目源码已上传github。有需要者请移步。

以上是关于ExpandableListView使用解析(三级列表的实现)的主要内容,如果未能解决你的问题,请参考以下文章

ExpandableListView:从孩子那里获取输入值

ExpandableListView使用-ScrollView嵌套ExpandableListView,列表显示不全

IOC的三级缓存图文详细解析(含如何解决循环依赖问题)

少儿编程 电子学会图形化编程等级考试Scratch三级真题解析(判断题)2022年9月

Android:ExpandableListView使用

使用来自 firebase 的数据创建 ExpandableListView