Search code examples
androidlistviewandroid-listfragment

Maintaining background state of list view items after an item is deleted


I have a ListFragment that contains a TextView and two Buttons in every item. Clicking on one Button changes the background color of the list item and clicking on the other deletes the item from the list. Now, the background state of an item will be maintained as long as no scrolling occurs. Once the list becomes long enough to scroll, the background states / views of various items are recycled causing list items that were not clicked to have their background colors changed.

I am able to retain the state of the background by storing list item positions accessible in the getView(int position, View convertView, ViewGroup parent) method of the ListFragment's CustomAdapter (a SimpleAdapter in my case) in an ArrayList and then checking if the ArrayList contains a selected position just before the inflated view is returned. If the position exists in the ArrayList then the background color of that item is set to green, else it is set to transparent. Here is my code:

@Override
public View getView(int position, View convertView, ViewGroup parent){

    View view = convertView;
    final int pos = position;

    view = super.getView(position,convertView,parent);  


    Button buttonChangeBackground = (Button) view.findViewById(R.id.button_1);
    buttonChangeBackground.setOnClickListener(new View.OnClickListener(){

        @Override
        public void onClick(View v) {
            View view =(View) v.getParent();
            view.setBackgroundColor(Color.GREEN);

            // storing list item positions in ArrayList
            coloredItems.add(pos);

        }
    });

    Button buttonDelete = (Button) view.findViewById(R.id.button_2);
    buttonDelete.setOnClickListener(new View.OnClickListener(){

        @Override
        public void onClick(View v) {

            datalist.remove(pos);
            notifyDataSetChanged();

        }
    });

   /*
    *  Using the stored positions to retain background state.
    */

    if(coloredItems.contains(pos))
        view.setBackgroundColor(Color.GREEN);
    else
        view.setBackgroundColor(Color.TRANSPARENT);

    return view;
}

This works as long as no item is deleted from the list. I understand that once a list item is deleted all items after and including the deleted item need to have their positions decremented by 1 in the ArrayList so that states are maintained in their modified positions. I have tried many methods however I always throw an IndexOutOfBoundsException.

What code should I add to ensure that all list items retain their background state even after a list item is deleted ? This appears to be a simple ArrayList manipulation problem but I am unable to arrive at a solution.


Solution

  • Answering my question. This answer is based on the comment left by @pskink.

    While creating the dataset for the adapter add an extra key that indicates the default background state of a list item and an appropriate value. In my case I used

    map.add("BACKGROUND","BACKGROUND_PLAIN");
    

    Now, in the getView method change the value of the key within the OnClickListener of the Button that changes the View's background. In my case:

    Button buttonChangeBackground = (Button) view.findViewById(R.id.button_1);
        buttonChangeBackground.setOnClickListener(new View.OnClickListener(){
    
            @Override
            public void onClick(View v) {
                        // obtaining map associated with current data item's position.
                final HashMap<String,String> map = datalist.get(pos);
                        // changing value associated with key.
                map.put("BACKGROUND", "GREEN"); 
                notifyDataSetChanged();
    
            }
        });
    

    Now, just before the getView method returns check if the data item associated with the current view contains a modified value or the default value set for the background. If the value is modified, set background of view to green, else set the background to its default value, which is Color.Transparent in my case.

    hashMap = datalist.get(position);
        if(hashMap.containsValue("GREEN")){
            view.setBackgroundColor(Color.GREEN);
    
        }
    
        else{
    
            view.setBackgroundColor(Color.TRANSPARENT); 
        }
    

    The full code:

    @Override
    public View getView(int position, View convertView, ViewGroup parent){
    
        View view = convertView;
        final int pos = position;
        HashMap<String,String> hashMap = new HashMap<String,String>(); 
    
        view = super.getView(position,convertView,parent);  
    
        Button buttonChangeBackground = (Button) view.findViewById(R.id.button_1);
        buttonChangeBackground.setOnClickListener(new View.OnClickListener(){
    
            @Override
            public void onClick(View v) {
    
                            // obtaining map associated with current data item's position.
                        final HashMap<String,String> map = datalist.get(pos);
                            // changing value associated with key.
                        map.put("BACKGROUND", "GREEN");
    
                        notifyDataSetChanged();
    
            }
        });
    
        Button buttonDelete = (Button) view.findViewById(R.id.button_2);
        buttonDelete.setOnClickListener(new View.OnClickListener(){
    
            @Override
            public void onClick(View v) {
    
                datalist.remove(pos);
                notifyDataSetChanged();
    
            }
        });
    
    
        hashMap = datalist.get(position);
        if(hashMap.containsValue("GREEN")){
            view.setBackgroundColor(Color.GREEN);
    
        }
    
        else{
    
            view.setBackgroundColor(Color.TRANSPARENT); 
        }
    
    
    
        return view;
    }