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?
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
}
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,
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
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With