Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I retain selected item highlighting on gridview when numColumns changes?

I have an ActionBarActivity with a GridView.

The GridView has 2 columns in portrait and 3 columns in landscape.

When I select items in portrait (starting my ActionMode) and then rotate the device, the selected item highlighting shifts one item to the left. For example, if I select the second item and rotate, the first item will be highlighted. If I select the first item and rotate, no items are highlighted.

The actual selection in the code is correct, just the highlighting is wrong.

I notice it does not do this if I keep the numColumns the same for portrait and landscape.

I believe this issue started occurring after I changed my activity to an ActionBarActivity so it could be a bug..

Anyone know why or how to fix it?

like image 913
joepetrakovich Avatar asked Jan 04 '15 19:01

joepetrakovich


2 Answers

I had a similar scenario and ended up solving the issue be creating a custom grid item with a boolean field to keep track of whether the item is selected or not and then highlighting the item appropriately through the custom adapter. Below is a rough outline of what I did:

(1) I created a custom grid item with a boolean field, which we will call selectedStatus for simplicity's sake. I also added the corresponding methods to my grid item class to get the selected status:

public boolean getSelectedStatus ()
{
    return selectedStatus;
}

public void setSelectedStatus (boolean paramSelectedStatus)
{
    this.selectedStatus = paramSelectedStatus;
}

(2) I then created a custom Adapter that extends BaseAdapter to handle the custom grid object I created. In this Adapter I check the if the selected status of the grid object is true or false and highlight the item accordingly, shown below:

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

    // rest of getView() code...

    if (!yourGridObject.getSelectedStatus())
    {
        convertView.setBackgroundColor(Color.TRANSPARENT);
    }
    else
    {
        convertView.setBackgroundColor(Color.LTGRAY);
    }

    // rest of getView() code...

    return convertView;
}

(3) Lastly, you add the onItemClickListener to set the selected status and the background color of the grid items when they are selected (clicked):

yourGridView.setOnItemClickListener(new OnItemClickListener()
{
    @Override
    public void onItemClick(AdapterView<?> parent, View view, 
            int position, long id)
    {
        YourGridObject yourGridObject = (YourGridObject) parent.getItemAtPosition(position);
        if (!yourGridObject.getSelected())
        {
            view.setBackgroundColor(Color.LTGRAY);
            yourGridObject.setSelected(true);
        }
        else
        {
            view.setBackgroundColor(Color.TRANSPARENT);
            yourGridObject.setSelected(false);
        }
    }
});

Implementing selection this way ensures that the highlighting (selection) of the grid items will not change when the number of columns and rows swap since the selection status is contained within the grid objects themselves.

like image 81
Willis Avatar answered Oct 24 '22 06:10

Willis


You don't need to manually handle selection of items as suggested by Willis. Android fully supports what you are asking. I will assume you are using an ArrayAdapter however this answer would apply to all adapters. Note some adapters (like CursorAdapter) won't suffer from your posted problem and don't require the following solution because it's already doing it internally.

The problem is solved in two parts. One, the adapter must enable stable Ids. Two, your adapter must actually return stable ids. You will need to extend the ArrayAdapter or which ever adapter you are using. Then ensure you have defined the following methods as shown below.

private class MyAdapter extends ArrayAdapter<YourObjects> {
    @Override
    public boolean hasStableIds() {
        return true;
    }

    @Override
    public long getItemId(int position) {
        //Return a unique and stable id for the given position
        //While unique, Returning the position number does not count as stable.
        //For example:
        return getItem(position).methodThatReturnsUniqueValue();
    }
}

Most adapters do not enable hasStableIds. It's primarily only used when enabling a choiceMode. Which I assume you are doing here. By returning true, you are essentially telling Android to keep track of activated (highlighted) items based on their ID value instead of their position number.

Even with stable Ids enabled, you have to actually return an ID that is unique and stable across positional changes. Since most adapters do NOT enable stable IDs, they usually only return the position number as the stable id. Technically, if an item's position never changes over time then the position number "could" be used as the stable id. However, the safest way to return a stable/unique ID is to have one assigned to the class object being stored in the adapter and pull from that.

like image 24
Jay Soyer Avatar answered Oct 24 '22 07:10

Jay Soyer