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" />
Related
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
}
});
...
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 used Android Studio to implement the Navigation Drawer and I can't get the blue colour that is used to show which section we're currently in to change.
I've tried numerous things, I'm currently using a listSelector which looks like:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_activated="true" android:drawable="#color/selected" />
<item android:state_pressed="true" android:drawable="#color/highlight" />
</selector>
I've also tried state_checked. state_pressed works in this situation but the currently selected item is still blue.
EDIT:
I've been examining this more and when the adapter is created the context that is passed is getActionBar().getThemedContext() so I'm thinking if I can find the right attribute to assign to my actionbar style I can change it from there. I've tried a few different attributes with no luck. Does anyone know the exact attribute?
I've also realised if I put
<item name="android:activatedBackgroundIndicator">#drawable/nav_listview_selector</item>
in the main part of my theme and change getActionBar().getThemedContext() for getActivity.getBaseContext then I can change the color but I don't think this is the correct way. I think the themed context should be used. So if anyone knows where the activatedBackgroundIndicator could be put so that it would be used in getActionBar.getThemedContext()
EDIT2:
So the text view used for the listview is one within the SDK it looks like this:
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#android:id/text1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceListItemSmall"
android:gravity="center_vertical"
android:paddingStart="?android:attr/listPreferredItemPaddingStart"
android:paddingEnd="?android:attr/listPreferredItemPaddingEnd"
android:background="?android:attr/activatedBackgroundIndicator"
android:minHeight="?android:attr/listPreferredItemHeightSmall"
/>
So I tried modifying the "?android:attr/activatedBackgroundIndicator" at the theme level but it has no effect for checked/selected/activated but it does for pressed. Does anyone know why this is? And how I can change it?
To solve this problem:
1- You don't need android:listSelector under your ListView.
2- Open (or Create) styles.xml under (res/values).
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<!-- Customize your theme here. -->
<item name="android:activatedBackgroundIndicator">#drawable/drawer_list_selector</item>
</style>
3- Under res/drawable folder create drawer_list_selector.xml file
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="#drawable/light_gray_color" />
<item android:state_activated="true" android:drawable="#drawable/red_color" />
<item android:drawable="#android:color/transparent" />
</selector>
4- Under res/drawable create red_color.xml / light_gray_color.xml (or any other name) and add your desired Hex color:
<?xml version="1.0" encoding="UTF-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#C8FF0000"/>
</shape>
5- Open your project AndroidManifest.xml and add android:theme tag (if not exist)
<application
android:allowBackup="true"
android:icon="#drawable/ic_launcher"
android:label="#string/app_name"
android:theme="#style/AppTheme" >
Reference / Credit: Changing Navigation Drawer Selected Item Color from default blue
To change the "Navigation Drawer item background colour for selected item" you could do it with the attribut:
colorControlHighlight
In your "styles.xml" it could look like this:
<style name="YourStyleNameFor.NavigationDrawer" parent="ThemeOverlay.AppCompat.Light">
<item name="colorControlHighlight">#color/your_highlight_color</item>
</style>
Don't forget to apply your Style in the tag:
android.support.design.widget.NavigationView
For example in your activity_main.xml it could look like this:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true"
app:headerLayout="#layout/navigationdrawer_header"
<!-- the following line referes to your style -->
app:theme="#style/YourThemeNameFor.NavigationDrawer"
<!-- the following two lines are maybe also interesting for you -->
app:itemIconTint="#color/gray3"
app:itemTextColor="#color/gray3"
app:menu="#menu/navigationdrawer_main" />
This is working for me:
First define a drawable item_bg.xml as:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_checked="true" android:drawable="#drawable/nav_menu_bg" />
</selector>
Then use this drawable in navigation_main_layout.xml as:
<android.support.design.widget.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
app:itemBackground="#drawable/item_bg"
android:fitsSystemWindows="true"
app:headerLayout="#layout/nav_header_navigation"
app:menu="#menu/navigation_main_layout_drawer" />
here is how i have done and it is working, the brief concept is maintain the position of selected item in adapter and call notifyDataSetChanged on calling notifyDatasetChanged the getView method is called again and in get view check the position on the selected position change the background view. Here is the code :
Adapter of NavigationDrawer List View
public class MenuAdapter extends BaseAdapter {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private Context mContext;
private String name;
private int profile;
private int mIcons[];
private int selectedPosition = 0;
private String mNavTitles[];
private LayoutInflater mInflater;
public MenuAdapter(String titles[], int icon[], String Name, int profile) {
mNavTitles = titles;
mIcons = icon;
name = Name;
this.profile = profile;
}
public MenuAdapter(String Titles[], int Icons[], Context mContext) {
mNavTitles = Titles;
mIcons = Icons;
this.mContext = mContext;
mInflater = (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
#Override
public int getCount() {
return mNavTitles.length;
}
#Override
public Object getItem(int position) {
return position;
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
convertView = mInflater.inflate(R.layout.item_row, parent, false);
TextView textView = (TextView) convertView.findViewById(R.id.rowText);
ImageView imageView = (ImageView) convertView.findViewById(R.id.rowIcon);
final LinearLayout layout = (LinearLayout) convertView.findViewById(R.id.outerLayout);
imageView.setImageResource(mIcons[position]);
textView.setText(mNavTitles[position]);
if (position == selectedPosition)
layout.setBackgroundColor(mContext.getResources().getColor(R.color.app_bg));
else
layout.setBackgroundColor(mContext.getResources().getColor(R.color.normal_bg));
return convertView;
}
public void setSelectedPosition(int position) {
this.selectedPosition = position;
}
}
in your activity do this
private class DrawerItemClickListener implements ListView.OnItemClickListener {
#Override
public void onItemClick(AdapterView parent, View view, int position, long id) {
mMenuAdapter.setSelectedPosition(position - 1);
mMenuAdapter.notifyDataSetChanged();
}
}
if anyone still face any difficulty, feel free to ask.
I have implement drawer menu with custom adapter class. May be it will help someone Drawer List
<ListView
android:id="#+id/listview_drawer"
android:layout_width="260dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:background="#color/menu_item_color"
android:choiceMode="singleChoice"
android:divider="#android:color/transparent"
android:dividerHeight="0dp"
android:fadingEdge="none"
/>
drawer_list_item.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/menu_selector"
>
<ImageView
android:id="#+id/imgIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="12dp"
android:layout_marginRight="12dp"
android:src="#drawable/ic_menu_home" />
<TextView
android:id="#+id/lblName"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:layout_toRightOf="#+id/imgIcon"
android:minHeight="48dp"
android:textColor="#color/menu_txt_color" />
menu_selector.xml
<selector xmlns:android="http://schemas.android.com/apk/res/android" android:exitFadeDuration="#android:integer/config_mediumAnimTime">
<!-- selected -->
<item android:drawable="#color/menu_item_active_color" android:state_focused="true" android:state_pressed="false"/>
<item android:drawable="#color/menu_item_active_color" android:state_pressed="true"/>
<item android:drawable="#color/menu_item_active_color" android:state_activated="true"/>
<item android:drawable="#color/menu_item_active_color" android:state_checked="true"/>
<item android:drawable="#color/menu_item_active_color" android:state_selected="true"/>
<item android:drawable="#color/menu_item_color" android:state_activated="false"/>
Add this on item click listner of listview
yourlistview.setItemChecked(position, true);
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/list_item_back_pressed" android:state_pressed="true" />
<item android:state_activated="true"><color android:color="#color/primary_blue"/></item>
<item android:drawable="#color/list_item_back_normal"/>
</selector>
In addition to providing a custom selector drawable for the listSelector, you should also set the background resource of the list item to a similar selector drawable that has different drawables for the different states.
I usually use my custom adapter that has an int selection field, and a setSelection(int) function. And in the getView function I set the background of the view according to position == selection.
Still not sure why it is that it doesn't work. But the way I found around it is to use my own simple_list_item_activated layout to be passed to the ArrayAdapter which was basically the same except for setting the text colour to white. I then replaced getActionBar().getThemedContext() with getActivity().getBaseContext() and it now has an effect.
This may not be the correct way and may have repercussions in the future, but for now I have it working the way I want it to.
I know its too late but I have solved this issue in my app.
Pls dont think it is silly, just simply change the position of "state_pressed" to top.
<item android:drawable="#drawable/list_item_bg_pressed" android:state_pressed="true"/>
<item android:drawable="#drawable/list_item_bg_normal" android:state_activated="false"/>
<item android:drawable="#drawable/list_item_bg_selected" android:state_activated="true"/>
In Future if anyone comes here using, Navigation Drawer Activity (provided by Studio in Activity Prompt window)
The answer is -
Use this before OnCreate() in MainActivity
int[][] state = new int[][] {
new int[] {android.R.attr.state_checked}, // checked
new int[] {-android.R.attr.state_checked}
};
int[] color = new int[] {
Color.rgb(255,46,84),
(Color.BLACK)
};
ColorStateList csl = new ColorStateList(state, color);
int[][] state2 = new int[][] {
new int[] {android.R.attr.state_checked}, // checked
new int[] {-android.R.attr.state_checked}
};
int[] color2 = new int[] {
Color.rgb(255,46,84),
(Color.GRAY)
};
ColorStateList csl2 = new ColorStateList(state2, color2);
and use this in onNavigationItemSelected() in MainActivity (you dont need to Write this function if you use Navigation Drawer activity, it will be added in MainActivity).
NavigationView nav = (NavigationView) findViewById(R.id.nav_view);
nav.setItemTextColor(csl);
nav.setItemIconTintList(csl2);
nav.setItemBackgroundResource(R.color.white);
Tip - add this code before If else Condition in onNavigationItemSelected()
This worked for me :
implemented menu drawer not by populating the navigation view with list, but with menu items.
created a drawable like this :
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#color/drawer_menu_selector_color" android:state_checked="true"></item>
<item android:drawable="#color/drawer_menu_selector_color" android:state_activated="true"></item>
and in onCreate method , set the id of the menu item which corresponds to selected activity as checked.
and implement the drawable selector file to your navigation drawer like this
app:itemBackground="#drawable/drawer_menu_selector"
fyi : need to define your 'app' namespace.
I searched for solution on this issue, tried everything, and only this solution worked for me.
You can find it on this link https://tuchangwei.github.io/2016/07/18/The-solution-that-the-menu-item-of-Navigation-View-can-t-change-its-background-When-it-is-selected-checked/
All the credit to https://tuchangwei.github.io/
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 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.