I have the following requirements:
For whatever reasons it seems not to be as straight forward as I thought. The only solution that goes at least a little bit in the right direction is this one: https://stackoverflow.com/a/16978159/658718
The caveat is, that this does not change the on select color, but changes the background color permanently, plus it already changes the background color for list view items if you scroll down a bit.
How can I approach this?
I'd say go with state-aware drawables . Create a state-aware drawable XML file for each of the colors you want the background of your single ListView to be. Here is an example of state- aware Drawables called background_black.xml and background_green.xml. They makes your default background color white and while pressed/selected temporarily changes it to black or green. Both of these files go in your Drawable folder.
background_black.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:drawable="@android:color/white" />
<item android:state_pressed="true" android:color="@android:color/black" />
<item android:state_selected="true" android:color="@android:color/black" />
</selector>
background_green.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_window_focused="false" android:drawable="@android:color/white" />
<item android:state_pressed="true" android:color="@android:color/green" />
<item android:state_selected="true" android:color="@android:color/green" />
</selector>
In your ListView item xml file, assign an ID to your root layout or whichever element is providing the visible background color. For this example, Ill assume its your root layout. Then in your Adapter's getView(), grab the element that you've assigned the id to and set one of the drawables you created with the color you want for its background. Like so:
@Override
public View getView(int position, View convertView, ViewGroup parent){
//inflate your convertView, etc...
...
ViewGroup baseLayout = (ViewGroup)convertView.findViewById(R.id.<your base layout id>);
//these conditions need to reflect how you decide which list item gets which color
if(position % 2 == 0){
baseLayout.setBackground(R.drawable.background_black);
} else {
baseLayout.setBackground(R.drawable.background_green);
//do whatever else you need
...
return convertView;
}
NOTE: setBackground() is a new function, use setBackgroundDrawable() if coding for older versions of Android
The difficulty here is that pressed/checked color is dynamic. You cannot use static xml color-state-list. But you can create ColorStateList by code. Here is how to do that.
You just have to implement the ListAdapter :
private class MyListAdapter implements ListAdapter{
@Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView!=null){
CheckedTextView textView = (CheckedTextView)convertView;
textView.setText("the text for item "+position);
textView.setTextColor(makeColorStateListForItem(position));
return textView;
}else{
CheckedTextView textView = new CheckedTextView(parent.getContext());
textView.setText("the text for item "+position);
textView.setTextColor(makeColorStateListForItem(position));
return textView;
}
}
private ColorStateList makeColorStateListForItem(int position){
int pressedColor = pressedColorForItem(position);
int checkedColor = checkedColorForItem(position);
int defaultColor = defaultColorForItem(position);
ColorStateList colorStateList = new ColorStateList(
new int[][]{
new int[]{android.R.attr.state_pressed},
new int[]{android.R.attr.state_checked},
new int[]{0},
},
new int[]{
pressedColor, //use when state is pressed
checkedColor, //use when state is checked, but not pressed
defaultColor}); //used when state is not pressed, nor checked
}
private int pressedColorForItem(int position){
//write your business logic to determine color here
return ...;
}
private int checkedColorForItem(int position){
//write your business logic to determine color here
return ...;
}
private int defaultColorForItem(int position){
return Color.WHITE;
}
//all other adapter methods
//...
Note the use of android.R.attr.state_checked
instead of the more intuitive android.R.attr.state_selected
because the state_selected
is not very precisely define with a touch screen (ie state_selected can give the expected behavior on the emulator, but on a real device it will probably fail)
On the other hand state_checked
+ CheckedTextView is going to work correctly on both simulator and real device.
Just call List.setChoiceMode(...);
when initializing listView and ListView.setItemChecked
in the clickListener.
final ListView listView = ...
listView.setAdapter(new MyListAdapter());
listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
listView.setItemChecked(position,true);
}
});
EDIT : to change the item background : just create a StateListDrawable instead of a simple ColorStateList :
private Drawable makeBackgroungForItem(int position){
int pressedColor = pressedBackgroundColorForItem(position);
int checkedColor = checkedBackgroundColorForItem(position);
int defaultColor = defaultBackgroundColorForItem(position);
StateListDrawable stateListDrawable = new StateListDrawable();
stateListDrawable.addState(new int[]{android.R.attr.state_list_pressed}, new ColorDrawable(pressedColor));
stateListDrawable.addState(new int[]{android.R.attr.state_list_checked}, new ColorDrawable(checkedColor));
stateListDrawable.addState(new int[]{0, new ColorDrawable(defaultColor));
return stateListDrawable;
}
And in the getView(...)
textView.setBackground(makeBackgroungForItem(position));
I suggest the following approach:
ListView
Adapter
that supports multiple different items ListItem
will have it's own .xml layout file, you can specify the desired selector there What you need:
ListItem
that every item in the ListView
inherits from View
that represents the item and it's type ListItem
class could have an Integer
field mColor, that holds the color the item represents ListItem
class could have a method to setup the selector with the speficied color Example:
public abstract class ListItem {
public static final int TYPE_WHATEVER_1 = 0;
public static final int TYPE_WHATEVER_2 = 1;
// and so on...
/** the total number of list-item-types */
public static final int TYPE_COUNT = typecounthere;
// if required for your implementation:
protected int mColor;
public abstract int getViewType();
public abstract View getView(LayoutInflater inflater, View convertView);
/** creates and sets the selector with your specified color */
public void setupSelectorColor() {
StateListDrawable states = new StateListDrawable();
ColorDrawable cdPressed = new ColorDrawable(mColor);
ColorDrawable cdSelected = new ColorDrawable(mColor);
ColorDrawable cdDefault = new ColorDrawable(Color.TRANSPARENT);
states.addState(new int[] {
android.R.attr.state_pressed
},
cdPressed);
states.addState(new int[] {
android.R.attr.state_selected
},
cdSelected);
states.addState(new int[] {},
cdDefault);
setBackgroundDrawable(states);
}
}
ListItem
getView(...)
method, inflate your desired layout getViewType()
method Example:
public class ItemTypeOne extends ListItem {
public ItemTypeOne(int color) {
mColor = color;
}
@Override
public int getViewType() {
// return the type
return TYPE_WHATEVER_1;
}
@Override
public View getView(LayoutInflater inflater, View convertView) {
if(convertView == null) {
// inflate the layout
convertView = inflater.inflate(R.layout.item_type_one, null);
}
// setup the selector
setupSelectorColor();
// do other stuff
return convertView;
}
}
Example:
public class ListItemAdapter extends ArrayAdapter<ListItem> {
public ListItemAdapter(Context context, List<ListItem> objects) {
super(context, 0, objects);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
return getItem(position).getView(LayoutInflater.from(getContext()), convertView);
}
@Override
public int getItemViewType(int position) {
return getItem(position).getViewType();
}
@Override
public int getViewTypeCount() {
return ListItem.TYPE_COUNT;
}
}
Putting it all together:
ArrayList<ListItem> list = new ArrayList<ListItem>();
// fill the list
list.add(new ItemTypeOne(somecolor));
list.add(new ItemTypeTwo(somecolor));
list.add(new ItemTypeOne(somecolor));
list.add(new ItemTypeWhatever(somecolor));
ListView lv = (ListView) v.findViewById(R.id.listView1);
ListItemAdapter a = new ListItemAdapter(Context, list);
lv.setAdapter(a);
Concerning CustomViews
and selectors and their behaviour , I suggest reading this question (and answer): How to implement a CustomView with custom selector states?
I'd say don't overcomplicate this. It can be simple as create an array of int
that will contain possible colors, and set them to each item using Random
class.
// This goes inside hosting fragment or activity
listview.setOnItemClickListner( new OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
if(view.isSelected()){
view.setSelected(false);
// also maybe change bg color back to normal?
}
else {
// This one for always a different color
view.setBackgroundColor(adapter.getColor());
// This is for foreground color change instead of background
FrameLayout frameLayout = (FrameLayout) view.findViewById(R.id.my_frame_layout);
final Drawable drawable = new ColorDrawable( /* your getColor() function */ );
frameLayout.setForeground(drawable);
// This one for alwyas the same color for the row at position given by {@param position}
view.setBackgroundColor(adapter.getColor(position));
view.setSelected(true);
}
}
});
// All this goes inside your custom listview Adapter
int[] colors = {
R.colors.red,
R.colors.blue,
...
}
Random random = new Random();
// If each time the selection will bring a different color, use this implementation
public int getColor() {
return colors[random.nextInt(colors.length)];
}
// If each row should have different color, but always the same color for a row then use this one instead
SparseIntArray spa = new SparseIntArray();
public int getColor(int position) {
if(spa.get(position) == 0) {
// the color hasnt been created for that row yet
spa.put(position, colors[random.nextInt(colors.length)];
}
return spa.get(position);
}
**Edit: ** now, if what you want is a foreground selection, then your row should have a FrameLayout
container, and you should change it's 'android:foreground' property:
final Drawable drawable = new ColorDrawable( /* your getColor() function */ );
frameLayout.setForeground(drawable);
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.