Search code examples
javaandroidexpandablelistview

ExpandableListView open collapse problem


I am using a custom expandable list adapter. I highlight a child when the user clicks on it. This works fine until the user opens/collapses a group. Say the user touches Group 2 Item 1. This highlights Group 2 Item 1. Then the user opens Group 1. Now Group 3 Item 2 is highlighted. I have done some testing choosing different items and I can not find a pattern where the highlighted row will jump to. Sometimes its up in the list, sometimes its down. I'm having trouble figuring out the logic to put in my activity's onGroupExpandListener and onGroupCollapseListener to rehighlight the correct view. Any ideas?

EDIT: Current code inside my onChildClickListener

if (groupPosition == 0){ 
      switch(childPosition) {
      case 0: 
        previouslySelectedView.setBackgroundResource(R.color.transparent);
        currentlySelectedView.setBackgroundResource(R.color.blue); 
        break;

Same code for all the groups/children


Solution

  • Item slection in ExpandableListView is made through flat list (absolute positiond). Thus if the newlly opened group is before the currenet selection and has fewer children then the selection will move up and vice versa. I suggest to set choice mode to none and implement onclick/expand logic to handle focus on your own - implement tags for views, for example, and set the currently highlighted item via the tag.

    Here are few suggestions:

    1. In the ExpandableListView.OnChildClickListener first you perform ExpandableListView.findViewWithTag(theTag) to check for views with such tag and unmark them (also setTag(null)) and restore the background. Then for the item clicked setTag(theTag) and change the background to selected. Of course you can have some other logic and have multiple items marked. Note that once the view is destroyed you will lose the selection (for example during expands).
    2. Have some custom map or something that will hold a unique ID of the view and the (un)marked state. That's the best solution that will allow to maintain persistent selection across scrolls and expands.
    3. In the backing adapter introduce a "marked" state. Thus the marking will be persistent even between application start/stop. Not a good approach, though, because selection is more of a UI behaviour.

    I am currently doing an ExpandableListView selection with Choice Mode Multiple of the list. But since, as I said, the selection is per position I had to sacrifice in terms of functionality - namely, I clear the selection whenever a manipulation is made. The previous implementation was with custom Map holding the selected IDs and - to honest - it was the better approach.

    This is how I get selected IDs (remember I use choice mode multiple):

    final SparseBooleanArray checkedPositions = expList.getCheckedItemPositions();
    final ExpandableListAdapter adapter = expList.getExpandableListAdapter();
    List<Long> checkedIds = new ArrayList<Long>();
    if (packedPositionType == ExpandableListView.PACKED_POSITION_TYPE_GROUP) {
        for (int i = checkedPositions.size() - 1; i >= 0; i--) {
            if (checkedPositions.valueAt(i)) {
                checkedIds.add(adapter.getGroupId(checkedPositions.keyAt(i)));
            }
        }
    }
    

    In your case, though, you will want to check for CHILD packed positions. Also note that my adapter has stable (unique) ids. If you do not have stable IDs then you can rely on ExpandableListView's getPackedPositionForChild() method and store the packed position as marked.