So I have a ListView and I want to change the color of each items background and text. This ListView is inside a ListFragment. My code inflates the layout in the onCreateView
and inflates the layout of each item in the newView
.
The android:state_pressed="true"
is working fine, whenever I press in one item the background changes to that color. But when selecting an item neither the bg color or text color changes, even though I've defined an item with android:state_selected="true"
in the selector.
Edit: I'm using SDK level 11 (Android 3.0) and a Motorola Xoom.
The list fragment layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical">
<ListView
android:id="@android:id/list"
android:layout_width="fill_parent"
android:layout_height="fill_parent"/>
</LinearLayout>
The list item layout:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="25dp"
android:background="@drawable/list_item_bg_selector">
<TextView android:id="@+id/form_title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_xlarge"
android:textStyle="bold"
android:textColor="@drawable/list_item_text_selector" />
<TextView android:id="@+id/form_subtitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/text_size_medium"
android:textStyle="normal"
android:layout_marginTop="5dp"
android:textColor="@drawable/list_item_text_selector" />
</LinearLayout>
The background 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="@color/white" />
<item
android:state_selected="true"
android:drawable="@drawable/list_item_bg_selected" />
<item
android:drawable="@color/list_bg" />
</selector>
The text selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_selected="true"
android:drawable="@color/white" />
<item
android:drawable="@color/list_text_blue" />
</selector>
The answer is to use the android:state_activated="true"
state, instead of the "selected" state. More on this here: ListFragment Item Selected Background
The best solution with support of all API levels is to implement Checkable feature for list item View
which means that the top view of your list item layout has to implement Checkable
interface (in my case it was TextView, but the same can be applied on ViewGroup
classes like LinearLayout
). When you click on a list item, the ListView
call setChecked
method and there we change the state of View to use android:state_checked="true"
selector. Together with list view android:choiceMode="singleChoice"
it will select only one item.
The trick is to override onCreateDrawableState
method and set the checked state here for drawables. See example of SelectableTextView
bellow. After the setChecked
is called, the checked state is stored and called refreshDrawableState
.
Example of SelectableTextView
:
package com.example.widget.SelectableTextView;
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.widget.Checkable;
import android.widget.TextView;
public class SelectableTextView extends TextView implements Checkable {
private static final int[] CHECKED_STATE_SET = {
android.R.attr.state_checked
};
private boolean mChecked;
public SelectableTextView(Context context) {
super(context);
}
public SelectableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public SelectableTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public SelectableTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
@Override
public void setChecked(boolean checked) {
if (mChecked != checked) {
mChecked = checked;
refreshDrawableState();
}
}
@Override
public boolean isChecked() {
return mChecked;
}
@Override
public void toggle() {
setSelected(!mChecked);
}
@Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (isChecked()) {
mergeDrawableStates(drawableState, CHECKED_STATE_SET);
}
return drawableState;
}
}
Example of selectable_list_item.xml
layout:
<?xml version="1.0" encoding="utf-8"?>
<com.example.widget.SelectableTextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@android:id/text1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:textColor="@color/list_item_selector_foreground"
android:background="@drawable/list_item_selector_background"
tools:text="Item 1"/>
Example of list_item_selector_foreground.xml
:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- checked -->
<item android:color="@color/list_item_text_active" android:state_checked="true"/>
<item android:color="@color/list_item_text"/>
</selector>
Example of list_item_selector_background.xml
:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="@color/list_item_background_selected" android:state_pressed="true"/>
<item android:drawable="@color/list_item_background_selected" android:state_focused="true"/>
<item android:drawable="@color/list_item_background_active" android:state_checked="true"/>
<item android:drawable="@color/list_item_background"/>
</selector>
Do not forget to set clickable="true"
for the layout. This solved my problem.
List item layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/list_item_bg_selector"
android:clickable="true" >
<TextView
android:id="@+id/tvNewsPreviewTitle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:maxLines="3"
android:ellipsize="end"
android:textSize="@dimen/news_preview_title_textsize"
android:textStyle="bold" />
</RelativeLayout>
Background selector:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="@color/black" />
<gradient android:startColor="@color/white" android:endColor="@color/white" />
</shape>
</item>
<item>
<shape android:shape="rectangle">
<stroke android:width="1dp" android:color="@color/holo_gray_darker" />
<gradient android:startColor="@color/holo_gray_bright" android:endColor="@color/holo_gray_bright" />
</shape>
</item>
</selector>
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