I'm trying to achieve that an icon in ActionBar will not change states discretely, but by fading animation. When I add android:enterFadeDuration and android:exitFadeDuration to the selector tag, my drawable is initially invisible - when I tap it, it changes state to state_pressed (properly with enter fade duration) and when I release it, it turns back to its normal visible unselected state.
I must be missing something obvious, or is this a bug of some kind?
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:enterFadeDuration="150" android:exitFadeDuration="150">
<item android:drawable="#drawable/filters_toggle_icon_selected" android:state_focused="true"/>
<item android:drawable="#drawable/filters_toggle_icon_selected" android:state_pressed="true"/>
<item android:drawable="#drawable/filters_toggle_icon" android:state_focused="false" android:state_pressed="false"/>
</selector>
I had a similar problem, with my code looking like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android"
android:enterFadeDuration="#android:integer/config_mediumAnimTime"
android:exitFadeDuration="#android:integer/config_mediumAnimTime" >
<item android:state_pressed="true" android:drawable="#color/pressed" />
<item android:drawable="#color/default" />
</selector>
At first, I found a hint to get rid of enterFadeDuration and only use exitFadeDuration. That solved the problem with initial invisibility, but the view still faded into invisibility during the first interraction.
Then, I modified my structure as follows:
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/default" />
<item>
<selector android:enterFadeDuration="#android:integer/config_mediumAnimTime"
android:exitFadeDuration="#android:integer/config_mediumAnimTime" >
<item android:state_pressed="true" android:drawable="#color/pressed" />
</selector>
</item>
</layer-list>
Basically, I just pushed the default drawable out of the selector. It's a workaround and it also works for selectors with multiple states, but has some notable limitations:
The default drawable is always visible as a bottom layer. It works for opaque colors, but transparency may cause undesirable results.
If the view starts in one of the states tested by selector, in still displays as default, because the selector still starts as invisible.
It might not be applicable to the original problem, but it's something to consider for overcoming this behaviour of selectors.
Use android:enterFadeDuration="#android:integer/config_mediumAnimTime" and android:exitFadeDuration="#android:integer/config_mediumAnimTime".
My problem was similar as well, the issue was that after setting a background drawable on my view, it was in the wrong state (sometimes it even mixed the stroke and solid of two states...). This was only before the first interaction, like receiving focus or changing enabled state.
I've found that if you call jumpToCurrentState() on the drawable (which has the fade duration properties) after setting it on the view, it will be set on the correct state, and you can keep on using the enter / exit fade duration properties.
Here's how I did it:
val stateList = (darkBackground as? RippleDrawable)?.findDrawableByLayerId(android.R.id.background) as? StateListDrawable
background = darkBackground
stateList?.jumpToCurrentState()
In this example I had a ripple drawable which contained the selector which had the fade properties (I had to add an ID to the selector so I could look it up using the ID).
This seems to be a bug that happens on specific Android versions. You can turn off the android:enterFadeDuration programmatically in Java code, by accessing the Selector with a StateListDrawable:
// Disable android:enterFadeDuration/exitFadeDuration on Android 4.2 only
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.JELLY_BEAN_MR1) {
StateListDrawable stateListDrawable =
(StateListDrawable) animatedButton.getBackground();
stateListDrawable.setEnterFadeDuration(0);
stateListDrawable.setExitFadeDuration(0);
}
Related
I'm not getting the same behavior when tinting an ImageView button with a color state list as I do when using a "drawable selector" (that points to a pre-rendered alternate image).
When the button is tapped there is no color change whatsoever, and when held down for a second or two a color change does occur but sometimes only in the bottom half of the icon.
note: I'm trying to make just the button change color, not the entire background, so that it appears to glow when touched (without having to pre-color it in an editor).
note: I realize these icons are old, and not from the new material set, but I can't use those just yet.
resource/drawable/ic_menu_my_calendar.png
resource/color/calendar_btn_color_state_list.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:color="#A0AA3355"/>
<item android:state_selected="true" android:color="#A0AA3355"/>
<item android:state_checked="true" android:color="#A0AA3355"/>
<item android:color="#FFAAAAAA"/>
</selector>
usage
_calendarButton = new ImageButton(this.Context);
_calendarButton.SetImageResource(Resource.Drawable.ic_menu_my_calendar);
_calendarButton.SetBackgroundColor(Color.Transparent);
var csl = ColorStateList.CreateFromXml(Resources, Resources.GetXml(Resource.Color.calendar_btn_color_state_list));
// all options give the same unexpected behavior
_calendarButton.Drawable.SetTintList(csl); // option 1
_calendarButton.Background.SetTintList(csl); // option 2
_calendarButton.ImageTintList = csl; // option 3
// tried a bunch of modes, didn't help
//_calendarButton.ImageTintMode = PorterDuff.Mode.SrcIn;
The following gives the behavior I'd like to achieve above.
resource/drawable/calendar_btn_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/ic_menu_my_calendar_pressed"/>
<item android:drawable="#drawable/ic_menu_my_calendar"/>
</selector>
usage
_calendarButton = new ImageButton(this.Context);
_calendarButton.SetImageResource(Resource.Drawable.calendar_btn_selector);
_calendarButton.SetBackgroundColor(Color.Transparent);
UPDATE
Found some cool effects using GIMP for pre-render method
It was done with layering
I am writing an Android app and now I am styling it. I am using a custom theme which is a child of Theme.Holo.Light. I really like Theme.Holo.Light because its buttons have a special effect when you click and hold it. Like the lower button in the picture below:
Not click:
click:
The upper button has been changed color. Now when I click on that button, it doesn't have that effect. I really don't understand why. Can anyone tell me why this happens and how can I get the same effect with a colored button?
And also, the colored button seems fatter.
This is because the button uses a selector to display different colors/effects/drawables based on the state of the click. You can check out the link on Color State List Resource.
To create your own you have to create a slecetor cml file and put it in your drawables folder.
For example.
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/shape_btn_default_normal_gray" android:state_enabled="true" android:state_pressed="false"/>
<item android:drawable="#drawable/shape_btn_default_pressed_gray" android:state_pressed="true"/>
<item android:drawable="#drawable/shape_btn_default_disabled_gray"/>
</selector>
or with colors
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/dark_green" android:state_enabled="true" android:state_pressed="false"/>
<item android:drawable="#color/light_green" android:state_pressed="true"/>
<item android:drawable="#color/gray"/>
</selector>
To apply this you have to set the background drawable in your layout xml like this.
<Button
android:id="#+id/my_btn"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:text="Some text"
android:background="#drawable/selector_btn_default_gray"/>
That is the "ripple effect" of material design.You have define you own style for that effect.Link below may help you or you may search for many other answers on StackOverflow. Material effect on button with background color
It does not loses its behavior you can see after click (in your second image) the button show same scale as the above have...so by default the background is set as to show that it is button (like with padding or so) and can changes to show oncklick effect...
So when you set your desire background to button...It takes complete change on what is on presently on it and then you have to manually set onclick effect..
I am trying to get a standard Android ToggleButton to take on a somewhat custom style: in the unchecked state no border/background fill is displayed, in the checked state a background fill is displayed, when pressed (checked or unchecked) a ripple effect is displayed to transition from one state to another.
I've got things working, for the most part using this as my ToggleButton's background:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/ripple" android:state_pressed="true"/>
<item android:drawable="#drawable/checked_bg" android:state_checked="true"/>
<item android:drawable="#android:color/transparent"/>
</selector>
ripple:
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
android:color="?android:attr/colorControlHighlight" >
<item
android:id="#android:id/mask">
<color
android:color="#android:color/white" />
</item>
</ripple>
checked_bg:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle" >
<solid
android:color="?android:attr/colorControlHighlight" />
</shape>
However, when I go from the checked state to the unchecked state, the ripple applies a solid fill to the button that does not match the checked button state fill color, so it appears to "jump" from one color to the next immediately. I believe this is because I am using colorControlHighlight for both the checked state background fill and the ripple color, so the ripple ends up at the color, and starts at another, slightly lighter color...
However, I can't simply change my checked state background fill to this lighter color, since that will make the case when I check/turn on the button look odd - it will start out at the lighter color, matching the checked background fill, and end up at a darker color, and then immediately jump to the lighter color again.
Is there a clean way to handle this? Or am I stuck with a jump in color change at one end?
Alternatively I could use two different ripples, one for the pressed = true and checked = false and the other for the pressed = true and checked = true case, right?
I have some TextViews which are dinamically added to a LinearLayout. These TextViews are clickable and have an onLongClickListener (I also intend to add onClickListener later)
Here is the thing, I want these TextView to change their background color when pressed and I read that you can use selectors to do such thing.
So I made this xml file in res/drawable/text_view_pressed.xml:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"
android:color="#000000"/>
<item android:state_pressed="false"
android:color="#FFFFFF"/>
</selector>
I tried to create a TextView and using this xml file likes this:
TextView t = new TextView(this);
t.setBackgroundColor(R.drawable.text_view_pressed);
But when I do this, it will give this error in t.setBackgroundColor: "Should pass resolved color instead of resource id here: getResources().getColor(R.color.text_view_pressed)" but it doesn't work as intended if I use getResources().getColor(R.color.text_view_pressed).
Anyone got an idea how to do it?
You are on the right track. However there is an important detail.
There are two types of resources that can be affected by states: ColorStateList and StateListDrawable.
A color state list can only be used in certain contexts, for example in TextView.setTextColor(). As far as I can see, you cannot use a color state list as parameter of setBackgroundColor() if you want to change the background of a View when it's pressed. You need a state list drawable for that. And in a state list drawable, the android:drawable attribute is mandatory.
So, to sum up:
The xml file should be placed in res\drawable,
Its structure should be slightly different (i.e. state list, not color list), and
You need to use setBackgroundResource() instead of setBackgroundColor().
Example file:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#android:color/white" />
<item android:drawable="#android:color/black"/>
</selector>
If you want to use custom colors instead of white and black, you simply need to define them as resources in res\values and reference them from here.
I am trying to set up a selector for TextView textColor using the following code:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="?android:attr/textColorTertiary" />
<item android:color="?android:attr/textColorPrimary"/>
</selector>
However, the color always appears to be red instead of those theme colors. If I put hardcoded color, everything seems to work fine. (ex. <item android:state_enabled="false" android:color="#666666" /> ).
What is the problem and how to solve it? P.S. if anyone knows how to set theme's default disabled color for disabled item in the list, please share, that is what I am trying to achieve. Thanks.
As far as i can see you may have to use 3 states in a selector.
state enabled
state focused
state pressed
in exactly this order. This might help
You used selector for what reason?
If you want to make your text of text view always red then no need of selector. Just define color in color.XML or in string.XML using add color.
And if you want to chanhe it on selection or focus than use the states.
state enabled
state pressed
state focused
Than it will work as you need.