Android  ExpandableListView

来源:互联网 发布:python numpy range 编辑:程序博客网 时间:2024/05/21 17:17
一、ExpandableListView介绍

    一个垂直滚动的显示两个级别(Child,Group)列表项的视图,列表项来自ExpandableListAdapter 。组可以单独展开。

  1.重要方法

      expandGroup(int groupPos) :在分组列表视图中展开一组,

      setSelectedGroup(int groupPosition) :设置选择指定的组。

      setSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup) :设置选择指定的子项。

      getPackedPositionGroup(long packedPosition) :返回所选择的组

      getPackedPositionForChild(int groupPosition, int childPosition) :返回所选择的子项

      getPackedPositionType(long packedPosition) :返回所选择项的类型(Child,Group)

      isGroupExpanded(int groupPosition) :判断此组是否展开


ExpandableListAdapter

    一个接口,将基础数据链接到一个ExpandableListView。此接口的实施将提供访问Child的数据(由组分类),并实例化的Child和Group。

  1.重要方法

    getChildId(int groupPosition, int childPosition)获取与在给定组给予孩子相关的数据。

    getChildrenCount(int groupPosition) 返回在指定Group的Child数目。


        2.上代码

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">public class MyExpandableListAdapter extends BaseExpandableListAdapter {    private List<String> groupData;    private List<List<String>> childrenData;    private Context context;    public MyExpandableListAdapter(Context context, List<String> groupData, List<List<String>> childrenData) {        this.context = context;        this.groupData = groupData;        this.childrenData = childrenData;    }    @Override    public int getGroupCount() {        return groupData.size();    }    @Override    public int getChildrenCount(int i) {        return childrenData.get(i).size();    }    @Override    public Object getGroup(int i) {        return groupData.get(i);    }    @Override    public Object getChild(int i, int i1) {        return childrenData.get(i).get(i1);    }    @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 b, View convertView, ViewGroup viewGroup) {        GroupHolder groupHolder = null;        if (convertView == null) {            convertView = View.inflate(context, R.layout.item_head, null);            groupHolder = new GroupHolder();            groupHolder.txt = (TextView) convertView.findViewById(R.id.txt);            convertView.setTag(groupHolder);        } else {            groupHolder = (GroupHolder) convertView.getTag();        }        groupHolder.txt.setText(groupData.get(groupPosition));        return convertView;    }    @Override    public View getChildView(int groupPosition, int childPosition,                             boolean isLastChild, View convertView, ViewGroup parent) {        ItemHolder itemHolder = null;        if (convertView == null) {            convertView = View.inflate(context, R.layout.item_child, null);            itemHolder = new ItemHolder();            itemHolder.tv_mes = (TextView) convertView.findViewById(R.id.tv_mes);            convertView.setTag(itemHolder);        } else {            itemHolder = (ItemHolder) convertView.getTag();        }        itemHolder.tv_mes.setText(childrenData.get(groupPosition).get(                childPosition));        return convertView;    }    @Override    public boolean isChildSelectable(int i, int i1) {        return true;    }    class GroupHolder {        public TextView txt;    }    class ItemHolder {        public TextView tv_mes;    }}</span></span></span>
3.MainActivity的代码
<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">public class MainActivity extends Activity implements  ExpandableListView.OnChildClickListener {    private List<String> groupArray;    private List<List<String>> childArray;    private ExpandableListView expandableListView;    private MyExpandableListAdapter mMyExpandableListAdapter;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        initView();        initData();    }    private void initData() {        //随便一堆测试数据        groupArray = new ArrayList<String>();        groupArray.add("A");        groupArray.add("B");        groupArray.add("C");        List<String> item_list1 = new ArrayList<String>();        item_list1.add("1");        item_list1.add("2");        item_list1.add("3");        List<String> item_list2 = new ArrayList<String>();        item_list2.add("4");        item_list2.add("5");        item_list2.add("6");        List<String> item_list3 = new ArrayList<String>();        item_list3.add("7");        item_list3.add("8");        item_list3.add("9");        childArray = new ArrayList<List<String>>();        childArray.add(item_list1);        childArray.add(item_list2);        childArray.add(item_list3);        mMyExpandableListAdapter = new MyExpandableListAdapter(MainActivity.this, groupArray, childArray);        expandableListView.setAdapter(mMyExpandableListAdapter);        for (int i = 0; i < mMyExpandableListAdapter.getGroupCount(); i++) {//展开所有父分组            expandableListView.expandGroup(i);        }    }    private void initView() {        expandableListView = (ExpandableListView) findViewById(R.id.expandableListView);        expandableListView.setOnChildClickListener(this);    }    @Override    public boolean onChildClick(ExpandableListView expandableListView, View view, int groupPosition, int childPosition, long l) {        Toast.makeText(MainActivity.this, "点击了" + childArray.get(groupPosition).get(childPosition), Toast.LENGTH_SHORT).show();        return true;    }}</span></span></span>
效果如图

4.expandableListview它是如何定义长按事件的呢?

  1.   @Override  
  2.         public boolean onItemLongClick(AdapterView<?> arg0, View view,  
  3.                 int pos, long id) {  
  4.             //pos不可用说明见下文  
  5.             return false;  
  6.         } 

如果这个方法是用在ListView长按事件中刚刚好,但在ExpandableListView中,第三个参数pos不能区分开点击的是父项还是子项,以及哪个父项或子项。

在ExpandableListView响应的onItemLongCkick方法中,pos参数值为:从上到下,父项+展现的子项到点击位置的数目(注意:是展现的,隐藏的子项不包括,从0开始)。

例如:

父项1(隐藏3个子项)

父项2

 |—子项2-0

 |—子项2-1

 |—子项2-2

长按子项2-1时,pos值为3。显然根据pos值是无法确定点击的是哪个子项或父项的。

因此依赖pos是很难处理点击位置的。

如果可以直接在onItemLongClick方法中获取groupPos,及childPos该多好呢?

下面我就来所说有几种方法:

1.这种方法是通过下标来找到位置,

<span style="font-size:18px;"><span style="font-size:18px;"><span style="font-size:18px;">  @Override      public boolean onItemLongClick(AdapterView<?> arg0, View view,              int pos, long id) {        long packedPosition = mExpandableListView.getExpandableListPosition(i);int itemType = ExpandableListView.getPackedPositionType(packedPosition);int groupPosition = ExpandableListView.getPackedPositionGroup(packedPosition);int childPosition = ExpandableListView.getPackedPositionChild(packedPosition);  //根据groupPos判断你长按的是哪个父项,做相应处理(弹框等)          } else {              //根据groupPos及childPos判断你长按的是哪个父项下的哪个子项,然后做相应处理。           }          return false;      }  </span>            //根据groupPos判断你长按的是哪个父项,做相应处理(弹框等)          } else {              //根据groupPos及childPos判断你长按的是哪个父项下的哪个子项,然后做相应处理。           }          return false;      }  </span></span><pre name="code" class="html"><span style="font-size:18px;">PositionMetadata getUnflattenedPos(final int flPos) {        /* Keep locally since frequent use */        final ArrayList<GroupMetadata> egml = mExpGroupMetadataList;        final int numExpGroups = egml.size();                /* Binary search variables */        int leftExpGroupIndex = 0;        int rightExpGroupIndex = numExpGroups - 1;        int midExpGroupIndex = 0;        GroupMetadata midExpGm;                 if (numExpGroups == 0) {            /*             * There aren't any expanded groups (hence no visible children             * either), so flPos must be a group and its group pos will be the             * same as its flPos             */            return PositionMetadata.obtain(flPos, ExpandableListPosition.GROUP, flPos,                    -1, null, 0);        }        /*         * Binary search over the expanded groups to find either the exact         * expanded group (if we're looking for a group) or the group that         * contains the child we're looking for. If we are looking for a         * collapsed group, we will not have a direct match here, but we will         * find the expanded group just before the group we're searching for (so         * then we can calculate the group position of the group we're searching         * for). If there isn't an expanded group prior to the group being         * searched for, then the group being searched for's group position is         * the same as the flat list position (since there are no children before         * it, and all groups before it are collapsed).         */        while (leftExpGroupIndex <= rightExpGroupIndex) {            midExpGroupIndex =                    (rightExpGroupIndex - leftExpGroupIndex) / 2                            + leftExpGroupIndex;            midExpGm = egml.get(midExpGroupIndex);                        if (flPos > midExpGm.lastChildFlPos) {                /*                 * The flat list position is after the current middle group's                 * last child's flat list position, so search right                 */                leftExpGroupIndex = midExpGroupIndex + 1;            } else if (flPos < midExpGm.flPos) {                /*                 * The flat list position is before the current middle group's                 * flat list position, so search left                 */                rightExpGroupIndex = midExpGroupIndex - 1;            } else if (flPos == midExpGm.flPos) {                /*                 * The flat list position is this middle group's flat list                 * position, so we've found an exact hit                 */                return PositionMetadata.obtain(flPos, ExpandableListPosition.GROUP,                        midExpGm.gPos, -1, midExpGm, midExpGroupIndex);            } else if (flPos <= midExpGm.lastChildFlPos                    /* && flPos > midGm.flPos as deduced from previous                     * conditions */) {                /* The flat list position is a child of the middle group */                                /*                  * Subtract the first child's flat list position from the                 * specified flat list pos to get the child's position within                 * the group                 */                final int childPos = flPos - (midExpGm.flPos + 1);                return PositionMetadata.obtain(flPos, ExpandableListPosition.CHILD,                        midExpGm.gPos, childPos, midExpGm, midExpGroupIndex);            }         }        /*          * If we've reached here, it means the flat list position must be a         * group that is not expanded, since otherwise we would have hit it         * in the above search.         */        /**         * If we are to expand this group later, where would it go in the         * mExpGroupMetadataList ?         */        int insertPosition = 0;                /** What is its group position in the list of all groups? */        int groupPos = 0;                /*         * To figure out exact insertion and prior group positions, we need to         * determine how we broke out of the binary search.  We backtrack         * to see this.         */         if (leftExpGroupIndex > midExpGroupIndex) {                        /*             * This would occur in the first conditional, so the flat list             * insertion position is after the left group. Also, the             * leftGroupPos is one more than it should be (since that broke out             * of our binary search), so we decrement it.             */              final GroupMetadata leftExpGm = egml.get(leftExpGroupIndex-1);                        insertPosition = leftExpGroupIndex;            /*             * Sums the number of groups between the prior exp group and this             * one, and then adds it to the prior group's group pos             */            groupPos =                (flPos - leftExpGm.lastChildFlPos) + leftExpGm.gPos;                    } else if (rightExpGroupIndex < midExpGroupIndex) {            /*             * This would occur in the second conditional, so the flat list             * insertion position is before the right group. Also, the             * rightGroupPos is one less than it should be, so increment it.             */            final GroupMetadata rightExpGm = egml.get(++rightExpGroupIndex);                        insertPosition = rightExpGroupIndex;                        /*             * Subtracts this group's flat list pos from the group after's flat             * list position to find out how many groups are in between the two             * groups. Then, subtracts that number from the group after's group             * pos to get this group's pos.             */            groupPos = rightExpGm.gPos - (rightExpGm.flPos - flPos);        } else {            // TODO: clean exit            throw new RuntimeException("Unknown state");        }                return PositionMetadata.obtain(flPos, ExpandableListPosition.GROUP, groupPos, -1,                null, insertPosition);    }    /**     * Translates either a group pos or a child pos (+ group it belongs to) to a     * flat list position.  If searching for a child and its group is not expanded, this will     * return null since the child isn't being shown in the ListView, and hence it has no     * position.     *      * @param pos a {@link ExpandableListPosition} representing either a group position     *        or child position     * @return the flat list position encompassed in a {@link PositionMetadata}     *         object that contains additional useful info for insertion, etc., or null.     */    PositionMetadata getFlattenedPos(final ExpandableListPosition pos) {        final ArrayList<GroupMetadata> egml = mExpGroupMetadataList;        final int numExpGroups = egml.size();        /* Binary search variables */        int leftExpGroupIndex = 0;        int rightExpGroupIndex = numExpGroups - 1;        int midExpGroupIndex = 0;        GroupMetadata midExpGm;                 if (numExpGroups == 0) {            /*             * There aren't any expanded groups, so flPos must be a group and             * its flPos will be the same as its group pos.  The             * insert position is 0 (since the list is empty).             */            return PositionMetadata.obtain(pos.groupPos, pos.type,                    pos.groupPos, pos.childPos, null, 0);        }        /*         * Binary search over the expanded groups to find either the exact         * expanded group (if we're looking for a group) or the group that         * contains the child we're looking for.         */        while (leftExpGroupIndex <= rightExpGroupIndex) {            midExpGroupIndex = (rightExpGroupIndex - leftExpGroupIndex)/2 + leftExpGroupIndex;            midExpGm = egml.get(midExpGroupIndex);                        if (pos.groupPos > midExpGm.gPos) {                /*                 * It's after the current middle group, so search right                 */                leftExpGroupIndex = midExpGroupIndex + 1;            } else if (pos.groupPos < midExpGm.gPos) {                /*                 * It's before the current middle group, so search left                 */                rightExpGroupIndex = midExpGroupIndex - 1;            } else if (pos.groupPos == midExpGm.gPos) {                /*                 * It's this middle group, exact hit                 */                                if (pos.type == ExpandableListPosition.GROUP) {                    /* If it's a group, give them this matched group's flPos */                    return PositionMetadata.obtain(midExpGm.flPos, pos.type,                            pos.groupPos, pos.childPos, midExpGm, midExpGroupIndex);                } else if (pos.type == ExpandableListPosition.CHILD) {                    /* If it's a child, calculate the flat list pos */                    return PositionMetadata.obtain(midExpGm.flPos + pos.childPos                            + 1, pos.type, pos.groupPos, pos.childPos,                            midExpGm, midExpGroupIndex);                } else {                    return null;                }            }         }        /*          * If we've reached here, it means there was no match in the expanded         * groups, so it must be a collapsed group that they're search for         */        if (pos.type != ExpandableListPosition.GROUP) {            /* If it isn't a group, return null */            return null;        }                /*         * To figure out exact insertion and prior group positions, we need to         * determine how we broke out of the binary search. We backtrack to see         * this.         */         if (leftExpGroupIndex > midExpGroupIndex) {                        /*             * This would occur in the first conditional, so the flat list             * insertion position is after the left group.             *              * The leftGroupPos is one more than it should be (from the binary             * search loop) so we subtract 1 to get the actual left group.  Since             * the insertion point is AFTER the left group, we keep this +1             * value as the insertion point             */              final GroupMetadata leftExpGm = egml.get(leftExpGroupIndex-1);                        final int flPos =                    leftExpGm.lastChildFlPos                            + (pos.groupPos - leftExpGm.gPos);            return PositionMetadata.obtain(flPos, pos.type, pos.groupPos,                    pos.childPos, null, leftExpGroupIndex);        } else if (rightExpGroupIndex < midExpGroupIndex) {            /*             * This would occur in the second conditional, so the flat list             * insertion position is before the right group. Also, the             * rightGroupPos is one less than it should be (from binary search             * loop), so we increment to it.             */            final GroupMetadata rightExpGm = egml.get(++rightExpGroupIndex);                        final int flPos =                    rightExpGm.flPos                            - (rightExpGm.gPos - pos.groupPos);            return PositionMetadata.obtain(flPos, pos.type, pos.groupPos,                    pos.childPos, null, rightExpGroupIndex);        } else {            return null;        }    }</span>

 基本就是我们高中学的数学那套,找规律,判断点击的child是在第几组,在判断是第几个。2.这种方法我看了一下,和第一种方法差不多,这种是通过id来判断位置
getExpandableListView().setOnItemLongClickListener(new OnItemLongClickListener() {    @Override    public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {        if (ExpandableListView.getPackedPositionType(id) == ExpandableListView.PACKED_POSITION_TYPE_CHILD) {            int groupPosition = ExpandableListView.getPackedPositionGroup(id);            int childPosition = ExpandableListView.getPackedPositionChild(id);            // You now have everything that you would as if this was an OnChildClickListener()             // Add your logic here.            // Return true as we are handling the event.            return true;        }        return false;    }});

3,(这是网上搜的,你说写就写呗,还不让人看明白了,自己被坑了)

<span style="font-size:18px;"><span style="font-size:18px;">使用上下文菜单实现长按事件注册上下文菜单 registerForContextMenu(downElv);,并重新下面两个方法:@Overridepublic void onCreateContextMenu(ContextMenu menu, View v,ContextMenuInfo menuInfo) {super.onCreateContextMenu(menu, v, menuInfo);ExpandableListView.ExpandableListContextMenuInfo info = (ExpandableListView.ExpandableListContextMenuInfo) menuInfo;//menuinfo该对象提供了选中对象的附加信息int type = ExpandableListView.getPackedPositionType(info.packedPosition);int group = ExpandableListView.getPackedPositionGroup(info.packedPosition);int child = ExpandableListView.getPackedPositionChild(info.packedPosition);System.out.println("LongClickListener*type-------------------------"+ type);System.out.println("LongClickListener*group-------------------------"+ group);System.out.println("LongClickListener*child-------------------------"+ child);this.setMGroupID(group);this.setMChildrenID(child);//处理逻辑if (type == 0) {// 分组长按事件showDialog(Globals.DIALOG_GROUPS_LONGCLICK);} else if (type == 1) {// 长按好友列表项   //showDialog(Globals.DIALOG_FRIENDlIST_LONGCLICK);可以自定义Dialog显示,也可以使用menu.add(0,0,0,"增加");添加菜单项}}//一般在此函数下面编写响应事件@Overridepublic boolean onContextItemSelected(MenuItem item) {return super.onContextItemSelected(item);}</span></span>

4,(这也是网上说的最多的,自己也被坑了,不知道view是复用的啊)看到了onItemLongClick方法第二个参数:view。这里的view是你按中的位置对应的view。view有个方法getTag(int key)。如果在创建此view的时候就把groupPos,childPos通过setTag(int key, Object value)设置进去,在响应onItemLongClick不就可以直接拿出来用了么。

现在就要讲到必须使用自定义的BaseExpandableListAdapter的理由了。

要把groupPos,childPos通过setTag的方式绑定到view中,就必须操作该view的创建过程。要控制这个过程就必须要在自定义BaseExpandableListAdapter中重写getGroupView及getChildView方法进行操作。如下:

  @Override      public View getChildView(int groupPosition, int childPosition,              boolean isLastChild, View convertView, ViewGroup parent) {          //我这里仅通过自己写的mkChildView()方法创建TextView来显示文字,更复杂的可以通过LayoutInflater来填充一个view          TextView childTv = mkChildView();          // 标记位置          // 必须使用资源Id当key(不是资源id会出现运行时异常),android本意应该是想用tag来保存资源id对应组件。          // 将groupPosition,childPosition通过setTag保存,在onItemLongClick方法中就可以通过view参数直接拿到了!                  childTv.setTag(R.id.xxx01, groupPosition);          childTv.setTag(R.id.xxx02, childPosition);          return childTv;      }        @Override      public View getGroupView(int groupPosition, boolean isExpanded,              View convertView, ViewGroup parent) {          TextView groupTv = mkGroupView();          // 设置同getChildView一样          groupTv.setTag(R.id.xxx01, groupPosition);          groupTv.setTag(R.id.xxx02, -1); //设置-1表示长按时点击的是父项,到时好判断。          groupTv.setText(groups[groupPosition]);          return groupTv;      }  }  
通过这个expandablelistview,自己看到了自己很多不足,自己要多努力了。






2 0
原创粉丝点击