androidviewexpandablelistviewexpandablelistadapter

Expandable List: colouring of child item when clicked bugged (it colours multiple items)


In my expandable list I'm trying to implement the following feature, when an user clicks on a child item it should colour that item so one can see that it was selected and when it clicks on it again it returns to the default colour. This is working fine, the problem is that it colours also other child items ahead on the list and it has a pattern, it colours a child item in intervals of 10 items. I think this is due to the view being recycled but I have no idea how to solve this. Any help would be appreciated :)

I'll post the most relevant code:

**public class ExpandableListAdapter extends BaseExpandableListAdapter** {

private Context context;
    private List<String> headers;
    private Map<String, List<String>> items;

public ExpandableListAdapter(Context context, List<String> headers, Map<String, List<String>> items) {
        this.context = context;
        this.headers = headers;
        this.items = items;
    }

 @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        if (convertView == null) {
            convertView = LayoutInflater.from(context).inflate(R.layout.list_item, parent, false);
        }

        TextView childItemTextView = convertView.findViewById(R.id.childItemTextView);

        String item = items.get(headers.get(groupPosition)).get(childPosition);
        childItemTextView.setText(item);


        return convertView;
    }

**public class ComplexSearchActivity extends AppCompatActivity implements ComplexSearchContract.View** {
     expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
            @Override
            public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {

                String selectedItem = items.get(headers.get(groupPosition)).get(childPosition);

                Log.d("Debug", "Clicked View Details: " + v.toString());
                Log.d("Debug", "Selected Item: " + selectedItem);

                presenter.onChildItemClick(selectedItem, v);

                return true; 
            }
        });

     @Override
    **public void updateItemSelection(View clickedView, boolean isSelected)** {
        TextView textView = clickedView.findViewById(R.id.childItemTextView);
        textView.setSelected(isSelected);

    }

}

**public class ComplexSearchPresenter implements ComplexSearchContract.Presenter** {
    public void onChildItemClick(String clickedItem, View clickedView) {
        if (selectedItems.contains(clickedItem)) {
            selectedItems.remove(clickedItem);
            Log.d("Debug", "Removed from selectedItems: " + clickedItem);
        } else {
            selectedItems.add(clickedItem);
            Log.d("Debug", "Added to selectedItems: " + clickedItem);
        }

        view.updateItemSelection(clickedView, selectedItems.contains(clickedItem));
    }

}

The TextView childItemTextView contains a selector that dictates the colour of the item when boolean isSelected is true or false: textView.setSelected(isSelected); The xml code is the following:

Layout resource- list_item.xml

<?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:orientation="vertical">
    <TextView
            android:id="@+id/childItemTextView"
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:paddingLeft="?android:attr/expandableListPreferredChildPaddingLeft"
            android:paddingTop="10dp"
            android:paddingBottom="10dp"
            android:background="@drawable/item_selector">

    </TextView>
</LinearLayout>

Drawable resource- item_selector.xml

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
            android:state_selected="true"
            android:drawable="@drawable/selected_state"
    />
    <item
            android:drawable="@drawable/default_state"
    />
</selector>

Solution

  • Try pass selectedItems into the adapter, so change the constructor of the adapter:

    private List<String> selectedItems; // Add this.
    
    public ExpandableListAdapter(Context context,List<String> headers,Map<String, List<String>>items, List<String>selectedItems){
        this.context=context;
        this.headers=headers;
        this.items=items;
        this.selectedItems=selectedItems; // Add this.
    }
    

    Then change getChildView():

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent){
        if(convertView==null){
            convertView= LayoutInflater.from(context).inflate(R.layout.list_item,parent,false);
        }
    
        TextView childItemTextView=convertView.findViewById(R.id.childItemTextView);
    
        String item=items.get(headers.get(groupPosition)).get(childPosition);
        childItemTextView.setText(item);
    
        childItemTextView.setSelected(selectedItems.contains(item)); // Add this.
    
        return convertView;
    }
    

    In the activity, change the creation of the adapter according to the constuctor of the adapter.

    Additonal comments: now only the string of child item is saved into selectItems list. This works only for all child items are different strings. If child items can have same string, then multiple childs are selected. For such case, combined both group and child item strings into one and saved into selectItems list, getChildView() need to change accordingly.