Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ListView item won't stay "selected"

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?

like image 891
yellavon Avatar asked Apr 15 '11 20:04

yellavon


4 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
   }
like image 150
sgarman Avatar answered Oct 22 '22 18:10

sgarman


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,

like image 23
user2180389 Avatar answered Oct 22 '22 19:10

user2180389


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

like image 33
Oleg Koshkin Avatar answered Oct 22 '22 20:10

Oleg Koshkin


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.

like image 3
Pierre-Antoine LaFayette Avatar answered Oct 22 '22 18:10

Pierre-Antoine LaFayette