I have a custom view LockButton which is just a FrameLayout that holds two specific child views. It holds an ImageButton as well as a type of custom view ProgressIndicator. This question centers around the behavior of LockButton and ImageButton. In particular I have added a custom state to the LockButton. The ImageButton has a StateListDrawable for it's source and a ColorStateList for its background.
The problem is when I change the state in LockButton, that change is not displayed in the ImageButton
What am I doing wrong?
LockButton Class:
public class LockButton extends FrameLayout {
private static final int[] STATE_LOCKED = {R.attr.state_locked};
private boolean locked = true;
View button;
public LockButton(#NonNull Context context) {
this(context, null);
}
public LockButton(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
LayoutInflater.from(context).inflate(R.layout.lock_button, this);
button = findViewById(R.id.lock_button_actual);
}
#Override
public void setOnClickListener(#Nullable OnClickListener l) {
button.setOnClickListener(l);
}
#Override
public int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState;
if(locked){
drawableState = super.onCreateDrawableState(extraSpace + 1);
mergeDrawableStates(drawableState, STATE_LOCKED);
} else {
drawableState = super.onCreateDrawableState(extraSpace);
}
return drawableState;
}
public void lock(boolean locked) {
if(this.locked != locked) {
this.locked = locked;
}
refreshDrawableState();
button.refreshDrawableState();
}
public static class ProgressCircle extends View {...}
}
LockButton Layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:state_locked="true"
android:layout_gravity="center"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<view class="com.example.LockButton$ProgressCircle"
android:layout_width="68dp"
android:layout_height="68dp"
android:layout_gravity="center"
/>
<ImageButton
android:id="#+id/lock_button_actual"
android:layout_width="#dimen/fab_size"
android:layout_height="#dimen/fab_size"
android:background="#drawable/button_lock_background"
android:src="#drawable/icon_lock_status"
android:tint="#color/white"
android:elevation="6dp"
android:layout_gravity="center"
android:duplicateParentState="true"
/>
</FrameLayout>
Attributes XML:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="lockState">
<attr name="state_locked" format="boolean" />
</declare-styleable>
</resources>
Drawable icon_lock_status.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item app:state_locked="false" android:drawable="#drawable/icon_lock_open" />
<item app:state_locked="true" android:drawable="#drawable/icon_lock_closed"/>
</selector>
Drawable button_lock_background.xml:
<?xml version="1.0" encoding="utf-8"?>
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:colorControlHighlight"
>
<item>
<shape android:shape="oval">
<solid android:color="#color/button_lock_color" />
</shape>
</item>
</ripple>
Color button_lock_color.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item app:state_locked="false" android:color="#color/grey_dark" />
<item app:state_locked="true" android:color="#color/vibrant_red" />
</selector>
When I need to change the state, I simply call
lockButton.lock(true); // or false
on the LockButton
I have also tested to see if using default states instead of my custom one would work any better. This did not help. The ImageButton still does not respond to changes in the default states.
Setting a StateListDrawable as a background to the LockButton (FrameLayout) does work. So the state change is working properly on the parent, it is just not reflected in the child.
Related
How can I set selected item background in MvxRecycleView?
I want to change background of selected item while another.
selector_category_item.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:state_enabled="true" >
<shape android:shape="rectangle">
<solid android:color="#color/colorPrimary"/>
</shape>
</item>
<item android:state_checked="false" android:state_enabled="true" >
<shape android:shape="rectangle">
<solid android:color="#color/grayBackground"/>
</shape>
</item>
</selector>
My MvxRecycleView
<MvxRecyclerView
android:overScrollMode="never"
android:scrollbars="vertical"
android:background="#e2e2e2"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
local:MvxItemTemplate="#layout/category_item"
local:MvxBind="ItemsSource Categories; ItemClick SelectCategoryCommand; SelectedItem SelectedCategory"
android:id="#+id/categoryRecyclerView" />
And category_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp"
android:clickable="true"
android:background="#drawable/selector_category_item"
android:layout_marginBottom="1dp"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ffimageloading.cross.MvxCachedImageView
android:id="#+id/categoryImage"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
local:MvxBind="DrawableName CategoryImage" />
<ImageView
android:id="#+id/favIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentEnd="true"
android:src="#drawable/no_fav_icon" />
</RelativeLayout>
<TextView
android:clickable="true"
android:focusableInTouchMode="true"
android:focusable="true"
android:layout_marginTop="8dp"
android:id="#+id/categoryText"
style="#style/TextStyleBlack"
android:fontFamily="#font/roboto_regular"
android:letterSpacing="-0.04"
android:lineSpacingExtra="0sp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:MvxBind="Text Name"
android:textSize="14sp" />
</LinearLayout>
I need change selected item background to other while item is selected.
How can I sort it out?
You can make a custom MvxRecyclerView adapter to handle selection:
public class MyRecyclerAdapter : MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerAdapter
{
public MyRecyclerAdapter(IMvxAndroidBindingContext bindingContext) : base(bindingContext)
{
}
public MyRecyclerAdapter(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
{
}
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
base.OnBindViewHolder(holder, position);
holder.ItemView.Selected = _selectedPosition == position;
holder.ItemView.Click += (s, e) => SelectIndex(holder.AdapterPosition);
}
private void SelectIndex(int index)
{
NotifyItemChanged(_selectedPosition);
_selectedPosition = index;
NotifyItemChanged(_selectedPosition);
}
private int _selectedPosition = RecyclerView.NoPosition;
}
Then you want to set the MvxRecyclerView.Adapter property somewhere in your view. The ideal place is usually OnCreate for an activity, or OnCreateView for a fragment:
recyclerView.Adapter = new MyRecyclerAdapter((IMvxAndroidBindingContext)BindingContext);
At this point you have a RecyclerView which sets the item you click on as selected. Now you can use a ColorStateList to change the color depending on the selection state. Add the file to Resources\drawable\selector_category_item.xml:
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="#color/colorAccent"
android:state_pressed="true" />
<item
android:drawable="#color/colorPrimary"
android:state_selected="true" />
</selector>
Finally set the background of the category_item.xml LinearLayout to:
android:background="#drawable/selector_category_item"
It's worth pointing out you can also use a different ColorStateList to change the android:textColor as desired based on the selection state as well. You just need to put those ColorStateList XML files in the Resources\color folder instead of Resources\drawable.
I posted a working sample on GitHub.
Make boolean flag to save selected state for each category item on Category model.
On your adapter viewHolder make instance for recycler item root view mBackground.
On onBindViewHolder update mBackground selection status.
class MyDataHolder extends RecyclerView.ViewHolder
implements View
.OnClickListener {
LinearLayout mBackground;
MyDataHolder(View itemView) {
super(itemView);
mBackground = (LinearLayout) itemView.findViewById(R.id.my_item_root_view);
itemView.setOnClickListener(this);
}
#Override
public void onBindViewHolder(MyCategoryAdapter.MyDataHolder holder, int position) {
final Category category = getItem(position);
holder.mBackground.setSelected(category.isSelected());
}
I am trying to implement a custom drawable state. I can see the state changing inside my custom view, however visually there is no colour change. Any help appreciated!
Here's my code:
selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/com.example.test.myapplication">
<item android:drawable="#drawable/defaultt" />
<item android:drawable="#drawable/error" app:my_state="1" />
<item android:drawable="#drawable/success" app:my_state="2" />
</selector>
error.xml
<?xml version="1.0" encoding="UTF-8"?>
<shape
android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android">
<corners android:radius="3dip" />
<stroke android:width="3dip" android:color="#ff0000" />
<solid android:color="#ff0000"/>
</shape>
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyState">
<attr name="my_state" format="integer" />
</declare-styleable>
</resources>
activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:orientation="vertical"
android:paddingBottom="#dimen/activity_vertical_margin" tools:context=".MainActivity">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<com.example.test.myapplication.CustomView
android:id="#+id/view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/selector"
/>
</LinearLayout>
CustomView.java
public class CustomView extends View {
public static final int DEFAULT = 0;
public static final int ERROR = 1;
public static final int SUCCESS = 2;
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
}
private int state = DEFAULT;
private static final int[] VALIDATION_STATE = new int[] { R.attr.my_state };
#Override
protected int[] onCreateDrawableState(int extraSpace) {
if( state == DEFAULT ) {
return super.onCreateDrawableState(extraSpace);
}
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (state > 0) {
Log.d("tag", "state is "+state);
mergeDrawableStates(drawableState, VALIDATION_STATE);
}
return drawableState;
}
public void setState(int state) {
this.state = state;
refreshDrawableState();
}
}
MainActivity.java
public class MainActivity extends ActionBarActivity {
private int mCurrentState = CustomView.DEFAULT;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (mCurrentState != CustomView.ERROR) {
((CustomView) findViewById(R.id.view)).setState(CustomView.ERROR);
mCurrentState = CustomView.ERROR;
} else {
((CustomView) findViewById(R.id.view)).setState(CustomView.SUCCESS);
mCurrentState = CustomView.SUCCESS;
}
}
});
}
}
Selector states should be defined as boolean attributes. You can place them directly inside a <resources> element -- you don't need to place them inside a <styleable> element.
res/attrs.xml:
<resources>
...
<attr name="state_mystate" format="boolean" />
</resources>
When you reference the attribute from a selector <item>, a value of true means the state must exist within the current state set while a value of false means the state must not exist. If you don't include a state on the <item> the it doesn't matter whether it exists within the current state set.
When matching is performed on the selector, the items are matched in the order they are defined. If the first item has no states defined, as it does in your example, it will match all possible states and always be matched first. As a result, you should order your selector items as most-specific to least-specific.
The example below is for drawable, but it applies to all selectors (color, etc.).
res/drawable/my_selector.xml:
<selector>
<!-- Matches only state sets that include app:state_mystate
and don't include android:state_pressed. -->
<item app:state_mystate="true"
android:state_pressed="false" ... />
<!-- Matches all state sets that include state_mystate. -->
<item app:state_mystate="true" ... />
<!-- Matches all possible states. -->
<item ... />
</selector>
Use Selector as view's background, like following codes :
my_selector.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" android:drawable="#drawable/cell_bg_e" />
<item android:state_checked="true" android:drawable="#drawable/cell_bg_e" />
<item android:state_selected="true" android:drawable="#drawable/cell_bg_e" />
<item android:drawable="#drawable/cell_bg_n_trans" />
</selector>
MyView.java
public class MyView extends LinearLayout
{
public MyView(Context context, CharSequence text, Drawable drawable) {
super(context);
setBackgroundResource(R.drawable.my_selector);
}
}
it works on all the devices except for some certain 800x480 resolution device(lick htc g12)
why ?
You should ensure the view you have set this selector drawable as a background of is clickable.
You can do this in code:
public MyView(Context context, CharSequence text, Drawable drawable) {
super(context);
setClickable(true);
setBackgroundResource(R.drawable.my_selector);
}
Alternatively in XML:
<MyView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/my_selector"
android:clickable="true"
android:orientation="horizontal"/>
I'm struggling with this already from hours and I cannot find a solution, I hope someone can help me.
I have a ListView with a custom adapter extending BaseAdapter and each element of the list extends a LinearLayout containing only a TextView. I started basically from How to add a custom button state.
This is the list_item.xml layout file of the list element:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.mypackage"
android:id="#+id/newItem"
app:state_playing="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/item_selector"
android:orientation="horizontal" >
<TextView
android:id="#+id/textview"
style="#style/Text_View_Style_White"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:ellipsize="marquee"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:padding="3dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="#drawable/item_text_selector" />
</LinearLayout>
Here is the relevant code of the BaseAdapter:
class AlbumListAdapter extends BaseAdapter implements Checkable {
private Context mContext;
public AlbumListAdapter(Context context) {
mContext = context;
}
#Override
public int getCount() {
return listSize; // is a variable
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
AlbumViewHolder albumHolder = null;
if (convertView == null) {
// Inflate the layout
LayoutInflater li = getActivity().getLayoutInflater();
convertView = li.inflate(R.layout.list_item, null);
albumHolder = new AlbumViewHolder(mContext, null);
albumHolder.albumTitle = (TextView) convertView
.findViewById(R.id.textview);
convertView.setTag(albumHolder);
} else {
albumHolder = (AlbumViewHolder) convertView.getTag();
}
albumHolder.albumTitle.setText("Some text in the TextView");
albumHolder.albumTitle.setSelected(true);
if (myBooleanCondition)
{
albumHolder.setPlaying(true);
albumHolder.refreshDrawableState();
for (int i = 0; i < albumHolder.getDrawableState().length; i++) {
if (albumHolder.getDrawableState()[i] != 0)
System.out.println(TAG + " State i = "
+ i + " -> " + getResources().getResourceEntryName(albumHolder.getDrawableState()[i]));
}
}
return convertView;
}
}
and here the list element class extending the LinearLayout:
static class AlbumViewHolder extends LinearLayout {
private static final int[] STATE_PLAYING_SET = { R.attr.state_playing };
private boolean mIsPlaying = false;
TextView albumTitle;
public AlbumViewHolder(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setPlaying(boolean isPlaying) {
mIsPlaying = isPlaying;
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super
.onCreateDrawableState(extraSpace + 1);
if (mIsPlaying) {
System.out.println(TAG
+ " - onCreateDrawableState() -> mIsPlaying = "
+ mIsPlaying);
mergeDrawableStates(drawableState, STATE_PLAYING_SET);
}
return drawableState;
}
}
Here is the attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="music">
<attr name="state_playing" format="boolean" />
<attr name="playing" format="boolean" />
</declare-styleable>
</resources>
and here the 2 selectors trying to use the selector state state_playing, one for the background of the LinearLayout of each element (item_selector.xml):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/com.example.mypackage">
<item android:drawable="#color/blue_fluo3" app:state_playing="true"/>
<item android:drawable="#color/orange_fluo" android:state_pressed="true" app:state_playing="false"/>
<item android:drawable="#color/orange_fluo" android:state_activated="true" app:state_playing="false"/>
<item android:drawable="#android:color/transparent" app:state_playing="false"/>
</selector>
and one for the textColor of the TextView (item_text_selector.xml):
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res/com.example.mypackage">
<item app:state_playing="true" android:color="#color/orange_fluo"/>
<item android:state_pressed="true" app:state_playing="false" android:color="#color/black"/>
<item android:state_activated="true" app:state_playing="false" android:color="#color/black"/>
<item app:state_playing="false" android:color="#android:color/white"/>
</selector>
The result is the same for both: the state_playing seems to work but only in state "false", because if I add a line at the beginning of the selector state list with <item app:state_playing="false" android:color="#color/yellow"/> for the text selector and <item android:drawable="#color/red" app:state_playing="false"/> for the background selector everything is working as expected, I can see the LinearLayout with red background and the text with yellow text!
I really don't know what to try anymore... (even if I set the property by default to the objects (app:state_playing="true") in the xml layout it doesn't work!
Any help, hint or experience sharing is extremely appreciated!
It looks okay to me,but I'd suggest setting duplicateParentState in the container layout as below. I have a vague memory of having issues along those lines myself...
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.example.mypackage"
android:id="#+id/newItem"
android:duplicateParentState="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/item_selector"
android:orientation="horizontal" >
<TextView
android:id="#+id/textview"
style="#style/Text_View_Style_White"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:duplicateParentState="true"
android:ellipsize="marquee"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:padding="3dp"
android:scrollHorizontally="true"
android:singleLine="true"
android:textColor="#drawable/item_text_selector" />
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>
#Andrew S: Along with using activated state in selector , activated state must be set to false for default case as shown in below selector code.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false"
android:state_activated="false"
android:color="#color/dark_text_blue"/>
<item android:state_pressed="true"
android:color="#color/red"/>
<item android:state_activated="true"
android:color="#color/red"/>
</selector>