ExpandableListView一点点

1,520 阅读5分钟
原文链接: blog.csdn.net

1. 简介

看下源码中的英文介绍

/**
* A view that shows items in a vertically scrolling two-level list. This
* differs from the {@link ListView} by allowing two levels: groups which can
* individually be expanded to show its children. The items come from the
* {@link ExpandableListAdapter} associated with this view.
*


* Expandable lists are able to show an indicator beside each item to display
* the item’s current state (the states are usually one of expanded group,
* collapsed group, child, or last child). Use
* {@link #setChildIndicator(Drawable)} or {@link #setGroupIndicator(Drawable)}
* (or the corresponding XML attributes) to set these indicators (see the docs
* for each method to see additional state that each Drawable can have). The
* default style for an {@link ExpandableListView} provides indicators which
* will be shown next to Views given to the {@link ExpandableListView}. The
* layouts Android.R.layout.simple_expandable_list_item_1 and
* android.R.layout.simple_expandable_list_item_2 (which should be used with
* {@link SimpleCursorTreeAdapter}) contain the preferred position information
* for indicators.
*


* The context menu information set by an {@link ExpandableListView} will be a
* {@link ExpandableListContextMenuInfo} object with
* {@link ExpandableListContextMenuInfo#packedPosition} being a packed position
* that can be used with {@link #getPackedPositionType(long)} and the other
* similar methods.
*


* Note: You cannot use the value wrap_content
* for the android:layout_height attribute of a
* ExpandableListView in XML if the parent’s size is also not strictly specified
* (for example, if the parent were ScrollView you could not specify
* wrap_content since it also can be any length. However, you can use
* wrap_content if the ExpandableListView parent has a specific size, such as
* 100 pixels.
*
* @attr ref android.R.styleable#ExpandableListView_groupIndicator
* @attr ref android.R.styleable#ExpandableListView_indicatorLeft
* @attr ref android.R.styleable#ExpandableListView_indicatorRight
* @attr ref android.R.styleable#ExpandableListView_childIndicator
* @attr ref android.R.styleable#ExpandableListView_childIndicatorLeft
* @attr ref android.R.styleable#ExpandableListView_childIndicatorRight
* @attr ref android.R.styleable#ExpandableListView_childDivider
* @attr ref android.R.styleable#ExpandableListView_indicatorStart
* @attr ref android.R.styleable#ExpandableListView_indicatorEnd
* @attr ref android.R.styleable#ExpandableListView_childIndicatorStart
* @attr ref android.R.styleable#ExpandableListView_childIndicatorEnd
*/

ExpandableListView是不同于ListView的两级列表View,可以收起可以展开。通过ExpandableListAdapter来配置需要现实的items。


2. 基本使用

2.1 布局文件

group.xml(一级节点item布局)

<?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="wrap_content"
    android:paddingLeft="30dp"
    android:orientation="vertical" >
    <TextView
        android:id="@+id/group_text"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:paddingTop="10dip"
        android:paddingBottom="10dip"
        android:text=""
        />
</LinearLayout>

child.xml(二级节点item)

<?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"
    android:paddingLeft="40dp">

    <TextView
        android:id="@+id/textOne"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:text="" />
</LinearLayout>

actiivty_main.xml(主界面布局)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="cn.onlyloveyd.expandablelistviewdemo.MainActivity">


    <ExpandableListView
        android:id="@+id/expandable_list"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:groupIndicator="@drawable/expand"
        android:childIndicator="@drawable/collapse"
        />

</LinearLayout>

2.2 代码

YEAdapter.Java(可伸展列表对应的Adapter)

public class YEAdapter extends BaseExpandableListAdapter {

    //一级节点数据
    private List<GroupItem> mGroupItems;
    private Context mContext;

    public YEAdapter(Context context, List<GroupItem> mData) {
        mGroupItems = mData;
        mContext = context;
    }

    /**
     * 一级节点数量
     * @return
     */
    @Override
    public int getGroupCount() {
        return mGroupItems.size();
    }

    /**
     * 指定位置一级节点下二级节点数量
     * @param groupPosition
     * @return
     */
    @Override
    public int getChildrenCount(int groupPosition) {
        return mGroupItems.get(groupPosition).mChildList.size();
    }

    /**
     * 获取一级节点对象
     * @param groupPosition
     * @return
     */
    @Override
    public Object getGroup(int groupPosition) {
        return mGroupItems.get(groupPosition);
    }

    /**
     * 获取二级节点对象
     * @param groupPosition
     * @param childPosition
     * @return
     */
    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return mGroupItems.get(groupPosition).mChildList.get(childPosition);
    }

    /**
     * 获取一级节点ID,这里用位置值表示
     * @param groupPosition
     * @return
     */
    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    /**
     * 获取二级节点ID,这里用位置值表示
     * @param groupPosition
     * @param childPosition
     * @return
     */
    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    /**
     * ID是否稳定
     * @return
     */
    @Override
    public boolean hasStableIds() {
        return true;
    }

    /**
     * 获取一级节点view
     * @param groupPosition
     * @param isExpanded
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        View view=null;
        TextView mTv;
        if(convertView!=null){
            view = convertView;
            mTv = (TextView) view.getTag();
        }else{
            view = View.inflate(mContext,R.layout.group, null);
            mTv = (TextView) view.findViewById(R.id.group_text);
            view.setTag(mTv);
        }
        mTv.setText(mGroupItems.get(groupPosition).title);
        return view;
    }


    /**
     * 获取二级节点View
     * @param groupPosition
     * @param childPosition
     * @param isLastChild
     * @param convertView
     * @param parent
     * @return
     */
    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        View view=null;
        TextView mTextView;
        if(convertView!=null){
            view = convertView;
            mTextView = (TextView) view.getTag();
        }else{
            view = View.inflate(mContext,R.layout.child, null);
            mTextView = (TextView) view.findViewById(R.id.textOne);
            view.setTag(mTextView);
        }
        mTextView.setText(mGroupItems.get(groupPosition).mChildList.get(childPosition).message);
        return view;
    }

    /**
     * 二级菜单是否可选(true为可选,false为不可选,也就是响应和不响应点击事件)
     * @param groupPosition
     * @param childPosition
     * @return
     */
    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return true;
    }

    /**
     * 一级节点对象
     */
    public static class GroupItem {
        String title;
        List<ChildItem> mChildList;
    }

    /**
     * 二级节点对象
     */
    public static class ChildItem {
        String message;
    }
}

具体解释请查阅注释信息

MainActivity.java

public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        List<YEAdapter.GroupItem> mGroupItems = new ArrayList<>();
        for (int i = 0; i < 5; i++) {
            YEAdapter.GroupItem item = new YEAdapter.GroupItem();
            item.title = "this is " + i + "nd group item";
            List<YEAdapter.ChildItem> mChildItems = new ArrayList<>();
            for (int j = 0; j < 4; j++) {
                YEAdapter.ChildItem item1 = new YEAdapter.ChildItem();
                item1.message = "this is " + i + "nd group item's " + j + "nd child";
                mChildItems.add(item1);
            }
            item.mChildList = mChildItems;
            mGroupItems.add(item);
        }


        ExpandableListView expandableListView = (ExpandableListView) this.findViewById(R.id.expandable_list);

        YEAdapter yeAdapter = new YEAdapter(this, mGroupItems);
        expandableListView.setAdapter(yeAdapter);

        expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
                Toast.makeText(MainActivity.this,  groupPosition + "nd group's " + childPosition + "nd Item is clicked!", Toast.LENGTH_SHORT).show();
                return false;
            }
        });

        expandableListView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
            @Override
            public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
                Toast.makeText(MainActivity.this, groupPosition + "nd group is clicked", Toast.LENGTH_SHORT).show();
                return false;
            }
        });

        expandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
            @Override
            public void onGroupCollapse(int groupPosition) {
                Toast.makeText(MainActivity.this, "the " + groupPosition + "nd group is collapsed", Toast.LENGTH_SHORT).show();

            }
        });

        expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
            @Override
            public void onGroupExpand(int groupPosition) {
                Toast.makeText(MainActivity.this, "the " + groupPosition + "nd group is expanded", Toast.LENGTH_SHORT).show();

            }
        });
    }
}

2.3 实际效果

这里写图片描述


3. 类结构

3.1 属性值

属性值 含义
groupIndicator 一级节点指示器
indicatorLeft 一级节点指示器距离左边界距离
indicatorRight 一级节点指示器距离右边界距离
childIndicator 意义同group中相同
childIndicatorLeft 意义同group中相同
childIndicatorRight 意义同group中相同
childDivider 设置二级节点的分割想,颜色或者drawable
indicatorStart 同indicatorLeft
indicatorEnd 同indicatorRight
childIndiatorStart 同childIndicatorLeft
childIndicatorEnd 同childIndicatorRight

主要看看分割线效果
设置图片
divider.xml

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid
        android:color="@color/colorAccent"/>
    <corners
        android:radius="3dp"/>
</shape>

这里写图片描述

设置颜色
android:childDivider=”@color/colorPrimary”
这里写图片描述

那么一级节点如何设置分割线?
看这个例子

        android:childDivider="@color/colorPrimary"
        android:divider="@drawable/divider"
        android:dividerHeight="2dp"

给一级菜单和二级菜单设置不同的分割线,看下效果

这里写图片描述

二级节点依然显示的时我们设置的分割线,但是当一级节点展开时,展开的节点的一级分割线变成了二级节点的分割线样式,收起后恢复成一级节点的分割线样式。

3.2 常用方法

这里写图片描述

具体内容请查阅源码。


4. 设置上下文菜单

4.1 expandable_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android" >
    <item
        android:id="@+id/menu_item1"
        android:title="菜单项1">
    </item>
    <item
        android:id="@+id/menu_item2"
        android:title="菜单项2">
    </item>

</menu>

4.2 onCreateContextMenu

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        menu.setHeaderTitle("expandable menu");
        getMenuInflater().inflate(R.menu.expandable_menu, menu);
    }

4.3 onContextItemSelected

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        // TODO Auto-generated method stub
        super.onContextItemSelected(item);
        switch (item.getItemId()) {
            case R.id.menu_item1:
                Toast.makeText(this, "this is first menu item", Toast.LENGTH_SHORT).show();
                break;
            case R.id.menu_item2:
                Toast.makeText(this, "this is second menu item", Toast.LENGTH_SHORT).show();
                break;
            default:
                break;
        }
        return super.onContextItemSelected(item);
    }

4.4 item长按事件显示菜单并注册上下文菜单

        expandableListView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                if (ExpandableListView.getPackedPositionType(id) == ExpandableListView.PACKED_POSITION_TYPE_CHILD)
                {
                    expandableListView.showContextMenu();
                    return true;
                }
                return false;
            }
        });
        registerForContextMenu(expandableListView);

5. 备注

程序源码