FileDocCategorySizeDatePackage
ExpandableListView.javaAPI DocAndroid 5.1 API53713Thu Mar 12 22:22:10 GMT 2015android.widget

ExpandableListView

public class ExpandableListView extends ListView
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

Fields Summary
public static final int
PACKED_POSITION_TYPE_GROUP
The packed position represents a group.
public static final int
PACKED_POSITION_TYPE_CHILD
The packed position represents a child.
public static final int
PACKED_POSITION_TYPE_NULL
The packed position represents a neither/null/no preference.
public static final long
PACKED_POSITION_VALUE_NULL
The value for a packed position that represents neither/null/no preference. This value is not otherwise possible since a group type (first bit 0) should not have a child position filled.
private static final long
PACKED_POSITION_MASK_CHILD
The mask (in packed position representation) for the child
private static final long
PACKED_POSITION_MASK_GROUP
The mask (in packed position representation) for the group
private static final long
PACKED_POSITION_MASK_TYPE
The mask (in packed position representation) for the type
private static final long
PACKED_POSITION_SHIFT_GROUP
The shift amount (in packed position representation) for the group
private static final long
PACKED_POSITION_SHIFT_TYPE
The shift amount (in packed position representation) for the type
private static final long
PACKED_POSITION_INT_MASK_CHILD
The mask (in integer child position representation) for the child
private static final long
PACKED_POSITION_INT_MASK_GROUP
The mask (in integer group position representation) for the group
private ExpandableListConnector
mConnector
Serves as the glue/translator between a ListView and an ExpandableListView
private ExpandableListAdapter
mAdapter
Gives us Views through group+child positions
private int
mIndicatorLeft
Left bound for drawing the indicator.
private int
mIndicatorRight
Right bound for drawing the indicator.
private int
mIndicatorStart
Start bound for drawing the indicator.
private int
mIndicatorEnd
End bound for drawing the indicator.
private int
mChildIndicatorLeft
Left bound for drawing the indicator of a child. Value of {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorLeft.
private int
mChildIndicatorRight
Right bound for drawing the indicator of a child. Value of {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorRight.
private int
mChildIndicatorStart
Start bound for drawing the indicator of a child. Value of {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorStart.
private int
mChildIndicatorEnd
End bound for drawing the indicator of a child. Value of {@link #CHILD_INDICATOR_INHERIT} means use mIndicatorEnd.
public static final int
CHILD_INDICATOR_INHERIT
Denotes when a child indicator should inherit this bound from the generic indicator bounds
private static final int
INDICATOR_UNDEFINED
Denotes an undefined value for an indicator
private android.graphics.drawable.Drawable
mGroupIndicator
The indicator drawn next to a group.
private android.graphics.drawable.Drawable
mChildIndicator
The indicator drawn next to a child.
private static final int[]
EMPTY_STATE_SET
private static final int[]
GROUP_EXPANDED_STATE_SET
State indicating the group is expanded.
private static final int[]
GROUP_EMPTY_STATE_SET
State indicating the group is empty (has no children).
private static final int[]
GROUP_EXPANDED_EMPTY_STATE_SET
State indicating the group is expanded and empty (has no children).
private static final int[]
GROUP_STATE_SETS
States for the group where the 0th bit is expanded and 1st bit is empty.
private static final int[]
CHILD_LAST_STATE_SET
State indicating the child is the last within its group.
private android.graphics.drawable.Drawable
mChildDivider
Drawable to be used as a divider when it is adjacent to any children
private final android.graphics.Rect
mIndicatorRect
private OnGroupCollapseListener
mOnGroupCollapseListener
private OnGroupExpandListener
mOnGroupExpandListener
private OnGroupClickListener
mOnGroupClickListener
private OnChildClickListener
mOnChildClickListener
Constructors Summary
public ExpandableListView(android.content.Context context)


       
        this(context, null);
    
public ExpandableListView(android.content.Context context, android.util.AttributeSet attrs)

        this(context, attrs, com.android.internal.R.attr.expandableListViewStyle);
    
public ExpandableListView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr)

        this(context, attrs, defStyleAttr, 0);
    
public ExpandableListView(android.content.Context context, android.util.AttributeSet attrs, int defStyleAttr, int defStyleRes)

        super(context, attrs, defStyleAttr, defStyleRes);

        final TypedArray a = context.obtainStyledAttributes(attrs,
                com.android.internal.R.styleable.ExpandableListView, defStyleAttr, defStyleRes);

        mGroupIndicator = a.getDrawable(
                com.android.internal.R.styleable.ExpandableListView_groupIndicator);
        mChildIndicator = a.getDrawable(
                com.android.internal.R.styleable.ExpandableListView_childIndicator);
        mIndicatorLeft = a.getDimensionPixelSize(
                com.android.internal.R.styleable.ExpandableListView_indicatorLeft, 0);
        mIndicatorRight = a.getDimensionPixelSize(
                com.android.internal.R.styleable.ExpandableListView_indicatorRight, 0);
        if (mIndicatorRight == 0 && mGroupIndicator != null) {
            mIndicatorRight = mIndicatorLeft + mGroupIndicator.getIntrinsicWidth();
        }
        mChildIndicatorLeft = a.getDimensionPixelSize(
                com.android.internal.R.styleable.ExpandableListView_childIndicatorLeft,
                CHILD_INDICATOR_INHERIT);
        mChildIndicatorRight = a.getDimensionPixelSize(
                com.android.internal.R.styleable.ExpandableListView_childIndicatorRight,
                CHILD_INDICATOR_INHERIT);
        mChildDivider = a.getDrawable(
                com.android.internal.R.styleable.ExpandableListView_childDivider);

        if (!isRtlCompatibilityMode()) {
            mIndicatorStart = a.getDimensionPixelSize(
                    com.android.internal.R.styleable.ExpandableListView_indicatorStart,
                    INDICATOR_UNDEFINED);
            mIndicatorEnd = a.getDimensionPixelSize(
                    com.android.internal.R.styleable.ExpandableListView_indicatorEnd,
                    INDICATOR_UNDEFINED);

            mChildIndicatorStart = a.getDimensionPixelSize(
                    com.android.internal.R.styleable.ExpandableListView_childIndicatorStart,
                    CHILD_INDICATOR_INHERIT);
            mChildIndicatorEnd = a.getDimensionPixelSize(
                    com.android.internal.R.styleable.ExpandableListView_childIndicatorEnd,
                    CHILD_INDICATOR_INHERIT);
        }

        a.recycle();
    
Methods Summary
public booleancollapseGroup(int groupPos)
Collapse a group in the grouped list view

param
groupPos position of the group to collapse
return
True if the group was collapsed, false otherwise (if the group was already collapsed, this will return false)

        boolean retValue = mConnector.collapseGroup(groupPos);
        
        if (mOnGroupCollapseListener != null) {
            mOnGroupCollapseListener.onGroupCollapse(groupPos);
        }

        return retValue;
    
android.view.ContextMenu.ContextMenuInfocreateContextMenuInfo(android.view.View view, int flatListPosition, long id)

        if (isHeaderOrFooterPosition(flatListPosition)) {
            // Return normal info for header/footer view context menus
            return new AdapterContextMenuInfo(view, flatListPosition, id);
        }

        final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
        PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition);
        ExpandableListPosition pos = pm.position;
        
        id = getChildOrGroupId(pos);
        long packedPosition = pos.getPackedPosition();

        pm.recycle();
        
        return new ExpandableListContextMenuInfo(view, packedPosition, id);
    
protected voiddispatchDraw(android.graphics.Canvas canvas)

        // Draw children, etc.
        super.dispatchDraw(canvas);

        // If we have any indicators to draw, we do it here
        if ((mChildIndicator == null) && (mGroupIndicator == null)) {
            return;
        }

        int saveCount = 0;
        final boolean clipToPadding = (mGroupFlags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
        if (clipToPadding) {
            saveCount = canvas.save();
            final int scrollX = mScrollX;
            final int scrollY = mScrollY;
            canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
                    scrollX + mRight - mLeft - mPaddingRight,
                    scrollY + mBottom - mTop - mPaddingBottom);
        }

        final int headerViewsCount = getHeaderViewsCount();
        
        final int lastChildFlPos = mItemCount - getFooterViewsCount() - headerViewsCount - 1;

        final int myB = mBottom; 
        
        PositionMetadata pos;
        View item;
        Drawable indicator; 
        int t, b;
        
        // Start at a value that is neither child nor group
        int lastItemType = ~(ExpandableListPosition.CHILD | ExpandableListPosition.GROUP);

        final Rect indicatorRect = mIndicatorRect;

        // The "child" mentioned in the following two lines is this
        // View's child, not referring to an expandable list's
        // notion of a child (as opposed to a group)
        final int childCount = getChildCount(); 
        for (int i = 0, childFlPos = mFirstPosition - headerViewsCount; i < childCount;
             i++, childFlPos++) {

            if (childFlPos < 0) {
                // This child is header
                continue;
            } else if (childFlPos > lastChildFlPos) {
                // This child is footer, so are all subsequent children
                break;
            }
            
            item = getChildAt(i);
            t = item.getTop();
            b = item.getBottom();
            
            // This item isn't on the screen
            if ((b < 0) || (t > myB)) continue;

            // Get more expandable list-related info for this item
            pos = mConnector.getUnflattenedPos(childFlPos);

            final boolean isLayoutRtl = isLayoutRtl();
            final int width = getWidth();

            // If this item type and the previous item type are different, then we need to change
            // the left & right bounds
            if (pos.position.type != lastItemType) {
                if (pos.position.type == ExpandableListPosition.CHILD) {
                    indicatorRect.left = (mChildIndicatorLeft == CHILD_INDICATOR_INHERIT) ?
                            mIndicatorLeft : mChildIndicatorLeft;
                    indicatorRect.right = (mChildIndicatorRight == CHILD_INDICATOR_INHERIT) ?
                            mIndicatorRight : mChildIndicatorRight;
                } else {
                    indicatorRect.left = mIndicatorLeft;
                    indicatorRect.right = mIndicatorRight;
                }

                if (isLayoutRtl) {
                    final int temp = indicatorRect.left;
                    indicatorRect.left = width - indicatorRect.right;
                    indicatorRect.right = width - temp;

                    indicatorRect.left -= mPaddingRight;
                    indicatorRect.right -= mPaddingRight;
                } else {
                    indicatorRect.left += mPaddingLeft;
                    indicatorRect.right += mPaddingLeft;
                }

                lastItemType = pos.position.type; 
            }

            if (indicatorRect.left != indicatorRect.right) {
                // Use item's full height + the divider height
                if (mStackFromBottom) {
                    // See ListView#dispatchDraw
                    indicatorRect.top = t;// - mDividerHeight;
                    indicatorRect.bottom = b;
                } else {
                    indicatorRect.top = t;
                    indicatorRect.bottom = b;// + mDividerHeight;
                }
                
                // Get the indicator (with its state set to the item's state)
                indicator = getIndicator(pos);
                if (indicator != null) {
                    // Draw the indicator
                    indicator.setBounds(indicatorRect);
                    indicator.draw(canvas);
                }
            }
            pos.recycle();
        }

        if (clipToPadding) {
            canvas.restoreToCount(saveCount);
        }
    
voiddrawDivider(android.graphics.Canvas canvas, android.graphics.Rect bounds, int childIndex)

        int flatListPosition = childIndex + mFirstPosition;
        
        // Only proceed as possible child if the divider isn't above all items (if it is above
        // all items, then the item below it has to be a group)
        if (flatListPosition >= 0) {
            final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
            PositionMetadata pos = mConnector.getUnflattenedPos(adjustedPosition);
            // If this item is a child, or it is a non-empty group that is expanded
            if ((pos.position.type == ExpandableListPosition.CHILD) || (pos.isExpanded() &&
                    pos.groupMetadata.lastChildFlPos != pos.groupMetadata.flPos)) {
                // These are the cases where we draw the child divider
                final Drawable divider = mChildDivider;
                divider.setBounds(bounds);
                divider.draw(canvas);
                pos.recycle();
                return;
            }
            pos.recycle();
        }
        
        // Otherwise draw the default divider
        super.drawDivider(canvas, bounds, flatListPosition);
    
public booleanexpandGroup(int groupPos)
Expand a group in the grouped list view

param
groupPos the group to be expanded
return
True if the group was expanded, false otherwise (if the group was already expanded, this will return false)

       return expandGroup(groupPos, false);
    
public booleanexpandGroup(int groupPos, boolean animate)
Expand a group in the grouped list view

param
groupPos the group to be expanded
param
animate true if the expanding group should be animated in
return
True if the group was expanded, false otherwise (if the group was already expanded, this will return false)

        ExpandableListPosition elGroupPos = ExpandableListPosition.obtain(
                ExpandableListPosition.GROUP, groupPos, -1, -1);
        PositionMetadata pm = mConnector.getFlattenedPos(elGroupPos);
        elGroupPos.recycle();
        boolean retValue = mConnector.expandGroup(pm);

        if (mOnGroupExpandListener != null) {
            mOnGroupExpandListener.onGroupExpand(groupPos);
        }

        if (animate) {
            final int groupFlatPos = pm.position.flatListPos;

            final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount();
            smoothScrollToPosition(shiftedGroupPosition + mAdapter.getChildrenCount(groupPos),
                    shiftedGroupPosition);
        }
        pm.recycle();

        return retValue;
    
private intgetAbsoluteFlatPosition(int flatListPosition)
Converts a group/child flat position into an absolute flat position, that takes into account the possible headers.

param
flatListPosition The child/group flat position
return
An absolute flat position.

        return flatListPosition + getHeaderViewsCount();
    
public ListAdaptergetAdapter()
This method should not be used, use {@link #getExpandableListAdapter()}.

        /*
         * The developer should never really call this method on an
         * ExpandableListView, so it would be nice to throw a RuntimeException,
         * but AdapterView calls this
         */
        return super.getAdapter();
    
private longgetChildOrGroupId(ExpandableListPosition position)
Gets the ID of the group or child at the given position. This is useful since there is no ListAdapter ID -> ExpandableListAdapter ID conversion mechanism (in some cases, it isn't possible).

param
position The position of the child or group whose ID should be returned.

        if (position.type == ExpandableListPosition.CHILD) {
            return mAdapter.getChildId(position.groupPos, position.childPos);
        } else {
            return mAdapter.getGroupId(position.groupPos);
        }
    
public ExpandableListAdaptergetExpandableListAdapter()
Gets the adapter that provides data to this view.

return
The adapter that provides data to this view.

        return mAdapter;
    
public longgetExpandableListPosition(int flatListPosition)
Converts a flat list position (the raw position of an item (child or group) in the list) to a group and/or child position (represented in a packed position). This is useful in situations where the caller needs to use the underlying {@link ListView}'s methods. Use {@link ExpandableListView#getPackedPositionType} , {@link ExpandableListView#getPackedPositionChild}, {@link ExpandableListView#getPackedPositionGroup} to unpack.

param
flatListPosition The flat list position to be converted.
return
The group and/or child position for the given flat list position in packed position representation. #PACKED_POSITION_VALUE_NULL if the position corresponds to a header or a footer item.

        if (isHeaderOrFooterPosition(flatListPosition)) {
            return PACKED_POSITION_VALUE_NULL;
        }

        final int adjustedPosition = getFlatPositionForConnector(flatListPosition);
        PositionMetadata pm = mConnector.getUnflattenedPos(adjustedPosition);
        long packedPos = pm.position.getPackedPosition();
        pm.recycle();
        return packedPos;
    
public intgetFlatListPosition(long packedPosition)
Converts a group and/or child position to a flat list position. This is useful in situations where the caller needs to use the underlying {@link ListView}'s methods.

param
packedPosition The group and/or child positions to be converted in packed position representation. Use {@link #getPackedPositionForChild(int, int)} or {@link #getPackedPositionForGroup(int)}.
return
The flat list position for the given child or group.

        ExpandableListPosition elPackedPos = ExpandableListPosition
                .obtainPosition(packedPosition);
        PositionMetadata pm = mConnector.getFlattenedPos(elPackedPos);
        elPackedPos.recycle();
        final int flatListPosition = pm.position.flatListPos;
        pm.recycle();
        return getAbsoluteFlatPosition(flatListPosition);
    
private intgetFlatPositionForConnector(int flatListPosition)
Converts an absolute item flat position into a group/child flat position, shifting according to the number of header items.

param
flatListPosition The absolute flat position
return
A group/child flat position as expected by the connector.

        return flatListPosition - getHeaderViewsCount();
    
private android.graphics.drawable.DrawablegetIndicator(android.widget.ExpandableListConnector.PositionMetadata pos)
Gets the indicator for the item at the given position. If the indicator is stateful, the state will be given to the indicator.

param
pos The flat list position of the item whose indicator should be returned.
return
The indicator in the proper state.

        Drawable indicator;
        
        if (pos.position.type == ExpandableListPosition.GROUP) {
            indicator = mGroupIndicator;
            
            if (indicator != null && indicator.isStateful()) {
                // Empty check based on availability of data.  If the groupMetadata isn't null,
                // we do a check on it. Otherwise, the group is collapsed so we consider it
                // empty for performance reasons.
                boolean isEmpty = (pos.groupMetadata == null) ||
                        (pos.groupMetadata.lastChildFlPos == pos.groupMetadata.flPos);
                
                final int stateSetIndex =
                    (pos.isExpanded() ? 1 : 0) | // Expanded?
                    (isEmpty ? 2 : 0); // Empty?
                indicator.setState(GROUP_STATE_SETS[stateSetIndex]);
            }
        } else {
            indicator = mChildIndicator;
            
            if (indicator != null && indicator.isStateful()) {
                // No need for a state sets array for the child since it only has two states
                final int stateSet[] = pos.position.flatListPos == pos.groupMetadata.lastChildFlPos
                        ? CHILD_LAST_STATE_SET
                        : EMPTY_STATE_SET;
                indicator.setState(stateSet);
            }
        }
        
        return indicator;
    
public static intgetPackedPositionChild(long packedPosition)
Gets the child position from a packed position that is of {@link #PACKED_POSITION_TYPE_CHILD} type (use {@link #getPackedPositionType(long)}). To get the group that this child belongs to, use {@link #getPackedPositionGroup(long)}. See {@link #getPackedPositionForChild(int, int)}.

param
packedPosition The packed position from which the child position will be returned.
return
The child position portion of the packed position. If this does not contain a child, returns -1.

        // Null
        if (packedPosition == PACKED_POSITION_VALUE_NULL) return -1;
        
        // Group since a group type clears this bit
        if ((packedPosition & PACKED_POSITION_MASK_TYPE) != PACKED_POSITION_MASK_TYPE) return -1;

        return (int) (packedPosition & PACKED_POSITION_MASK_CHILD);
    
public static longgetPackedPositionForChild(int groupPosition, int childPosition)
Returns the packed position representation of a child's position.

In general, a packed position should be used in situations where the position given to/returned from an {@link ExpandableListAdapter} or {@link ExpandableListView} method can either be a child or group. The two positions are packed into a single long which can be unpacked using {@link #getPackedPositionChild(long)}, {@link #getPackedPositionGroup(long)}, and {@link #getPackedPositionType(long)}.

param
groupPosition The child's parent group's position.
param
childPosition The child position within the group.
return
The packed position representation of the child (and parent group).

        return (((long)PACKED_POSITION_TYPE_CHILD) << PACKED_POSITION_SHIFT_TYPE)
                | ((((long)groupPosition) & PACKED_POSITION_INT_MASK_GROUP)
                        << PACKED_POSITION_SHIFT_GROUP)
                | (childPosition & PACKED_POSITION_INT_MASK_CHILD);  
    
public static longgetPackedPositionForGroup(int groupPosition)
Returns the packed position representation of a group's position. See {@link #getPackedPositionForChild(int, int)}.

param
groupPosition The child's parent group's position.
return
The packed position representation of the group.

        // No need to OR a type in because PACKED_POSITION_GROUP == 0
        return ((((long)groupPosition) & PACKED_POSITION_INT_MASK_GROUP)
                        << PACKED_POSITION_SHIFT_GROUP); 
    
public static intgetPackedPositionGroup(long packedPosition)
Gets the group position from a packed position. See {@link #getPackedPositionForChild(int, int)}.

param
packedPosition The packed position from which the group position will be returned.
return
The group position portion of the packed position. If this does not contain a group, returns -1.

        // Null
        if (packedPosition == PACKED_POSITION_VALUE_NULL) return -1;
        
        return (int) ((packedPosition & PACKED_POSITION_MASK_GROUP) >> PACKED_POSITION_SHIFT_GROUP);
    
public static intgetPackedPositionType(long packedPosition)
Gets the type of a packed position. See {@link #getPackedPositionForChild(int, int)}.

param
packedPosition The packed position for which to return the type.
return
The type of the position contained within the packed position, either {@link #PACKED_POSITION_TYPE_CHILD}, {@link #PACKED_POSITION_TYPE_GROUP}, or {@link #PACKED_POSITION_TYPE_NULL}.

        if (packedPosition == PACKED_POSITION_VALUE_NULL) {
            return PACKED_POSITION_TYPE_NULL;
        }
        
        return (packedPosition & PACKED_POSITION_MASK_TYPE) == PACKED_POSITION_MASK_TYPE
                ? PACKED_POSITION_TYPE_CHILD
                : PACKED_POSITION_TYPE_GROUP;
    
public longgetSelectedId()
Gets the ID of the currently selected group or child. Can return -1 if no selection.

return
The ID of the currently selected group or child. -1 if no selection.

        long packedPos = getSelectedPosition();
        if (packedPos == PACKED_POSITION_VALUE_NULL) return -1;

        int groupPos = getPackedPositionGroup(packedPos);
        
        if (getPackedPositionType(packedPos) == PACKED_POSITION_TYPE_GROUP) {
            // It's a group
            return mAdapter.getGroupId(groupPos);
        } else {
            // It's a child
            return mAdapter.getChildId(groupPos, getPackedPositionChild(packedPos));
        }
    
public longgetSelectedPosition()
Gets the position of the currently selected group or child (along with its type). Can return {@link #PACKED_POSITION_VALUE_NULL} if no selection.

return
A packed position containing the currently selected group or child's position and type. #PACKED_POSITION_VALUE_NULL if no selection or if selection is on a header or a footer item.

        final int selectedPos = getSelectedItemPosition();

        // The case where there is no selection (selectedPos == -1) is also handled here.
        return getExpandableListPosition(selectedPos);
    
booleanhandleItemClick(android.view.View v, int position, long id)
This will either expand/collapse groups (if a group was clicked) or pass on the click to the proper child (if a child was clicked)

param
position The flat list position. This has already been factored to remove the header/footer.
param
id The ListAdapter ID, not the group or child ID.

        final PositionMetadata posMetadata = mConnector.getUnflattenedPos(position);
        
        id = getChildOrGroupId(posMetadata.position);
        
        boolean returnValue;
        if (posMetadata.position.type == ExpandableListPosition.GROUP) {
            /* It's a group, so handle collapsing/expanding */

            /* It's a group click, so pass on event */
            if (mOnGroupClickListener != null) {
                if (mOnGroupClickListener.onGroupClick(this, v,
                        posMetadata.position.groupPos, id)) {
                    posMetadata.recycle();
                    return true;
                }
            }

            if (posMetadata.isExpanded()) {
                /* Collapse it */
                mConnector.collapseGroup(posMetadata);

                playSoundEffect(SoundEffectConstants.CLICK);

                if (mOnGroupCollapseListener != null) {
                    mOnGroupCollapseListener.onGroupCollapse(posMetadata.position.groupPos);
                }
            } else {
                /* Expand it */
                mConnector.expandGroup(posMetadata);

                playSoundEffect(SoundEffectConstants.CLICK);

                if (mOnGroupExpandListener != null) {
                    mOnGroupExpandListener.onGroupExpand(posMetadata.position.groupPos);
                }
                
                final int groupPos = posMetadata.position.groupPos;
                final int groupFlatPos = posMetadata.position.flatListPos;

                final int shiftedGroupPosition = groupFlatPos + getHeaderViewsCount(); 
                smoothScrollToPosition(shiftedGroupPosition + mAdapter.getChildrenCount(groupPos),
                        shiftedGroupPosition);
            }

            returnValue = true;
        } else {
            /* It's a child, so pass on event */
            if (mOnChildClickListener != null) {
                playSoundEffect(SoundEffectConstants.CLICK);
                return mOnChildClickListener.onChildClick(this, v, posMetadata.position.groupPos,
                        posMetadata.position.childPos, id);
            }

            returnValue = false;
        }

        posMetadata.recycle();

        return returnValue;
    
private booleanhasRtlSupport()
Return true if the application tag in the AndroidManifest has set "supportRtl" to true

        return mContext.getApplicationInfo().hasRtlSupport();
    
public booleanisGroupExpanded(int groupPosition)
Whether the given group is currently expanded.

param
groupPosition The group to check.
return
Whether the group is currently expanded.

        return mConnector.isGroupExpanded(groupPosition);
    
private booleanisHeaderOrFooterPosition(int position)

param
position An absolute (including header and footer) flat list position.
return
true if the position corresponds to a header or a footer item.

        final int footerViewsStart = mItemCount - getFooterViewsCount();
        return (position < getHeaderViewsCount() || position >= footerViewsStart);
    
private booleanisRtlCompatibilityMode()
Return true if we are in RTL compatibility mode (either before Jelly Bean MR1 or RTL not supported)

        final int targetSdkVersion = mContext.getApplicationInfo().targetSdkVersion;
        return targetSdkVersion < JELLY_BEAN_MR1 || !hasRtlSupport();
    
public voidonInitializeAccessibilityEvent(android.view.accessibility.AccessibilityEvent event)

        super.onInitializeAccessibilityEvent(event);
        event.setClassName(ExpandableListView.class.getName());
    
public voidonInitializeAccessibilityNodeInfo(android.view.accessibility.AccessibilityNodeInfo info)

        super.onInitializeAccessibilityNodeInfo(info);
        info.setClassName(ExpandableListView.class.getName());
    
public voidonRestoreInstanceState(android.os.Parcelable state)

        if (!(state instanceof SavedState)) {
            super.onRestoreInstanceState(state);
            return;
        }

        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        
        if (mConnector != null && ss.expandedGroupMetadataList != null) {
            mConnector.setExpandedGroupMetadataList(ss.expandedGroupMetadataList);
        }
    
public voidonRtlPropertiesChanged(int layoutDirection)

        resolveIndicator();
        resolveChildIndicator();
    
public android.os.ParcelableonSaveInstanceState()

    

    
       
        Parcelable superState = super.onSaveInstanceState();
        return new SavedState(superState,
                mConnector != null ? mConnector.getExpandedGroupMetadataList() : null);
    
public booleanperformItemClick(android.view.View v, int position, long id)

        // Ignore clicks in header/footers
        if (isHeaderOrFooterPosition(position)) {
            // Clicked on a header/footer, so ignore pass it on to super
            return super.performItemClick(v, position, id);
        }
        
        // Internally handle the item click
        final int adjustedPosition = getFlatPositionForConnector(position);
        return handleItemClick(v, adjustedPosition, id);
    
private voidresolveChildIndicator()
Resolve start/end child indicator. start/end child indicator always takes precedence over left/right child indicator when defined.

        final boolean isLayoutRtl = isLayoutRtl();
        if (isLayoutRtl) {
            if (mChildIndicatorStart >= CHILD_INDICATOR_INHERIT) {
                mChildIndicatorRight = mChildIndicatorStart;
            }
            if (mChildIndicatorEnd >= CHILD_INDICATOR_INHERIT) {
                mChildIndicatorLeft = mChildIndicatorEnd;
            }
        } else {
            if (mChildIndicatorStart >= CHILD_INDICATOR_INHERIT) {
                mChildIndicatorLeft = mChildIndicatorStart;
            }
            if (mChildIndicatorEnd >= CHILD_INDICATOR_INHERIT) {
                mChildIndicatorRight = mChildIndicatorEnd;
            }
        }
    
private voidresolveIndicator()
Resolve start/end indicator. start/end indicator always takes precedence over left/right indicator when defined.

        final boolean isLayoutRtl = isLayoutRtl();
        if (isLayoutRtl) {
            if (mIndicatorStart >= 0) {
                mIndicatorRight = mIndicatorStart;
            }
            if (mIndicatorEnd >= 0) {
                mIndicatorLeft = mIndicatorEnd;
            }
        } else {
            if (mIndicatorStart >= 0) {
                mIndicatorLeft = mIndicatorStart;
            }
            if (mIndicatorEnd >= 0) {
                mIndicatorRight = mIndicatorEnd;
            }
        }
        if (mIndicatorRight == 0 && mGroupIndicator != null) {
            mIndicatorRight = mIndicatorLeft + mGroupIndicator.getIntrinsicWidth();
        }
    
public voidsetAdapter(ListAdapter adapter)
This overloaded method should not be used, instead use {@link #setAdapter(ExpandableListAdapter)}.

{@inheritDoc}

        throw new RuntimeException(
                "For ExpandableListView, use setAdapter(ExpandableListAdapter) instead of " +
                "setAdapter(ListAdapter)");
    
public voidsetAdapter(ExpandableListAdapter adapter)
Sets the adapter that provides data to this view.

param
adapter The adapter that provides data to this view.

        // Set member variable
        mAdapter = adapter;
        
        if (adapter != null) {
            // Create the connector
            mConnector = new ExpandableListConnector(adapter);
        } else {
            mConnector = null;
        }
        
        // Link the ListView (superclass) to the expandable list data through the connector
        super.setAdapter(mConnector);
    
public voidsetChildDivider(android.graphics.drawable.Drawable childDivider)
Sets the drawable that will be drawn adjacent to every child in the list. This will be drawn using the same height as the normal divider ({@link #setDivider(Drawable)}) or if it does not have an intrinsic height, the height set by {@link #setDividerHeight(int)}.

param
childDivider The drawable to use.

        mChildDivider = childDivider;
    
public voidsetChildIndicator(android.graphics.drawable.Drawable childIndicator)
Sets the indicator to be drawn next to a child.

param
childIndicator The drawable to be used as an indicator. If the child is the last child for a group, the state {@link android.R.attr#state_last} will be set.

        mChildIndicator = childIndicator;
    
public voidsetChildIndicatorBounds(int left, int right)
Sets the drawing bounds for the child indicator. For either, you can specify {@link #CHILD_INDICATOR_INHERIT} to use inherit from the general indicator's bounds.

see
#setIndicatorBounds(int, int)
param
left The left position (relative to the left bounds of this View) to start drawing the indicator.
param
right The right position (relative to the left bounds of this View) to end the drawing of the indicator.

        mChildIndicatorLeft = left;
        mChildIndicatorRight = right;
        resolveChildIndicator();
    
public voidsetChildIndicatorBoundsRelative(int start, int end)
Sets the relative drawing bounds for the child indicator. For either, you can specify {@link #CHILD_INDICATOR_INHERIT} to use inherit from the general indicator's bounds.

see
#setIndicatorBounds(int, int)
param
start The start position (relative to the start bounds of this View) to start drawing the indicator.
param
end The end position (relative to the end bounds of this View) to end the drawing of the indicator.

        mChildIndicatorStart = start;
        mChildIndicatorEnd = end;
        resolveChildIndicator();
    
public voidsetGroupIndicator(android.graphics.drawable.Drawable groupIndicator)
Sets the indicator to be drawn next to a group.

param
groupIndicator The drawable to be used as an indicator. If the group is empty, the state {@link android.R.attr#state_empty} will be set. If the group is expanded, the state {@link android.R.attr#state_expanded} will be set.

        mGroupIndicator = groupIndicator;
        if (mIndicatorRight == 0 && mGroupIndicator != null) {
            mIndicatorRight = mIndicatorLeft + mGroupIndicator.getIntrinsicWidth();
        }
    
public voidsetIndicatorBounds(int left, int right)
Sets the drawing bounds for the indicators (at minimum, the group indicator is affected by this; the child indicator is affected by this if the child indicator bounds are set to inherit).

see
#setChildIndicatorBounds(int, int)
param
left The left position (relative to the left bounds of this View) to start drawing the indicator.
param
right The right position (relative to the left bounds of this View) to end the drawing of the indicator.

        mIndicatorLeft = left;
        mIndicatorRight = right;
        resolveIndicator();
    
public voidsetIndicatorBoundsRelative(int start, int end)
Sets the relative drawing bounds for the indicators (at minimum, the group indicator is affected by this; the child indicator is affected by this if the child indicator bounds are set to inherit).

see
#setChildIndicatorBounds(int, int)
param
start The start position (relative to the start bounds of this View) to start drawing the indicator.
param
end The end position (relative to the end bounds of this View) to end the drawing of the indicator.

        mIndicatorStart = start;
        mIndicatorEnd = end;
        resolveIndicator();
    
public voidsetOnChildClickListener(android.widget.ExpandableListView$OnChildClickListener onChildClickListener)

        mOnChildClickListener = onChildClickListener;
    
public voidsetOnGroupClickListener(android.widget.ExpandableListView$OnGroupClickListener onGroupClickListener)

        mOnGroupClickListener = onGroupClickListener;
    
public voidsetOnGroupCollapseListener(android.widget.ExpandableListView$OnGroupCollapseListener onGroupCollapseListener)

        mOnGroupCollapseListener = onGroupCollapseListener;
    
public voidsetOnGroupExpandListener(android.widget.ExpandableListView$OnGroupExpandListener onGroupExpandListener)

        mOnGroupExpandListener = onGroupExpandListener;
    
public voidsetOnItemClickListener(OnItemClickListener l)
Register a callback to be invoked when an item has been clicked and the caller prefers to receive a ListView-style position instead of a group and/or child position. In most cases, the caller should use {@link #setOnGroupClickListener} and/or {@link #setOnChildClickListener}.

{@inheritDoc}

        super.setOnItemClickListener(l);
    
public booleansetSelectedChild(int groupPosition, int childPosition, boolean shouldExpandGroup)
Sets the selection to the specified child. If the child is in a collapsed group, the group will only be expanded and child subsequently selected if shouldExpandGroup is set to true, otherwise the method will return false.

param
groupPosition The position of the group that contains the child.
param
childPosition The position of the child within the group.
param
shouldExpandGroup Whether the child's group should be expanded if it is collapsed.
return
Whether the selection was successfully set on the child.

        ExpandableListPosition elChildPos = ExpandableListPosition.obtainChildPosition(
                groupPosition, childPosition); 
        PositionMetadata flatChildPos = mConnector.getFlattenedPos(elChildPos);
        
        if (flatChildPos == null) {
            // The child's group isn't expanded
            
            // Shouldn't expand the group, so return false for we didn't set the selection
            if (!shouldExpandGroup) return false; 

            expandGroup(groupPosition);
            
            flatChildPos = mConnector.getFlattenedPos(elChildPos);
            
            // Sanity check
            if (flatChildPos == null) {
                throw new IllegalStateException("Could not find child");
            }
        }
        
        int absoluteFlatPosition = getAbsoluteFlatPosition(flatChildPos.position.flatListPos);
        super.setSelection(absoluteFlatPosition);
        
        elChildPos.recycle();
        flatChildPos.recycle();
        
        return true;
    
public voidsetSelectedGroup(int groupPosition)
Sets the selection to the specified group.

param
groupPosition The position of the group that should be selected.

        ExpandableListPosition elGroupPos = ExpandableListPosition
                .obtainGroupPosition(groupPosition);
        PositionMetadata pm = mConnector.getFlattenedPos(elGroupPos);
        elGroupPos.recycle();
        final int absoluteFlatPosition = getAbsoluteFlatPosition(pm.position.flatListPos);
        super.setSelection(absoluteFlatPosition);
        pm.recycle();