I'm trying to figure out how to add an outer glow to a TextView when it's touched. The approach I'm working with is to use a Selector, but it doesn't seem to be working.
I've got the following structure
layout\HomeView.axml
<TextView
android:id="#+id/textview1"
android:clickable="true"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
style="#drawable/control_selector_state" />
drawable\control_selector_state.xml
<!-- yes these are all the same for testing purposes -->
<?xml version="1.0" encoding="utf-8" ?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true"
style="#style/control_style_focused"/>
<item android:state_focused="true"
android:state_pressed="true"
style="#style/control_style_focused" />
<item android:state_pressed="true"
style="#style/control_style_focused" />
<item style="#style/control_style_focused" />
</selector>
values\styles.xml
<resources>
<style name="control_style_focused">
<item name="android:shadowColor">#0000ff</item>
<item name="android:textColor">#ff0000</item>
<item name="android:shadowDx">0.0</item>
<item name="android:shadowRadius">8</item>
</style>
</resources>
The problem I'm having is that my TextView text is white, and the style doesn't seem to be applying.
How do I get this style to apply to my TextView?
so as #Longwayto said, selector styles are only available for drawables. That doesn't mean it's impossible.
Here's a working approach.
First you create a custom TextView that extends TextView
public class MyTextView: TextView
{
private readonly Context _context;
public FontIconTextView(Context context, IAttributeSet attrs) : base(context)
{
_context = context;
Initialize(attrs);
}
private void Initialize(IAttributeSet attrs)
{
var a = _context.Theme.ObtainStyledAttributes(attrs, Resource.Styleable.MyTextView, 0, 0);
_touchGlowColor = a.GetString(Resource.Styleable.MyTextView_TouchGlowColor);
_touchGlowSize = a.GetInteger(Resource.Styleable.MyTextView_TouchGlowSize, 0);
Initialize();
}
private void Initialize()
{
// other initialize stuff happens here...
}
private int _touchGlowSize;
private string _touchGlowColor;
public override bool OnTouchEvent(MotionEvent motionEvent)
{
if (Enabled)
{
var color = string.IsNullOrEmpty(_touchGlowColor) ? new Color(255, 255, 255, 255) : Color.ParseColor(_touchGlowColor);
switch (motionEvent.Action)
{
case MotionEventActions.Down:
SetShadowLayer(_touchGlowSize, 0, 0, color);
break;
case MotionEventActions.Up:
case MotionEventActions.Cancel:
SetShadowLayer(0, 0, 0, new Color(255, 255, 255, 255));
break;
}
}
return base.OnTouchEvent(motionEvent);
}
}
then, you have to go into your values directory and specify your custom attributes.
Resources\values\CustomBindingAttributes.xml
<?xml version="1.0" encoding="utf-8" ?>
<resources>
<declare-styleable name="MyTextView">
<attr name="TouchGlowColor" format="string" />
<attr name="TouchGlowSize" format="integer" />
</declare-styleable>
</resources>
All of the above will be reusable across your entire application... no more duplicating shit on every view.
Lastly, here's how your button will look.
<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="match_parent">
<!-- obviously the foo/bar is just to show that you add android attrs like normal -->
<some.name.space.MyTextView
android:foo="foo"
amdroid:bar="bar"
local:TouchGlowColor="#66e400"
local:TouchGlowSize="20" />
</LinearLayout>
one gotcha I ran into is setting TouchGlowSize to 30 caused the app to crash. not sure why atm
Related
I have a custom class McEditText that merges a constraint layout with a background xml drawable colored stroke.
(There's more to McEditText than Im showing here. The ellipses are the excluded code. So the problem is not the merge)
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
...
<androidx.constraintlayout.widget.ConstraintLayout
android:id="#+id/edit_border"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/mc_edit_text_border"
android:paddingTop="3dp"
android:paddingBottom="3dp"
android:paddingStart="8dp"
android:paddingEnd="8dp"
app:layout_constraintTop_toBottomOf="#id/edit_label"
app:layout_constraintStart_toStartOf="parent">
...
</androidx.constraintlayout.widget.ConstraintLayout>
...
</merge>
mc_edit_text_border.xml in my drawables folder
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#android:color/transparent" />
<stroke
android:width="2dp"
android:color="#drawable/mc_edit_text_bordercolor" />
</shape>
mc_edit_text_bordercolor.xml in my drawables folder
<?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 android:color="#color/InputDisabledBorderColor" android:state_enabled="false"/>
<item android:color="#color/InputReadonlyBorderColor" app:state_readonly="true"/>
<item android:color="#color/InputErrorBorderColor" app:state_error="true" android:state_selected="false"/>
<item android:color="#color/InputErrorActiveBorderColor" app:state_error="true" android:state_selected="true"/>
<item android:color="#color/InputSuccessBorderColor" app:state_success="true" android:state_selected="false"/>
<item android:color="#color/InputSuccessActiveBorderColor" app:state_success="true" android:state_selected="true"/>
<item android:color="#color/InputActiveBorderColor" android:state_selected="true"/>
<item android:color="#color/InputBorderColor" android:state_selected="false"/>
</selector>
So far its all pretty standard stuff. The complexity is that I've added a few extra states, which you can see in the above selector xml: state_error, state_readonly and state_success. I read about how to do that here.
So in my attrs.xml...
<resources>
<attr name="state_error" format="boolean"/>
<attr name="state_success" format="boolean"/>
<attr name="state_readonly" format="boolean"/>
<declare-styleable name="McEditText">
<attr name="state_error"/>
<attr name="state_success"/>
<attr name="state_readonly"/>
<attr name="editTextKind" format="enum">
<enum name="normal" value="0" />
<enum name="error" value="1" />
<enum name="success" value="2" />
<enum name="readonly" value="3" />
<enum name="disabled" value="4" />
</attr>
</declare-styleable>
</resources>
Then in my McEditText.kt I do this...
class McEditText #JvmOverloads constructor(
context: Context,
attrs: AttributeSet
) : ConstraintLayout(context, attrs) {
private val stateError = intArrayOf(R.attr.state_error)
private val stateSuccess = intArrayOf(R.attr.state_success)
private val stateReadOnly = intArrayOf(R.attr.state_readonly)
enum class Kind {
NORMAL, ERROR, SUCCESS, READONLY, DISABLED
}
init {
context.theme.obtainStyledAttributes(attrs, R.styleable.McEditText, 0, 0).apply {
try {
kind = Kind.values()[getInteger(R.styleable.McEditText_editTextKind, Kind.NORMAL.ordinal)]
} finally {
recycle()
}
}
_layout = inflate(context, R.layout.layout_mcedittext, this#McEditText) as ConstraintLayout
_editBorder = _layout.findViewById(R.id.edit_border)
if (kind == Kind.DISABLED) {
_editBorder.isEnabled = false
} else {
_editBorder.isEnabled = true
}
redraw()
}
private fun redraw() {
refreshDrawableState()
invalidate()
requestLayout()
}
override fun onCreateDrawableState(extraSpace: Int): IntArray {
var drawableState = super.onCreateDrawableState(extraSpace + 2)
when (kind) {
Kind.ERROR -> View.mergeDrawableStates(drawableState, stateError)
Kind.SUCCESS -> View.mergeDrawableStates(drawableState, stateSuccess)
Kind.READONLY -> View.mergeDrawableStates(drawableState, stateReadOnly)
else -> {drawableState = super.onCreateDrawableState(extraSpace)}
}
return drawableState
}
}
Then finally I create a couple instances of the control in my fragment.xml
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<com.mcafee.vision.ui.components.McEditText
android:id="#+id/as_disabled"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:label="Disabled"
custom:editTextKind="disabled"
/>
<View
android:layout_width="match_parent"
android:layout_height="10dp"/>
<com.mcafee.vision.ui.components.McEditText
android:id="#+id/as_error"
android:layout_width="match_parent"
android:layout_height="wrap_content"
custom:label="Error"
custom:editTextKind="error"
/>
</LinearLayout>
The problem is that it doesnt work. When I make an instance of this control the border color never changes for my custom states (though it does work for the built in states like enabled=false). Im not sure how to set the custom states beyond merging them in the onCreateDrawableState() override. This article details how to do this but it doesn't appear to work. Am I missing something?
I figured it out. I needed to add android:duplicateParentState="true" to all of the views in my mcedittext.xml layout so that the states propogate down to the children.
This is the xml of "mydrawable" which I'm using as background for buttons
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="false" android:state_pressed="false" >
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="-45"
android:endColor="#color/colorPrimary700"
android:startColor="#color/colorPrimary600"
android:type="linear" />
<corners android:radius="#dimen/ic_button_corner"></corners>
</shape>
</item>
<item android:state_focused="false" android:state_pressed="true" >
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:angle="-45"
android:endColor="#color/colorPrimary800"
android:startColor="#color/colorPrimary700"
android:type="linear" />
<corners android:radius="#dimen/ic_button_corner"></corners>
</shape>
</item>
</selector>
This is a use case
<Button
android:id="#+id/login_button"
style="?android:textAppearanceSmall"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/mydrawable"
android:text="#string/log_in"/>
How to port this static drawable into a custom view with configurable parameters like the colors used in the gradients for each item and the size corner radius?
For example via Java
MyDrawable myDrawable=new MyDrawable();
myDrawable.setGradientColors(color1, color2);
myDrawable.setCornerRadius(size);
button.setBackground(Drawable);
Is it also possible via custom Button (MyButton, instead of MyDrawable)?
<MyButton
parameter_gradientcolor1:#color/color1
parameter_gradientcolor2:#color/color2
... />
EDIT
This is not working, neither reactions to click event nor correct gradient
public class SelectorButton extends AppCompatButton {
StateListDrawable mStateListDrawable;
public SelectorButton(Context context, AttributeSet attrs) {
super(context, attrs);
float cornerRadius = attrs.getAttributeFloatValue("app", "cornerRadius", 0);
int normalStartColor = attrs.getAttributeIntValue("app", "normalStartColor", R.color.mds_grey_400);
int normalEndColor = attrs.getAttributeIntValue("app", "normalEndColor", R.color.mds_grey_500);
int pressedStartColor = attrs.getAttributeIntValue("app", "pressedStartColor", R.color.mds_grey_400);
int pressedEndColor = attrs.getAttributeIntValue("app", "pressedEndColor", R.color.mds_grey_500);
GradientDrawable normalDrawable = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{normalStartColor, normalEndColor});
normalDrawable.setCornerRadius(cornerRadius);
GradientDrawable pressedDrawable = new GradientDrawable(
GradientDrawable.Orientation.TOP_BOTTOM,
new int[]{pressedStartColor, pressedEndColor});
pressedDrawable.setCornerRadius(cornerRadius);
mStateListDrawable = new StateListDrawable();
mStateListDrawable.addState(new int[]{-android.R.attr.state_pressed, -android.R.attr.state_focused},
normalDrawable);
mStateListDrawable.addState(new int[]{android.R.attr.state_pressed, -android.R.attr.state_focused},
pressedDrawable);
setBackground(mStateListDrawable);
}
}
This is in the layout
<com.utils.views.SelectorButton
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Login"
android:clickable="true"
app:normalEndColor="#color/mds_blue_400"
app:normalStartColor="#color/mds_red_500"
app:pressedEndColor="#color/mds_amber_500"
app:pressedStartColor="#color/mds_green_300" />
Yes, you can do this via custom button. Here is a code snippet.
public class SelectorButton extends AppCompatButton {
StateListDrawable mStateListDrawable;
public SelectorButton(Context context, AttributeSet attrs) {
super(context, attrs);
mStateListDrawable = new StateListDrawable();
GradientDrawable normalDrawable = new GradientDrawable(yourColor);
normalDrawable.setCornerRadius(yourRadius);
mStateListDrawable.addState(
new int[]{-android.R.attr.state_pressed, -android.R.attr.state_enabled}, );
setBackground(mStateListDrawable);
}
}
In order to set style via XML, you can define custom style such as colors or corner radius in attrs.xml.If you have any question, feel free to ask.
EDIT
Now I will show you how to declare custom style in XMLand use them. For example, I want to set normal and pressed state gradient color.
In yourProject/app/src/main/res/values dir, create a new file called attrs.xml.
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="SelectorButton">
<attr name="normalStartColor" format="color"/>
<attr name="normalEndColor" format="color"/>
<attr name="pressedStartColor" format="color"/>
<attr name="pressedEndColor" format="color"/>
</declare-styleable>
</resources>
As you see, I define four attributes.Now you can set these attributes via xml.
<SelectorButton
app:normalStartColor=""
app:normalEndColor=""
app:pressedStartColor=""
app:pressedEndColor=""
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
EDIT: obtain values from xml
Sorry for my mistakes. You can obtain these values like this.
final TypedArray a = context.obtainStyledAttributes(
attrs, R.styleable.SelectorButton, 0, 0);
int normalStartColor = a.getColor(R.styleable.SelectorButton_normalStartColor, 0);
a.recycle();
And there is a pressed state. You can do like this.
mStateListDrawable.addState(new int[]{android.R.attr.state_pressed, -android.R.attr.state_enabled}, pressedDrawable);
Drawable in xml is just a drawable, so more like an image, you can not style it like that.
However, you can add custom style to your custom button MyButton.
That can be learned from over here https://developer.android.com/training/custom-views/create-view.html#customattr
When creating a ColorStateList for a share button, I first used android:drawable to specify an item color like so (by accident)
<item android:state_pressed="true" android:drawable="#color/stock_orange"/>
instead of android:color (like "normal")
<item android:state_pressed="true" android:color="#color/stock_orange"/>
Though there wasn't a crash and a color change occurred when pressed, it was the wrong color (magenta instead of the specified orange).
Is there an obvious explanation for this? Can/should drawables be used in a color state list?
Resource/Color/share_btn_color_state_list.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#A0A0A0"/>
<!--QUESTION: works, but color is magenta, not orange -->
<!--item android:state_pressed="true" android:drawable="#color/stock_orange"/-->
<item android:state_pressed="true" android:color="#color/stock_orange"/>
<item android:color="#color/black"/>
</selector>
Resource/Layout/share_view.xml
<ImageView
android:id="#+id/share_btn_iv"
android:layout_width="40dp"
android:layout_height="40dp"
android:background="#drawable/ic_share_black_24dp"/>
Usage
_shareButton = FindViewById<ImageView>(Resource.Id.share_btn_iv);
_shareButton.Enabled = true;
_shareButton.Drawable.SetTintList(
ColorStateList.CreateFromXml(
Resources,
Resources.GetXml(Resource.Color.share_btn_color_state_list)
)
);
drawable in a ColorStateList
One way is to customize an ImageView and use a combination of a ColorFilter and a ColorStateList that contains your tint color for when the button is pressed.
Extend ImageView and wrap DrawableStateChanged() with code that sets the tint based on the new state :
public class TintableImageView : ImageView
{
private ColorStateList tint;
public TintableImageView(Context context) : base(context)
{
}
public TintableImageView(Context context, IAttributeSet attrs):base(context,attrs)
{
init(context, attrs, 0);
}
public TintableImageView(Context context, IAttributeSet attrs, int defStyle):base(context,attrs,defStyle)
{
init(context, attrs, defStyle);
}
private void init(Context context, IAttributeSet attrs, int defStyle)
{
TypedArray a = context.ObtainStyledAttributes(attrs, Resource.Styleable.TintableImageView, defStyle, 0);
tint = a.GetColorStateList(Resource.Styleable.TintableImageView_tint);
a.Recycle();
}
protected override void DrawableStateChanged()
{
base.DrawableStateChanged();
if(tint != null && tint.IsStateful)
{
UpdateTintColor();
}
}
public void SetColorFilter(ColorStateList tint)
{
this.tint = tint;
base.SetColorFilter(new Color(tint.GetColorForState(GetDrawableState(), new Color(0))));
}
private void UpdateTintColor()
{
var color = new Color(tint.GetColorForState(GetDrawableState(), new Color(0)));
SetColorFilter(color);
}
}
Define a custom attribute :
attrs.xml
<?xml version="1.0" encoding="UTF-8"?>
<resources>
<declare-styleable name="TintableImageView">
<attr name="tint" format="reference|color" />
</declare-styleable>
</resources>
Color selector like this : Resource\Color\color_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:color="#color/colorAccent"/>
<item android:color="#00000000"/>
</selector>
When use this custom ImageView ;
<?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-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageDemo.TintableImageView
android:id="#+id/myImageView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/download"
app:tint="#color/color_selector"
android:clickable="true"/>
</LinearLayout>
EDIT :
For example, add a translucent color as the color effect :
Resource\Color\color_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:color="#55000000"/>
<item android:color="#00000000"/>
</selector>
Effect :
Usually the color state list are used to change the color of any ui element for different states For example, a Button widget can exist in one of several different states (pressed, focused, or neither) and, using a color state list, you can provide a different color during each state.
And a StateListDrawable is a drawable object defined in XML that uses a several different images to represent the same graphic, depending on the state of the object. For example, a Button widget can exist in one of several different states (pressed, focused, or neither) and, using a state list drawable, you can provide a different background image for each state.
Though what you did worked but it is not a recommended approach
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" />
I have an ImageButton in my app and I need to change the tint of the image when the button is pressed/focused. I have the ImageButton set to get its src from an XML file which as follows:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- pressed -->
<item
android:state_pressed="true"
android:tint="#color/black"
android:drawable="#drawable/search"
/>
<!-- focused -->
<item
android:state_focused="true"
android:tint="#color/black"
android:drawable="#drawable/search"
/>
<!-- default -->
<item
android:tint="#null"
android:drawable="#drawable/search"
/>
</selector>
However the tint isn't applied when the ImageButton is pressed or focused - the image just displays as normal. The color black is defined as #000000 as always. Any ideas?
You can change the tint, quite easily in code via:
ImageButton button = (ImageButton) this.findViewById(R.id.button_i_want_to_modify);
button.setColorFilter(Color.argb(255, 255, 255, 255)); // White Tint
Here is how to do it using just xml.
In your drawable folder create a selector. Eg: touch_selector.xml
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<!-- State when a row is being pressed, but hasn't yet been activated (finger down) -->
<item android:state_pressed="true" android:color="#color/semi_slate" />
<!-- When the view is "activated". In SINGLE_CHOICE_MODE, it flags the active row
of a ListView -->
<item android:state_activated="true" android:color="#color/semi_slate" />
<!-- Default, "just hangin' out" state. -->
<item android:color="#android:color/transparent" />
</selector>
In my Image view in xml I set the android:tint attribute to the drawable created above.
android:tint = "#drawable/touch_selector"
The whole code looked like this:
<?xml version="1.0" encoding="utf-8"?>
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/poster"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:adjustViewBounds="true"
android:scaleType="centerCrop"
android:tint="#drawable/touch_selector" />
This is an all xml solution, to put tint on an ImageView on press or on active.
Similar can be done for ImageButton
Note that this only works for API level >= 21.
Finally I have found a solution for API < 21:
Button more = (Button) findViewById(R.id.more);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
more.getBackground().setColorFilter(color, PorterDuff.Mode.SRC_IN);
} else {
Drawable wrapDrawable = DrawableCompat.wrap(more.getBackground());
DrawableCompat.setTint(wrapDrawable, color));
more.setBackgroundDrawable(DrawableCompat.unwrap(wrapDrawable));
}
May this help someone not to lose 2 hours !
I found a way to do this in xml (in api 21 and up at least).
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<bitmap
android:src="#drawable/search"
android:tint="#color/black"
/>
</item>
<item android:drawable="#drawable/search"/>
</selector>
By setting the tint on the bitmap it's possible to reuse the same drawable in xml without having to intercept touches or subclass ImageView or ImageButton.
Once the selector has been created, just apply that as the src of the ImageView or ImageButton.
bt.setOnTouchListener(new View.OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
bt.setColorFilter(Color.argb(255, 255, 255, 255)); // White Tint
return true; // if you want to handle the touch event
case MotionEvent.ACTION_UP:
bt.clearColorFilter(); // White Tint
return true; // if you want to handle the touch event
}
return false;
}
});
What I'm doing is add a custom button that has the function setColorFilter.
Like this I can use the new button in the xml.
public class CustomButton extends Button {
public CustomButton(Context context) {
super(context);
}
public CustomButton(Context context, AttributeSet attributes) {
super(context, attributes);
};
#Override
public boolean onTouchEvent(MotionEvent event) {
int maskedAction = event.getActionMasked();
if (maskedAction == MotionEvent.ACTION_DOWN)
getBackground().setColorFilter(Color.argb(150, 155, 155, 155), PorterDuff.Mode.DST_IN);
else if (maskedAction == MotionEvent.ACTION_UP)
getBackground().setColorFilter(null);
return super.onTouchEvent(event);
}}
and for the ImageButton
public class CustomImageButton extends ImageButton {
public CustomImageButton(Context context) {
super(context);
}
public CustomImageButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
int maskedAction = event.getActionMasked();
if (maskedAction == MotionEvent.ACTION_DOWN)
setColorFilter(Color.argb(150, 155, 155, 155), PorterDuff.Mode.DST_IN);
else if (maskedAction == MotionEvent.ACTION_UP)
setColorFilter(null);
return super.onTouchEvent(event);
}}
I noticed there are some requests here for people wanting to know how to do this in XML. It is actually quite simple. This can be accomplished using a layer-list
Your button's drawable (drawable/some_button.xml):
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/some_button_highlighted" />
<item android:drawable="#drawable/some_button_image" />
</selector>
And this is the highlighted drawable (drawable/some_button_highlighted.xml)
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/some_button_image"/>
<item>
<shape>
<solid android:color="#color/highlighted_button_color" />
</shape>
</item>
</layer-list>
Now you can use this in any other xml:
...
android:drawable="#drawable/some_button"
...
I hope this helps someone in the future.
You can set color (tint) from xml.
Set transparent (android:background="#null") for background then use tint :
<ImageButton
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:tint="#color/Amber_200"
android:background="#null"
android:src="#drawable/back_selector" />
I have the same issue and I found this solution :
logoImage.setOnTouchListener { v, event ->
if (event.action == MotionEvent.ACTION_DOWN) {
if (logoImage.isSelected) {
logoImage.setColorFilter(context.getColor(R.color.blue_unselect), PorterDuff.Mode.SRC_IN)
} else {
logoImage.setColorFilter(null)
}
} else if (event.action == MotionEvent.ACTION_UP) {
if (!logoImage.isSelected) {
logoImage.setColorFilter(null)
} else {
logoImage.setColorFilter(context.getColor(R.color.blue_unselect), PorterDuff.Mode.SRC_IN)
}
} else if (event.action == MotionEvent.ACTION_CANCEL) {
if (root.isSelected) {
logoImage.setColorFilter(null)
} else {
logoImage.setColorFilter(context.getColor(R.color.blue_unselect), PorterDuff.Mode.SRC_IN)
}
}
false
}
it's working for me.
As you defined the selector to be the src of the ImageButton Android will AFAIK just take the drawable because that's what matches the type of src. So tint won't be used.
Nevertheless, I had a similar problem: I also tried to use a selector like yours but for the android:tint value of the ImageButton instead of android:src. Of course I omitted the tint values you have in your selector. This would solve your problem, too, because you want to use the same drawable for all states. Curiously I get a NumberFormatException everytime stating that the system was unable to parse 'res/color/tint_selector.xml' (which is indeed my selector) as integer. To be specific my code looks like this:
This is my selector, saved in /res/color/tint_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:color="#D3D3D3"/> <!-- pressed -->
<item android:color="#ff000000"/> <!-- default -->
</selector>
And this is the corresponding ImageButton:
<ImageButton android:id="#+id/program_help"
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:src="#drawable/symbol"
android:tint="#color/tint_selector">
</ImageButton>
Maybe this helps you a bit although it currently doesn't work.