I have the following problem:
My app has a light and a dark theme and I'm trying to apply touch feedback to some custom views for both above and below v21 (ripple touch feedback). I've created two drawable resources in drawable-v21 for a toggle button, one for light and one for dark:
Dark:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/color_control_highlight_default">
<item android:id="#android:id/mask" android:drawable="#drawable/dark_button_border"/>
<item android:drawable="#drawable/button_toggle_states"/>
</ripple>
Light:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?attr/color_control_highlight_default">
<item android:id="#android:id/mask" android:drawable="#drawable/light_button_border" />
<item android:drawable="#drawable/button_toggle_states" />
</ripple>
As can be seen both resources reference another drawable which defines the colours for different states of the buttons:
button_toggle_states:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?attr/color_primary_1" android:state_focused="true" />
<item android:drawable="?attr/color_primary_1" android:state_checked="true" />
<item android:drawable="?attr/color_primary_1" android:state_selected="true" />
<item android:drawable="?attr/button_border" />
</selector>
This drawable references attributes that change between the light and the dark theme and this is where my problem is. When trying to use this drawable I get a runtime error that the button_toggle_states in res/drawable-v21 can't be found. When I change the drawable to reference colours and other drawables instead of attributes, i.e.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/lt_blue" android:state_focused="true" />
<item android:drawable="#color/lt_blue" android:state_checked="true" />
<item android:drawable="#color/lt_blue" android:state_selected="true" />
<item android:drawable="#drawable/light_button_border" />
</selector>
everything works fine. But with that solution I'll have to make two button_toggle_state files, one for dark and one for light theme, while I thought this shouldn't be needed since we can reference attributes in v21 and up.
Is there somethin I'm doing wrong or is referencing attributes in a drawable that is used by another drawable just not supported?
i ran into the same issue and was not able to get this running. My workaround is to have the drawable itself as a reference in the theme and then have two Drawables: one for light and one for dark.
See also:
https://code.google.com/p/android/issues/detail?id=26251
How to reference colour attribute in drawable?
Related
The following is the XML for my current BottomNavigationView. Currently, all three of the icon drawables are unfilled icons with the same color. I would like be able to present a filled in version of the icon when that state is selected as well as possibly changing the color to make it obvious it is the current icon state. The following image is an example of what I mean.
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="#+id/action_favorites"
android:enabled="true"
android:icon="#drawable/icon_flyer"
android:title="Flyer"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_schedules"
android:enabled="true"
android:icon="#drawable/icon_list"
android:title="List"
app:showAsAction="ifRoom" />
<item
android:id="#+id/action_music"
android:enabled="true"
android:icon="#drawable/icon_contact"
android:title="Contact"
app:showAsAction="ifRoom" />
</menu>
To change the icon color by state you can set a color state drawable for the "itemIconTint" property in your BottomNavigationView. For the text color you can set the same color state drawable in the "itemTextColor" property. Here is an example of a color state drawable for the BottomNavigationView:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/green" android:state_pressed="true" />
<item android:color="#color/green" android:state_checked="true" />
<item android:color="#color/gray" />
</selector>
The "android:state_pressed" state is the menu item's pressed state. The "android:state_checked" is the menu item's selected state.
This only changes the color of the icons and labels in your BottomNavigationView. For filling your icon you can set an icon state drawable for the "icon" property in your menu item. Here is an example of an icon state drawable:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ic_favorites_filled" android:state_checked="true" />
<item android:drawable="#drawable/ic_favorites" />
</selector>
Create res/color/bottom_nav_color.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:color="your_highlight_color" />
<item android:state_pressed="true" android:color="your_highlight_color" />
<item android:color="your_inactive_color" />
</selector>
Then in your BottomNavigationView:
app:itemTextColor="#color/bottom_nav_color"
app:itemIconTint="#color/bottom_nav_color"
Step 1: add all the icons to your drawable folders
Step 2: create new drawable resource file for each icon. In the file named "bottom_navigation_icon" (for example), specify selectors. The default icon needs to be stated in the last line:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ic_home_active"
android:state_checked="true"/>
<item android:drawable="#drawable/ic_home_inactive"/>
</selector>
Step 3: in your menu for the bottom navigation, set the code below for each item:
android:icon="#drawable/bottom_navigation_icon"
That's all. Now your icon changes when you click on it and changes back to the previous one when you click away!
You can set a StateDrawable to your menuitem.
Create an xml file in your drawable folder with the following code:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/button_pressed_green"
android:state_pressed="true" />
<item android:drawable="#drawable/button_normal" />
</selector>
Update the xml file that it fits your needs (state and drawable) and reference it in your menuitem instead of your current icon.
I currently have a set of ToggleButtons, enclosed by a RadioGroup, with each button referring to a selector that changes the color of the button state, and writing code to implement the radio-buttonesque behavior. This works great. (EDIT: I should mention that the selector is what the android:background element of the button is referred to)
The Selector is something like this
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/gold"
android:state_checked="true" />
<item android:drawable="#color/white" />
</selector>
Now I want a second set of buttons to have the exact same behavior. The catch is that this time, I want Images on some of them, not text.
There are lots of StackOverflow posts on how to change the Image based on the Button's state but I don't want to change the image based on the state. I want to change the color of the button based on the state (the images have transparent backgrounds).
I'm really trying to avoid modifying the button images themselves.
I believe the solution involves making a selector with a layer-list but this is new territory for me.
Create a xml named colors.xml in res/values folder:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="red">#ff0000</color>
<color name="green">#00ff00</color>
</resources>
In drawable folder, create a xml file my_btn_toggle.xml:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="false" android:drawable="#color/red" />
<item android:state_checked="true" android:drawable="#color/green" />
</selector>
and in xml section define your toggle button:
<ToggleButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/btntoggle"
android:background="#drawable/my_btn_toggle"/>
I think you are looking for tinting attributes - mentioned here.
In your case, try to set ColoStateList to android:tint attribute. In ColorStateList doc you have example that shows color selector - like drawable selector, but for colors.
(I'm not sure it's already support pre-Lollipop devices or you should do it programatically with PorterDuff filters for older versions.)
What apparently one needs to do is create a layered drawable in the selector like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true">
<layer-list>
<item android:drawable="#color/gold"
android:gravity="fill"/>
<item android:drawable="#drawable/image"
android:gravity="fill"/>
</layer-list>
</item>
<item android:state_checked="false">
<layer-list>
<item android:drawable="#color/white"
android:gravity="fill"/>
<item android:drawable="#drawable/image"
android:gravity="fill"/>
</layer-list>
</item>
</selector>
The standard approach to variable state button selectors is what Priyank mentions above. Just the graphic must be transparent and declared like above, such that it will be layered with a background color to pull off the color state change
I am using attr to create a selector drawable for my project so that once i change theme colors, i dont have to make any change in the drawable file. I am using following libs:
compile 'com.android.support:appcompat-v7:+'
compile 'com.android.support:cardview-v7:+'
compile 'com.android.support:design:22.2.0'
Here is the source code for drawable:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?attr/colorPrimary" android:state_enabled="true" android:state_window_focused="false"/>
<item android:drawable="?attr/colorPrimaryDark" android:state_pressed="true"/>
<item android:drawable="#android:color/darker_gray" android:state_enabled="false"/>
<item android:drawable="?attr/colorPrimary"/>
</selector>
in this same code, if i replace attributes with colors defined in colors.xml file, the same drawable works.
Sample drawable with colors:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/color_primary" android:state_enabled="true" android:state_window_focused="false"/>
<item android:drawable="#color/color_primary_dark" android:state_pressed="true"/>
<item android:drawable="#android:color/darker_gray" android:state_enabled="false"/>
<item android:drawable="#color/color_primary"/>
</selector>
Thanks in advance!
Finally, found the problem. There is a bug in android [pre-lollipop] OS which doesnt allow you to use attr in drawable. Here is the link to bug:
https://code.google.com/p/android/issues/detail?id=26251
Android dev team has released a fix but it works on android L and above.For workaround to this problem, refer to following solution:
How to reference style attributes from a drawable?
I encountered the same problem and as of 2019 it hasn't been resolved so you can't have an attribute referenced in a selector as a drawable. I will share the solution I got for the problem as I don't see it posted in here. I found it in the last comment of the bug report also referred by mudit in his answer.
The workaround is basically create a drawable resource that will be the one referring the attribute value.
To illustrate your case the solution would be instead of:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="?attr/colorPrimary" android:state_enabled="true" android:state_window_focused="false"/>
<item android:drawable="?attr/colorPrimaryDark" android:state_pressed="true"/>
<item android:drawable="#android:color/darker_gray" android:state_enabled="false"/>
<item android:drawable="?attr/colorPrimary"/>
</selector>
you would replace the ?attr/* for a drawable resource:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/colorPrimaryDrawable" android:state_enabled="true" android:state_window_focused="false"/>
<item android:drawable="#drawable/colorPrimaryDarkDrawable" android:state_pressed="true"/>
<item android:drawable="#android:color/darker_gray" android:state_enabled="false"/>
<item android:drawable="#drawable/colorPrimaryDrawable"/>
</selector>
The drawables would be defined as:
drawable/colorPrimaryDrawable
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle">
<solid android:color="?attr/colorPrimary" />
</shape>
drawable/colorPrimaryDarkDrawable
<shape xmlns:android="http://schemas.android.com/apk/res/android"android:shape="rectangle">
<solid android:color="?attr/colorPrimaryDark" />
</shape>
Hope it helps!!
I dunno if it's still relevant to you, but I was struggling with the same thing and I think I have found a workaround (kinda). The direct use of ?attr/ is not working (working on api 21 and up, but even then it's not working with color selectors (only with drawables).
So I did it like this (giving you my own example).
Create an atribute in attr.xml
<attr name="nav_item_color_selector" format="reference|color"/>
Then in all the themes you're using, add the attribute like so (f.e. for the Light Theme):
<item name="nav_item_color_selector">#color/text_color_selector_light</item>
or for the dark theme(default):
<item name="nav_item_color_selector">#color/text_color_selector</item>
Now my text_color_selector.xml (both) look like this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
android:color="#color/myAccentColor" />
<item android:state_selected="false"
android:color = "#color/myTextPrimaryColor"/>
<item android:color="#color/myTextPrimaryColor" />
and when I want to use them, f.e in tinting my custom image view, I use:
<com.yourdomain.yourpackage.NavDrawerIcon
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:src="#mipmap/ic_launcher"
android:id="#+id/nav_item_icon"
android:layout_margin="24dp"
android:tint = "?attr/nav_item_color_selector"
android:tintMode="src_atop"/>
You can also reitieve it programmatically by using TypedValue, like so:
TypedValue typedValue = new TypedValue();
Resources.Theme theme = context.getTheme();
theme.resolveAttribute(R.attr.nav_item_color_selector, typedValue, true);
XmlResourceParser parser = viewGroup.getContext().getResources().getXml(typedValue.resourceId);
try {
ColorStateList sl = ColorStateList.createFromXml(viewGroup.getContext().getResources(), parser);
viewHolder.textView.setTextColor(sl);
} catch (Exception e) { }
I hope this helps :-)
I ran into a similar problem while using attributes for color in a drawable selector.
I solved it by making a color selector instead of a drawable selector.
Basically, make a new resource, with Resource Type as Color, which will be your color selector. Here, unlike with drawable selectors, you can use your attributes without issue:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="?colorBackgroundDisabled" android:state_enabled="false" />
<item android:color="?colorBackground" />
</selector>
^I named this selector_background.
Then you can use that selector:
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#color/selector_background"/>
In stead of putting a "?" Change it to "#"
I have a drawable selector as a background for each item in a ListView to highlight the selected row. Eveything works fine in Ice Cream Sandwich, but doesn't seem to work in Jelly Bean. Can't find any documentation saying what changes could have caused it to stop working and what I need to do to fix it.
By not working, I mean when I click on a row in the ListView the item's background color isn't turning the #color/blue color, but it does in ICS.
This is the selector code I'm using (listing_selector.xml):
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_focused="true" android:drawable="#color/blue" />
<item android:state_pressed="true" android:drawable="#color/blue" />
<item android:state_activated="true" android:drawable="#color/blue_selected" />
<item android:state_selected="true" android:drawable="#color/blue_selected" />
<item android:drawable="#android:color/transparent" />
</selector>
This is the layout of the ListView item:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:background="#color/listing_selector"
>
<TextView
android:id="#+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
/>
</RelativeLayout>
This the blue color resource:
<resources>
<color name="blue">#ff33b5e5</color>
</resources>
UPDATE 1:
Tried moving the selector from the color folder to the drawable folder and updating the code to this:
android:background="#drawable/listing_selector"
UPDATE 2:
Also, on the ListView, tried adding this:
<ListView android:id="#android:id/list"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:listSelector="#drawable/listing_selector"
/>
UPDATE 3:
I thought it might be something in my code, but I removed all the code from onListItemClick of the ListView and still the selector isn't working.
UPDATE 4:
I've narrowed it down to state_selected or state_activated not working as, state_pressed seems to be working
UPDATE 5:
I think I was mistaken. I don't think the selector is being recognized, at all. I was confusing the built-in ListView highlighting as my selector. I'm now wondering if it has something to do with the way my project is setup. I have the selector in a Library Class. Maybe something changed with that from ICS to JB, however moving the selector to my app's project didn't seem to fix it.
UPDATE 6:
Ok, after some more hair pulling, I've narrowed it down, again, to either state_selected or state_activated not being recognized, as changing the color for state_pressed does work, which means my selector is being recognized. From the comments in seems to be something with my app specifically as others have been able to get selectors working with Jelly Bean.
Though something else that is interesting is that changing the drawable value for the default state is not recognized. Where I have color/transparent, I would think changing that to a color would cause the listing to change to that color, but it doesn't.
Also, this isn't working in ICS either.
UPDATE 7:
After even more hair pulling, I've discovered that long-pressing on a menu item results in that item's color being changed. Just clicking on an item still does not work. Not even sure what the means.
** Final Update:**
I give up, I removed the selector and am just refreshing the ListView on click and remembering the position clicked and highlighting it from code. Not ideal, but not worth the effort to try to fix.
Okay, i think its an issue with your selector. Try removing the state_focused and the state_activated. You could try this for your selector:
<!-- Disabled State -->
<item
android:state_enabled = "false"
android:state_focused = "true"
android:state_pressed = "true"
android:drawable="#android:color/transparent">
</item>
<item
android:state_enabled = "false"
android:state_focused = "true"
android:drawable="#android:color/transparent">
</item>
<!-- Pressed State -->
<item
android:state_pressed = "true"
android:state_focused = "true">
<shape>
<solid android:color="#color/blue"/>
</shape>
</item>
<item
android:state_pressed = "true"
android:state_focused = "false">
<shape>
<solid android:color="#color/blue"/>
</shape>
</item>
<!-- Normal State -->
<item
android:drawable="#android:color/transparent">
</item>
I found that I needed to use a shape object too instead of android:drawable, because on pre-ICS phones, the whole list will be highlighted that color instead of the pressed list item.
You can add in your state_selected code too, but I'm not sure how it will be used. Check out the default selector code for jelly bean for the states they use: list_selector_background.xml.
listing_selector.xml must be in res/drawable folder and set the android:background attribute of your RelativeLayout like this:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:orientation="horizontal"
android:background="#drawable/listing_selector"
>
I cant say the reason why its working in one version and not working in another, but I got a alternative solution.
Define your color as a drawable in resources
<?xml version="1.0" encoding="utf-8"?>
<resources>
<drawable name="blue">#00F</drawable>
...
</resources>
Use this color drawable in your 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="#drawable/blue" />
...
...
</selector>
Try this and let me know.
From the API Docs:
Note: Remember that Android applies the first item in the state list
that matches the current state of the object. So, if the first item in
the list contains none of the state attributes above, then it is
applied every time, which is why your default value should always be
last (as demonstrated in the following example).
Thus, try to reorder the states of your selectors according to the suggested order. In your case that would be this:
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_pressed="true" android:drawable="#color/blue" />
<item android:state_focused="true" android:drawable="#color/blue" />
<item android:state_selected="true" android:drawable="#color/blue_selected" />
<item android:state_activated="true" android:drawable="#color/blue_selected" />
<item android:drawable="#android:color/transparent" />
</selector>
I think it works fine !!
your selector xml file code:-
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/gray" android:state_focused="true"/>
<item android:drawable="#color/gray" android:state_pressed="true"/>
<item android:drawable="#color/orange" android:state_activated="true"/>
<item android:drawable="#color/orange" android:state_selected="true"/>
<item android:drawable="#color/orange"/>
</selector>
now create a folder values-v16 under res folder and put this colors file in it.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="gray">#4f4f4f</color>
<color name="orange">#e26c0e</color>
</resources>
and then the listview :
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="20dp"
android:listSelector="#drawable/listing_selector" >
</ListView>
Hope this works ! Tested on version 4.1
This is a very old question but I am still supporting Jellybean and I've noticed for background selectors to work I need to do something in particular. If I have a viewgroup with a background drawable and I need selector colors on it I need to do things in this order:
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/my_drawable_button"/>
my_drawable_button must now reference a selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/primary_background_pressed"
android:state_pressed="true" />
<item android:drawable="#drawable/primary_background"
android:state_enabled="true" />
<item android:drawable="#drawable/primary_background_disabled"
android:state_enabled="false" />
</selector>
primary_background.xml:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="#color/secondary" />
</shape>
The others are the same with different colors. This is an awful lot of code to do something very basic, but I have found on Jellybean it's the only solution.
Future API levels allow you to specify the color selector on the drawable solid itself, eliminating 4 files:
post api 21:
<?xml version="1.0" encoding="utf-8"?>
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="#color/purple">
<item>
<shape android:shape="rectangle">
<corners android:radius="8dp" />
<solid android:color="#color/button_selector" />
</shape>
</item>
</ripple>
button_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#color/gray" android:state_enabled="false"/>
<item android:color="#color/secondary"/>
</selector>
I use this for my own button to set the background for state pressed and unpressed:
<?xml version="1.0" encoding="utf-8"?>
<item android:drawable="#drawable/btn1_pressed" android:state_pressed="true"/>
<item android:drawable="#drawable/btn1_unpressed"/> <!-- default -->
But I also like the textColor to become white in state pressed.
I tried to just add textColor="#ffffff" to the item but I guess that's not the correct way, since it did not work.
What would be the best approach without having to code it in each java file that uses this button?
Thanks!
The best is to set Textcolor and Background resource separate.
android:background="#drawable/background"
android:textColor="#drawable/button_text_color"
so your Background: as you wrote it yourself
and Textcolor:
<?xml version="1.0" encoding="UTF-8"?>
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="#ffffffff" />
<item android:color="#ff000000" />
</selector>