Android - Keep ListView's item highlighted once one has been clicked
Asked Answered
T

11

37

So I have an activity with 2 ListView widgets, when you select a value in the first one, the second is populated with values related to the selection in the first ListView. This mechanic works without a problem, but now I want the user choices to stay highlighted. I've read a good ammount of question related to this topic and it seems there is a myriad of ways one can accomplish this but after trying about 4-5 of em' I still can't get it to work.

I've got it working on the second ListView using the android:listSelector="#CCCCCC" XML Attribute, but this seems to be wiped clean once a OnItemClickListener is introduced into the mix (like the one I use on my first ListView).

So far here's what I've got:

Custom OnItemClickListener I found browsing various answer regarding this topic (slightly modified it in order for it to load my info the second ListView):

private class ItemHighlighterListener implements OnItemClickListener {

    private View oldSelection = null;

    public void clearSelection() {
        if(oldSelection != null) {
            oldSelection.setBackgroundColor(android.R.color.transparent);
        }
    }

    public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
        clearSelection();
        oldSelection = view;
        view.setBackgroundDrawable(view.getContext().getResources().getDrawable(R.drawable.list_selector));
        loadClubs(mXMLPortalOptions.getRegion(pos).getId());
        mClubList.setAdapter(new ArrayAdapter<String>(getApplicationContext(), R.layout.list_item_white, mClubs));
    }
}

Here's my list_selector.xml file :

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

    <item android:state_selected="true"><shape>
            <solid android:color="#CCCCCC" />
        </shape></item>

    <item android:state_selected="false"><shape>
            <solid android:color="#FFFFFF" />
        </shape></item>

</selector>

The method (OnItemClick) is called and executed, but the background of my ListItem stays the same color :/

I can't believe that this simple task has proven so complicated.

If I have omitted code that could be useful or if my question is lacking details, feel free to point that out and I'll do my best to explain myself.

Tearing answered 14/2, 2012 at 17:1 Comment(4)
I think this is related to which version of the SDK you're using.Dripps
API level 7 is what I'm using.Tearing
The post by i.masm has a link to what I was thinking. android-developers.blogspot.com/2008/12/touch-mode.htmlDripps
Maybe this can help: #15344820Oniskey
P
63

Put a position variable for selected item. Change the position in onItemClicked() method. Check the selected position in List Adapter inside getView() and set the background for the selected item.

public class TestAdapter extends BaseAdapter
{
    private Context context;
    private ArrayList<TestList> testList;
    private int selectedIndex;
    private int selectedColor = Color.parseColor("#1b1b1b");

    public TestAdapter(Context ctx, ArrayList<TestList> testList)
    {
        this.context = ctx;
        this.testList = testList;
        selectedIndex = -1;
    }

    public void setSelectedIndex(int ind)
    {
        selectedIndex = ind;
        notifyDataSetChanged();
    }

    @Override
    public int getCount()
    {
        return testList.size();
    }

    @Override
    public Object getItem(int position)
    {
        return testList.get(position);
    }

    @Override
    public long getItemId(int position)
    {
        return position;
    }

    private class ViewHolder
    {
        TextView tv;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent)
    {
        View vi = convertView;
        ViewHolder holder;
        if(convertView == null)
        {
            vi = LayoutInflater.from(context).inflate(R.layout.test_list_item, null);
            holder = new ViewHolder();

            holder.tv = (TextView) vi;

            vi.setTag(holder);
        }
        else
        {
            holder = (ViewHolder) vi.getTag();
        }

        if(selectedIndex!= -1 && position == selectedIndex)
        {
            holder.tv.setBackgroundColor(Color.BLACK);
        }
        else
        {
            holder.tv.setBackgroundColor(selectedColor);
        }
        holder.tv.setText("" + (position + 1) + " " + testList.get(position).getTestText());

        return vi;
    }

}

Now set the selectedIndex variable when a list item clicked.

public class TestActivity extends Activity implements OnItemClickListener
{
    // Implemented onItemClickListener

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id)
    {
        adapter.setSelectedIndex(position);
    }
}
Plate answered 14/2, 2012 at 17:11 Comment(8)
can you please explain in more detail.. i am getting something but not 100%. so please explain in detail steps..pleaseRodolphe
Excellent explanation - Several new concepts all explained in one shot, thanks!Etruria
Great and simple, that's how I like my code! I didn't get it to work until I made some things static and moved some things around (see my answer below). +1Bolivia
Hello I am trying to use this approach with an expandable listview but can't get it work , anytime i open a group I find an item selected in itCantilena
@Plate actually this worked, but it make a slow UI response cause every time click on item, it call notifyDataSetChanged() Could you tell me if there is anyway to fix it?Subset
@R4j It should not make your UI slow. Maybe there are other reason. Each time notifyDataSetChanged() called only the visible number of rows get updated.Plate
Can you explain me this below code., if(selectedIndex!= -1 && position == selectedIndex) { holder.tv.setBackgroundColor(Color.BLACK); } else { holder.tv.setBackgroundColor(selectedColor); }Amorete
@dya If selecectedIndex is set and the current rendered position is the selectedIndex then set the background color to black. otherwise set it to normal color. It's just a skeleton code. modify it yourself.Plate
B
5

To expand on Shaiful's great solution, you might not get his to work in your situation.

If you are using have your code all in public void onListItemClick(ListView l, View v, int index, long id), if you're using fragments and have to declare an interface instead of implementing OnListItemClickListener, or whatever is causing your IDE to generate errors, you might have to access variables and methods statically.

public static int selectedPosition = 0;
ArrayAdapter<Your_obj> adapter = null;

@Override
public void onListItemClick(ListView l, View v, int index, long id) {
    super.onListItemClick(l, v, index, id);

        selectedPosition = index;
        Your_adapter.setSelectedIndex(selectedPosition);
        adapter.notifyDataSetChanged();
}

And in Your_adapter:

private static int selectedIndex;

//public Your_adapter...

public static void setSelectedIndex(int ind) {
    selectedIndex = ind;
}

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    WellHolder holder = null;

    if (null == convertView) {

                //set up your "holder"
    }

    if (position == selectedIndex) {
        convertView.setBackgroundColor(convertView.getResources().getColor(R.color.cyan));
    }
    else {
        convertView.setBackgroundColor(convertView.getResources().getColor(R.color.silver));
    }

    return convertView;
}

Some other differences are that you don't have to initialize any variables as "0" or "-1" and notifyDataSetChanged() is called in your activity.

Once again, thanks for your solution @Shaiful. It certainly helped save me time trying to get what is default in iOS to work for Android, all while avoiding selector/item/focused/pressed/etc.

Bolivia answered 10/7, 2013 at 21:30 Comment(1)
It's because you are not using state list instead this is an inneficient and not so suitable solution, it's better to lean on the platform on this kind of stufFiacre
I
4

I faced similar problem. That's my solution:

First add custom list selector to your list view:

<ListView
    android:id="@+id/list"
    android:layout_width="match_parent"
    android:layout_height="fill_parent"
    android:listSelector="@drawable/listselector" />

Inside listselector.xml:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_accelerated="false"
        android:drawable="@drawable/bg" />
</selector>

And finally a drawable bg.xml with color of your highlight:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
    <solid android:color="#33b5e6"/>
</shape>
Insulate answered 15/3, 2015 at 14:29 Comment(0)
G
3

I think the best and easiest solution is this. You don't need to set any android:listSelector on the ListView itself or do any changes to the adapter. You don't even need to call any setSelection(position) in the OnItemClickListener as it is handled automatically.

  1. Set to your ListView:

    android:choiceMode="singleChoice"
    
  2. Set background of the list item itself:

    android:background="?android:attr/activatedBackgroundIndicator"
    
  3. That's it.

This way you'll get default system behavior. That's how it is done in the default android.R.layout.simple_list_item_activated_1 layout.

Goldfilled answered 15/1, 2017 at 9:56 Comment(0)
C
2

I was looking for it two weeks ago and the result is that is not possible with a drawable selector. For more info read this post in Android Developers Blog: Touch Mode

In resume: Only when your finger are on the screen, item is selected.

Other possibility is save which item is selected in a var and paint different using your custom adapter like Shaiful says.

Caelum answered 14/2, 2012 at 19:32 Comment(0)
U
2

lv.setSelector(R.drawable.highlighter);

put a highlighter.png image in drawable folder
simplest way to highlight selected item in list view.

Unbelievable answered 15/4, 2013 at 23:12 Comment(0)
M
2
//create a list_itemselectorin drawable folder
//you will get the list item selected background color change once you select //the item

    <selector xmlns:android="http://schemas.android.com/apk/res/android">

        <!-- Focused State -->
        <item android:state_focused="true"><shape>
                <solid android:color="#66FFFFFF" />
            </shape></item>
        <!-- Pressed State -->

        <item android:state_pressed="true"><shape>
                <solid android:color="@color/Black" />
            </shape></item>

        <!-- Default State -->
        <item><shape>
                <solid android:color="@color/Black" />
            </shape></item>

    </selector>


    //create a list in layout folder
      <ListView
            android:id="@+id/mySlidingList"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:choiceMode="singleChoice"
            android:divider="@color/GrayHot"
            android:dividerHeight="1dip"
            android:listSelector="@drawable/list_itemselector"
            android:scrollbars="none" />

// And see the output.

Mor answered 24/5, 2015 at 3:11 Comment(0)
N
0

If you can use a drawable for displaying listItem Highlighted then you should use following code:-

listView.setSelector(R.drawable.bg_image);

It works.

Neap answered 25/9, 2014 at 5:15 Comment(0)
D
0

There is simple fully-XML solution, which worked for me. Firstly, define XML-drawable with selector code in which "normal" state will correspond to "selected unpressed" visual state of a list item, and state_pressed=true to "pressed" visual state. Example of file "custom_item_selector.xml", resembling Holo blue selection:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true">
        <shape android:shape="rectangle">
            <solid
                android:color="#643292ff">
            </solid>
            <stroke
                android:width="1dp"
                android:color="#c83292ff">
            </stroke>
        </shape>
    </item>
    <item>
        <shape android:shape="rectangle">
            <solid
                android:color="#323292ff">
            </solid>
            <stroke
                android:width="1dp"
                android:color="#783292ff">
            </stroke>
        </shape>
    </item>
</selector>

(may also set focused state there). Secondly, apply this xml-drawable as ListView's listSelector and set it's desired choiceMode:

<ListView
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:id="@+id/listView"
            android:choiceMode="singleChoice"
            android:listSelector="@drawable/custom_item_selector"/>

That's all. It allows to define different visual states for "simply selected" and "pressed selected" items, for example making items brighter on press.

Delladelle answered 1/2, 2016 at 16:59 Comment(0)
C
0

To keep the list items(multiple selection) highlighted, when clicked(activated), please follow the steps.

1. Set background to list item layout as drawable.

    <?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="40dp"
        android:background="@drawable/list_item_selector">

        <ImageView
            android:id="@+id/icon"
            android:layout_width="22px"
            android:layout_height="22px"
            android:layout_marginLeft="4px"
            android:layout_marginRight="10px"
            android:layout_marginTop="4px"
            android:src="@mipmap/ic_launcher" >
        </ImageView>

        <TextView
            android:id="@+id/label"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@+id/label"
            android:textSize="20px" >
        </TextView>
    </LinearLayout>

2. drawable selector

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">

<item android:state_pressed="true"     android:drawable="@android:color/holo_red_light" />

<item android:state_activated="true" android:drawable="@android:color/holo_orange_dark" />

</selector>

3. Listview set multiple choice mode

getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

When Pressed:enter image description here

Below image shows, when user selected multiple list items.

When Activated: enter image description here

Chimera answered 20/5, 2016 at 20:34 Comment(0)
N
0

To summarize this post and maybe help someone else in future I suggest the answer :)

First, we need to create res/drawable/list_item_background.xml file with the following contents:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item
        android:state_activated="true"
        android:drawable="@color/list_item_activated" />
    <item
        android:drawable="@color/list_item_default" />
</selector>

Specify your drawable resources, of course. And you can also add other <item> elemens with different states like state_pressed, state_focused etc.

Then, we should set the background parameter to our custom list item ViewGroup element (f.i. res/layout/list_item_layout.xml) like this:

android:background="@drawable/list_item_background"

The next step is modifying our custom Adapter class. Here is the following code fragment:

public class CustomAdapter extends BaseAdapter {
    private List<Item> items;
    private LayoutInflater itemInflater;        
    private int selectedIndex; // add this

    public CustomAdapter(Context c, List<Item> items) {
        this.items = items;
        this.itemInflater = LayoutInflater.from(c);
        selectedIndex = -1; // add this
    }

    /* add this */
    public void setSelectedIndex(int index) {
        selectedIndex = index;
        notifyDataSetChanged();
    }

    /* other adapter's stuff */

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        if(convertView == null) {
            convertView = itemInflater.inflate(R.layout.list_item_layout, parent, false);
        }

        // add this
        convertView.setActivated(selectedIndex != -1 && position == selectedIndex);

        /* do some stuff */

        return convertView;
    }
}

Finally, we should call the setSelectedIndex(position) adapter's method in onItemClick(...) method of AdapterView.OnItemClickListener.

public class YourActivity extends Activity
        implements AdapterView.OnItemClickListener {

    private CustomAdapter mCustomAdapter;

    /* activity implementation */

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        mCustomAdapter.setSelectedIndex(position);
    }
}

Now, we can be satisfied with proper list items highlighting :)

P.S. If we want to enable multiple choice mode on our list we'll just place the following string to our activity class where listView instance is kept:

listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);

Thus, we'll get proper multiple items highlighting.

-- Hope this helps anyone :)

Nikethamide answered 13/8, 2016 at 19:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.