ImageButton Icon Tint based on State - android

I am currently trying to implement tinting on some ImageButtons with currently pure white Drawables. The Tint Color should be provided by a StateList and should change depending on the buttons current state. The state list does look like:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#FF0000"/>
<item android:color="#00FF00" android:state_enabled="false"/>
<item android:color="#0000FF" android:state_pressed="true" android:state_enabled="true"/>
</selector>
And the layout XML snippet for the button is:
<ImageButton
android:id="#+id/btnNext"
style="#style/Widget.AppCompat.Button.Borderless"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="#dimen/vertical_content_padding"
android:layout_marginEnd="#dimen/horizontal_content_padding"
android:contentDescription="#string/next"
android:src="#drawable/ic_chevron_right_white_24dp"
android:tint="#color/state_btn_tint_light"/>
The default tint from the Color State List is selected and correctly displayed. But disabling the button or pressing it doesn't trigger any color change at all of the Icon. I tried setting it pragmatically too with setImageTintList.

Your code should work. However, there is one mistake:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#FF0000"/>
<item android:color="#00FF00" android:state_enabled="false"/>
<item android:color="#0000FF" android:state_pressed="true" android:state_enabled="true"/>
</selector>
You need to reverse the order of the states.
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:color="#0000FF" android:state_pressed="true" android:state_enabled="true"/>
<item android:color="#00FF00" android:state_enabled="false"/>
<item android:color="#FF0000"/>
</selector>
I am not entirely sure how it works internally but it seems to function like If/Else statements. If the first condition fails, then it goes to the second. Since your first item has no conditions, it will always be picked and the others will be ignored, giving the impression that the color state list is not working at all. Hence, always put the color state with the most restrictive conditions first.

I'm going to simplify my answer. hopefully, it helps.
You can always enter any color you want in colors.xml right? so let's use it.
you can create an integer array list of the color you want like this
integer[] colors = new integer[]{R.color.white, R.color.black, ...};
Now imageButtons can take background tint like this for example:
imagebutton.setImageTintList(ColorStateList.valueOf(ContextCompat.getColor(mContext,R.color.white)));
Now let's put them together with an animation, create animation method:
public ValueAnimator ColorAnimation(ImageButton imagebutton, int colorFrom, int colorTo){
ValueAnimator anim = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
anim .addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator animation) {
imagebutton.setImageTintList(ColorStateList.valueOf((int) animation.getAnimatedValue()));
}
});
anim.setInterpolator(new DecelerateInterpolator());
anim.setDuration(300); // -- Whatever time
return anim;
};
Now, in your activity, you can implement it like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.layout);
mContext = this;
...
ImageButton myButton = (ImageButton) findViewById(R.id.imagebutton);
//-- Define All the Colors You want
integer[] listOfColors = new integer[]{R.color.white, R.color.black, R.color.yellow, R.color.red, R.color.blue};
//-- With this you can randomly get a color from the list
int aColor = (int)(Math.random()*listOfColors.length);
//-- Your Default imageButton Color
int DefaultColor = ContextCompat.getColor(mContext,R.color.white);
myButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ColorAnimation(myButton, DefaultColor, listOfColors[aColor]).start;
//-- You can even enter whatever color want directly for "colorTo"
//-- You can Reverse this animation as well
}
});
...

Related

Pressed State Not Being Selected in ImageButton Tint List

I have a view class TintSelectorImageButton that applies a tint using DrawCompat::setTintList:
public class TintSelectorImageButton extends ImageButton{
// Actual resource values seem to be fairly large and positive, so -1 should be a safe sentinel
protected static final int NO_TINT = -1;
public TintSelectorImageButton(Context context, AttributeSet attrs){
super(context, attrs);
TypedArray args = context.obtainStyledAttributes(attrs, R.styleable.TintSelectorImageButton);
if(args.hasValue(R.styleable.TintSelectorImageButton_tintList)){
int colorStates = args.getResourceId(R.styleable.TintSelectorImageButton_tintList, NO_TINT);
if(colorStates != NO_TINT){
ColorStateList colorStateRes = ContextCompat.getColorStateList(context, colorStates);
Drawable wrappedDrawable = DrawableCompat.wrap(getDrawable().mutate());
DrawableCompat.setTintList(wrappedDrawable, colorStateRes);
setImageDrawable(wrappedDrawable);
}
}
args.recycle();
}
}
I am providing it a ColorStateList through the defined xml property that looks like this one:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:color="#color/sheet_toolbar_disabled"
android:state_enabled="false"/>
<item
android:color="#color/sheet_nav_clicked"
android:state_pressed="true"/>
<item
android:color="#color/sheet_toolbar_enabled"/>
</selector>
Unfortunately, the pressed state is not actually showing when I touch the button, but click events are being passed properly to the provided View.OnClickListener:
mPrevSheetButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
showSheet(mPrevSheetUid);
}
});
To get things to work for now, I just took the image and colored it in an image editor and used this selector:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:drawable="#drawable/button_prev_light_blue"
android:state_pressed="true"/>
<item
android:drawable="#drawable/button_prev_disabled"
android:state_enabled="false"/>
<item
android:drawable="#drawable/button_prev"/>
</selector>
Oddly, the drawable selector reaches the "pressed" state without issue.
To make this work with a ColorStateList change the background of your ImageButton to ?android:attr/selectableItemBackground or ?android:attr/selectableItemBackgroundBorderless.
For example,
<ImageButton
android:id="#+id/imageButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:attr/selectableItemBackgroundBorderless"
android:src="#drawable/button" />

Android - how to define a configurable size shape

Following suggestions in few other posts, I implemented a circular button to be used in the app:
It is implemented using XML selector:
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- Non focused states
-->
<item android:state_focused="false" android:state_selected="false" android:state_pressed="false"
android:drawable="#drawable/big_ring_button_unfocused" />
<item android:state_focused="false" android:state_selected="true" android:state_pressed="false"
android:drawable="#drawable/big_ring_button_unfocused" />
<!-- Focused states
-->
<item android:state_focused="true" android:state_selected="false" android:state_pressed="false"
android:drawable="#drawable/big_ring_button_focused" />
<item android:state_focused="true" android:state_selected="true" android:state_pressed="false"
android:drawable="#drawable/big_ring_button_focused" />
<!-- Pressed
-->
<item android:state_pressed="true" android:drawable="#drawable/big_ring_button_pressed" />
</selector>
The contents of the ..._unfocused file (the others just change colors):
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="ring"
android:innerRadius="#dimen/big_ring_button_inner_radius"
android:thickness="#dimen/big_ring_button_thickness"
android:useLevel="false">
<solid android:color="#color/white" />
</shape>
I'd like to use this template for all rounded buttons in my app, but since the text inside the buttons change, the size of the button itself should change.
I thought that I might be able to accomplish this programmatically, so I checked the documentation of GradientDrawable - there is no method there to change innerRadius attribute.
Currently I have 4 XML files per each round button (selector, unfocused, focused and pressed) which is extremely ugly and will become a pain in the neck to maintain.
How could I make this button configurable in size (either XML or programmatically)?
Thanks
You cannot change the innerRadius attribute programmatically, as it's only read from XML attributes when inflating (see for example this answer).
However, you could achieve more or less the same effect programmatically, by using a custom drawable to draw the ring. For example:
public class RoundBorderDrawable extends GradientDrawable
{
private static final int RING_THICKNESS = 20;
private static final int PADDING = 8;
private static final int NORMAL_COLOR = Color.BLUE;
private static final int PRESSED_COLOR = Color.RED;
private Paint mPaint;
public RoundBorderDrawable()
{
mPaint = new Paint();
mPaint.setStyle(Style.STROKE);
mPaint.setStrokeWidth(RING_THICKNESS);
mPaint.setColor(NORMAL_COLOR);
}
#Override
public boolean isStateful()
{
return true;
}
#Override
protected boolean onStateChange(int[] stateSet)
{
boolean result = super.onStateChange(stateSet);
int color = NORMAL_COLOR;
for (int i = 0; i < stateSet.length; i++)
{
if (stateSet[i] == android.R.attr.state_pressed)
color = PRESSED_COLOR;
}
if (color != mPaint.getColor())
{
mPaint.setColor(color);
invalidateSelf();
result = true;
}
return result;
}
#Override
public void draw(Canvas canvas)
{
super.draw(canvas);
int diameter = Math.min(canvas.getWidth(), canvas.getHeight()) - RING_THICKNESS - 2 * PADDING;
canvas.drawCircle(canvas.getWidth() / 2, canvas.getHeight() / 2, diameter / 2, mPaint);
}
}
This will draw a circle that fits inside the Button dimensions (I assumed you wanted to draw a circle even for non-square buttons).
Then just set a new instance of RoundBorderDrawable as the background of your button (the properties that are constants in this example could, of course, be supplied via the constructor or with setter methods).
I have a better approach for doing this. Instead of Ring use Oval and give stroke with color white and width #dimen/big_ring_button_thickness
but this doesn't give you circular shape. To achieve circular shape your button should be square, I mean width and height of the button should be same.

Changing Textcolor and Background of a TextView

I am totaly new in Android development.
I try to change the FontColor and the Background of ListViewItem if it`s selected or pressed.
Changing the Background is absolutely no Problem by using following 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/background_pressed"></item>
<item android:state_focused="true"
android:drawable="#drawable/background_pressed"></item>
<item android:state_selected="true"
android:drawable="#drawable/background_pressed"></item>
<item android:drawable="#android:color/transparent"></item>
</selector>
But how can I change the Background and the FontColor?
Changing the TextView's font color is done in the same way as the listitem's backgroundcolor: create a StateListDrawable and set the TextView's android:textColor to your custom drawable.
For an example, see Change ListViews text color on click. Combine this with what you already have for the listitem's background.
If you want the colors to fade into eachother, set (e.g.) android:exitFadeDuration="#android:integer/config_mediumAnimTime" in the <selector>-tag.
Try this
#Override
public void onCreate(Bundle savedInstanceState) {
TextView txtName;
txtName.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
TextView textView = (TextView) v;
textView.setBackgroundColor(Color.RED);
textView.setTextColor(Color.YELLOW);
}
});
}

button textcolor not changed

button3.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
button3.setBackgroundResource(R.color.buttonDarkSelected);
button4.setBackgroundResource(R.color.buttonMediumUnselected);
button3.setTextColor(R.color.yellow);
}
});
button4.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
// TODO Auto-generated method stub
button4.setBackgroundResource(R.color.buttonDarkSelected);
button3.setBackgroundResource(R.color.buttonMediumUnselected);
button4.setTextColor(R.color.yellow);
}
});
}
I am trying to change Button's background and textcolor clicking in that buttons. the background is fine working fine but textcolor always black (instead I want it be yellow) what's my problem here.
Try this:
button4.setTextColor(getApplication().getResources().getColor(R.color.yellow));
This will work to change the text color to yellow
You can see this :
http://developer.android.com/reference/android/graphics/drawable/StateListDrawable.html
With state list, you can choose differents color for button with differents state.
regards,
define a selector like this:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:state_pressed="false" android:color="#ffffff" />
<item android:state_focused="true" android:state_pressed="true" android:color="#000000" />
<item android:state_focused="false" android:state_pressed="true" android:color="#000000" />
<item android:color="#ffffff" />
</selector>
save this as xml file (for example btn_text_color.xml)in your ../res/drawable/
Link it to your button like:
android:textColor="#drawable/btn_text_color"
For more details on the subject, see the link that #Kentino posted.

Button states with Background as AnimationDrawable in Android

I have made custom buttons in Android for a while. Things were simple, just made image resources for button states and made a selector for it. Everything went smooth and nice. Now I encountered a new situation.
I have made a animation drawable and set it as a background for my button.
<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
<item android:drawable="#drawable/frame1" android:duration="600" />
<item android:drawable="#drawable/frame2" android:duration="300" />
<item android:drawable="#drawable/frame3" android:duration="500" />
</animation-list>
If I set the animation as button's Background it works fine. If I try to make a simple selector
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="false"
android:drawable="#drawable/animation" />
<item
android:state_pressed="true"
android:drawable="#drawable/pressed" />
</selector>
where normal state of the button would have animation as background and the pressed state a static image, things don't work right.
On my main activity, on onWindowFocus I get the button background and start the animation
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
btn = (Button)findViewById(R.id.btnAnim);
btnAnimation = (AnimationDrawable) btnAnim.getBackground();
btnAnimation.start();
}
Here appears to be the problem, because my animation won't be taken correctly from the selector and I get the following error:
03-14 15:21:16.146: ERROR/AndroidRuntime(440): FATAL EXCEPTION: main
03-14 15:21:16.146: ERROR/AndroidRuntime(440): java.lang.ClassCastException: android.graphics.drawable.StateListDrawable
03-14 15:21:16.146: ERROR/AndroidRuntime(440): at com.bebenjoy.MainActivity.onWindowFocusChanged(MainActivity.java:53)
03-14 15:21:16.146: ERROR/AndroidRuntime(440): at ...
Any idea on how to fix this ? Thanks.
You're doing incorrect cast -- your background drawable is StateListDrawable, not AnimationDrawable. I'd rather do something like:
#Override
public void onWindowFocusChanged(boolean hasFocus) {
super.onWindowFocusChanged(hasFocus);
btn = (Button)findViewById(R.id.btnAnim);
StateListDrawable background = (StateListDrawable) btn.getBackground();
Drawable current = background.getCurrent();
if (current instanceof AnimationDrawable) {
btnAnimation = (AnimationDrawable) current;
btnAnimation.start();
}
}
My answer is a bit late, I know, but I faced the same issue. I checked a lot of solutions, but found only one.
I have tried to start the animation in onWindowFocusChanged(), start the animation in aseparate thread, but it doesn't help.
I solved this issue using setVisible (boolean visible, boolean restart)
So you can try this:
private Button ImgBtn;
private AnimationDrawable btnAnimation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
btn = (Button)findViewById(R.id.button1);
StateListDrawable background = (StateListDrawable) btn.getBackground();
btnAnimation = (AnimationDrawable) background.getCurrent();
btnAnimation.setVisible(true, true); // it works even in onCreate()
}
Hope this will help somebody :)

Categories

Resources