Home » Android » android – ListView item won't stay "selected"

android – ListView item won't stay "selected"

Posted by: admin June 15, 2020 Leave a comment

Questions:

I want to change the background of a listview item when the user clicks it. Kind of like the Honeycomb settings page (Although I’m not dealing with just settings so I’m not using PreferenceActivity) I have this functionality working through a resource state selector state selector except for the cases when clicking on the listview menu changes the linear layout to the right of the listview (sort of a split screen view). I’m guessing the listview looses focus so state_pressed is no longer true.

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

Any tips to keep that listview item colored until another listview item is selected? Thanks!

EDIT:

I was able to get the background changed in a setOnItemClickListener with

view.setBackgroundResource(R.color.red); 

I only need one selected at a time so when the other list items are clicked, I tried lv.invalidate() and lv.getChildAt(0).invalidate() but neither worked and the second causes null pointer exception. Any ideas for putting the color back?

How to&Answers:

When you release your finger from the cell it no longer registers as pressed. What you are going to want to do is actually change the background of the individual row when a users selects is. This means implementing an onItemClick or onItemTouch and flagging the adapter to redraw the row with the new background. If you are already using a custom list adapter you can just implement a check against a boolean in your getView() method. You will also need to keep track which rows are ‘selected’ and which are not.

pseudocode:

   public View getView(int pos, View convertView, ViewGroup parent) {
      if(isChecked[pos]) //set background to checked color
   }

Answer:

Hope this help,

1.- Create a shape file for focused item: \drawable\list_selector_focused.xml

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

    <gradient android:angle="90" android:startColor="#f5c98c" android:endColor="#f7ddb8"/> 

</shape>

2.- Create a shape file for pressed item: \drawable\list_selector_pressed.xml

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

    <gradient android:angle="90" android:startColor="#fb9d23" android:endColor="#ffc579" />

</shape>

3.- Create list selector file: \drawable\list_selector.xml

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

    <item android:state_pressed="true" android:drawable="@drawable/list_selector_pressed" />
    <item android:state_focused="true" android:drawable="@drawable/list_selector_focused" />
    <item android:drawable="@drawable/list_selector_focused" />

</selector>

4.- Add this attribute to your ListView in the layout file:

 android:choiceMode="singleChoice"
 android:listSelector="@drawable/list_selector"

You can use colors instead of gradient shapes,

Answer:

This is implementation of sgarman idea:

    package com.mypackage;

import java.util.Vector;

import com.myapp.R;
import com.myapp.data.Address;

import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class AddressesListAdapter extends BaseAdapter{
    protected Context context;
    protected LayoutInflater mInflater;
    protected int itemResourceId;
    protected Vector<Address> contentItems = new Vector<Address>();
    protected Vector<Boolean> selectedStates;
    private static final String TAG = "myapp";

    public AddressesListAdapter(Context context, Vector<Address> contentItems) {
        this.context = context;
        this.contentItems = contentItems;
        mInflater = LayoutInflater.from(context);
        itemResourceId = R.layout.address_list_item;
        selectedStates = new Vector<Boolean>();
        //initial fill
        clearSelectedState();
    }

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

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

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

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) {
        final ViewHolder holder;
        if (convertView == null) {
            convertView = mInflater.inflate(itemResourceId, null);

            holder = new ViewHolder();
            holder.addressName = (TextView) convertView.findViewById(R.id.addressName);
            convertView.setTag(holder);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        Address address = (Address) contentItems.get(position);
        holder.addressName.setText(address.getAddressName());
        holder.addressName.setOnClickListener(new SetFocusListener(position));

        //restore saved position from saving vector
        if (selectedStates.get(position)) holder.addressName.setBackgroundColor(Color.BLUE);
        else holder.addressName.setBackgroundColor(Color.TRANSPARENT);

        return convertView;
    }

    private void clearSelectedState () {
        selectedStates.clear();  
        for (int i = 0 ; i <= contentItems.size(); i++) {
            selectedStates.add(new Boolean(false));
        } 
    }

    private class SetFocusListener implements View.OnClickListener {
        private int position;

        public SetFocusListener(int position) {
            this.position = position;
        }

        @Override
        public void onClick(View v) {
            //clear selected state vector
            clearSelectedState();
            //set selected position
            selectedStates.set(position, new Boolean(true));
            //refresh adapter to redraw focus
            notifyDataSetChanged();
        }
    }

    static class ViewHolder {
          TextView addressName;
    }
}

Th only concern that it may be to costly to setup new listener for every getView() iteration

Answer:

The android state checked is best used to resolve this issue.

Someone mentioned using android:background=”?android:attr/activatedBackgroundIndicator”.

This just points to one of the activated_background_* resources in frameworks/base/core/res/res/drawable of the android source code. For example activated_background_holo_dark.xml:

<selector xmlns:android="http://schemas.android.com/apk/res/android"> 
  <item android:state_activated="true" android:drawable="@android:drawable/list_activated_holo" />
  <item android:drawable="@color/transparent" /> 
</selector>

So essentially you want to use state_activated to represent when the user presses the button as well when it is in a checked (i.e. in a persistent selected state) state. Note that activated was only introduced after Honeycomb, if you are targeting older devices you’ll need to rely on state_checked (more details here).

Now if you want to set an item as checked, you need to call listView.setItemChecked(position, true). You’ll likely want to set the android:choiceMode property on your ListView to the appropriate value (e.g. if you want only one thing selected at a time use singleChoice). You don’t need to invalidate, the call to setItemChecked will trigger a relayout which will update the view.

Also be careful if you allow reordering items in your ListView as the current checked item(s) will need to be updated. If you use stable Ids, this will be handled automatically.

To see an example of this in action, check out the NavigationDrawer sample code found in the training series: http://developer.android.com/training/implementing-navigation/nav-drawer.html.

Answer:

By default, ‘Selected’ isn’t the same as ‘Clicked’ when you’re using a touch interface – something that cause me some real headaches when I started Android development.

To support both users that navigate by touch and users that use scrollwheels/trackballs, you might want to use setSelection, and do your manipulation in an AdapterView.OnItemSelectedListener implementation (set with setOnItemSelectedListener).

Another gotcha is that setSelection won’t highlight an item if the last event was a touch event.

I’d recommend that you create a custom View for your list items, and handle highlighting in there.

Hope this helps,

Phil Lello

Answer:

Ok, so I tried the solution above from where it says “This is implementation of sgarman idea:” This only works if SetFocusListener is an OnTouchListner. Ohterwise the onClick method consumes the click. I had to pair this solution with an OnItemClick listener on my list item to get the list to actually show the highlighted item.

Answer:

I used android:state_activated="true" instead of state_selected. It works like a charm!

Answer:

If you keep your listView through the whole activity you can do a mListView.isItemChecked(position) in the getView() method. And them set the background color depending on the result.

Answer:

Have you tried android:state_selected="true" ?

Answer:

try

    android:background="?android:attr/activatedBackgroundIndicator"

😉