What I want to do is change the background color (set custom drawable) of a popup error message displayed after using setError() method.
Currently, it looks like this:
I've found that Android has two files:
popup_inline_error.9.png
popup_inline_above_error.9.png
And you're supposed to be able to set them using two attributes:
errorMessageBackground
errorMessageAboveBackground
But when I try to set them in my theme, all I get is:
<item name="errorMessageBackground">#drawable/popup_inline_error_holo_light</item>
<item name="errorMessageAboveBackground">#drawable/popup_inline_error_above_holo_light</item>
error: Error: No resource found that matches the given name: attr 'errorMessageBackground'.
(it's the same with android:errorMessageBackground)
I'm putting this question here, cause I've run out of ideas - maybe someone already managed to do that?
EDIT:
Header of the Theme I'm using:
<resources xmlns:android="http://schemas.android.com/apk/res/android">
<style
name="Theme.MyThemeName"
parent="#style/Theme.Sherlock.Light">
ANOTHER EDIT:
Uh, I've found that my question is a duplicate of:
android:errorMessageBackground getting no resource found error in styles.xml
YET ANOTHER EDIT:
This is a known problem, take a look at this link: https://code.google.com/p/android/issues/detail?id=55879
I would suggest to use #Codeversed solution, but if it doesn't fit for you for some reason you can use my custom EditText implementation.
Usual EditText representation:
EditText with error:
In few words: I've created custom xml state for error display. See related code below:
InputEditText.java:
import android.annotation.TargetApi;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.os.Build;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.widget.EditText;
import com.example.oleksandr.inputedittext.R;
/**
* Input EditText which allows define custom drawable for error state
*/
public class InputEditText extends EditText {
private static final int[] STATE_ERROR = {R.attr.state_error};
private boolean mIsError = false;
public InputEditText(Context context) {
this(context, null, 0);
init();
}
public InputEditText(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public InputEditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
#TargetApi(Build.VERSION_CODES.LOLLIPOP)
public InputEditText(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init();
}
private void init() {
addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
// empty
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
setError(null);
}
#Override
public void afterTextChanged(Editable s) {
// empty
}
});
}
#Override
public void setError(CharSequence error) {
mIsError = error != null;
super.setError(error);
refreshDrawableState();
}
#Override
public void setError(CharSequence error, Drawable icon) {
mIsError = error != null;
super.setError(error, icon);
refreshDrawableState();
}
#Override
protected int[] onCreateDrawableState(int extraSpace) {
final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
if (mIsError) {
mergeDrawableStates(drawableState, STATE_ERROR);
}
return drawableState;
}
}
drawable/edittext_bg_error.xml
<?xml version="1.0" encoding="utf-8"?>
<shape
android:id="#+id/listview_background_shape"
xmlns:android="http://schemas.android.com/apk/res/android"
>
<stroke
android:width="2dp"
android:color="#f00"
/>
<padding
android:bottom="2dp"
android:left="2dp"
android:right="2dp"
android:top="2dp"
/>
<corners android:radius="5dp"/>
<solid android:color="#ffffffff"/>
</shape>
drawable/edittext_bg_selector.xml
<?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">
<!-- custom error state drawable -->
<item android:drawable="#drawable/edittext_bg_error" app:state_error="true"/>
<!-- Do whatever you want for all other states -->
<item android:drawable="#android:drawable/editbox_background_normal"/>
</selector>
add to your attrs.xml
<attr name="errorColor" format="reference"/>
and to styleables.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="error">
<attr name="state_error" format="boolean"/>
</declare-styleable>
</resources>
and usage is really simple:
<com.example.oleksandr.inputedittext.views.InputEditText
android:id="#id/edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/edittext_bg_selector"
android:inputType="text"
android:text="#string/hello_world"
/>
[EDIT]:
Just realized, that original answer was about changing error popup color, but not EditText background color. Anyway, hope this can help someone.
you will need to include these dependancies:
compile 'com.android.support:appcompat-v7:23.1.1'
compile 'com.android.support:design:23.1.1'
and here is a sample on how to use it:
<android.support.design.widget.TextInputLayout
android:id="#+id/input_layout_password"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/input_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/hint_email" />
</android.support.design.widget.TextInputLayout>
This will give you the Material Design you are looking for to give form validation as well as a nice animation effect for the label.
private EditText adTitle;
// ....
adTitle.setError(Html.fromHtml("<font color='red'>hello</font>"));
You can use this method just pass msg text,your edittext id
public static void setErrorMsg(String msg,EditText viewId)
{
//Osama ibrahim 10/5/2013
int ecolor = Color.WHITE; // whatever color you want
String estring = msg;
ForegroundColorSpan fgcspan = new ForegroundColorSpan(ecolor);
SpannableStringBuilder ssbuilder = new SpannableStringBuilder(estring);
ssbuilder.setSpan(fgcspan, 0, estring.length(), 0);
viewId.setError(ssbuilder);
}
Related
I want setError when TextInputLayout isEmpty, I write this code but when show error message, set red background for TextInputLayout!
I do not want set background! I want just show the error message.
My code:
if (TextUtils.isEmpty(userName)) {
register_UserName_layout.setError("Insert Username");
}
XML code :
<android.support.design.widget.TextInputLayout
android:id="#+id/register_userUsernameTextLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/register_headerLayout"
android:layout_margin="10dp"
android:textColorHint="#c5c5c5">
<EditText
android:id="#+id/register_userUserNameText"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#drawable/selector_bg_edit"
android:hint="نام کاربری"
android:paddingBottom="2dp"
android:textColor="#color/colorAccent"
android:textCursorDrawable="#drawable/bg_input_cursor"
android:textSize="16sp" />
</android.support.design.widget.TextInputLayout>
How can I fix this? Thanks all <3
I have found a workaround to this problem. You just need to create a custom EditText and override the getBackground() method of it to return a new drawable. That way TextInputLayout won't be able to set color filter on the EditText's background, since you do not return EditText's background, but some another drawable. See below:
#Override
public Drawable getBackground() {
return ContextCompat.getDrawable(getContext(), R.drawable.some_drawable);
}
and use the custom EditText inside TextInputLayout:
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CustomEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/custom_edit_text_bg" />
</android.support.design.widget.TextInputLayout>
in my case I added line
<solid android:color="#color/transparent"/>
<shape android:shape="rectangle"
xmlns:android="http://schemas.android.com/apk/res/android" >
<stroke android:color="#color/lightGray" android:width="1dp"/>
<solid android:color="#color/transparent"/>
<padding android:top="7dp" android:bottom="7dp" android:left="7dp" android:right="7dp"/>
<corners android:radius="2dp"/>
</shape>
this results in red border only not entire background
You can subclass TextInputLayout and use that:
package com.mypackage;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout;
import android.util.AttributeSet;
public class CustomTextInputLayout extends TextInputLayout {
public CustomTextInputLayout(Context context) {
super(context);
}
public CustomTextInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
clearEditTextColorfilter();
}
#Override
public void setError(#Nullable CharSequence error) {
super.setError(error);
clearEditTextColorfilter();
}
private void clearEditTextColorfilter() {
EditText editText = getEditText();
if (editText != null) {
Drawable background = editText.getBackground();
if (background != null) {
background.clearColorFilter();
}
}
}
}
in your layout:
<com.mypackage.CustomTextInputLayout
android:id="#+id/register_userUsernameTextLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/register_headerLayout"
android:layout_margin="10dp"
android:textColorHint="#c5c5c5">
<EditText
android:id="#+id/register_userUserNameText"
android:layout_width="match_parent"
android:layout_height="40dp"
android:background="#drawable/selector_bg_edit"
android:hint="نام کاربری"
android:paddingBottom="2dp"
android:textColor="#color/colorAccent"
android:textCursorDrawable="#drawable/bg_input_cursor"
android:textSize="16sp" />
</android.support.design.widget.TextInputLayout>
Bottom line with this issue is check if your EditText has a background color set. If so, remove it and put a background color on your text Input Layout widget instead. This will fix the issue of the big red box. At least it did for me.
Subclass EditText and apply the follow methods:
Override getBackground and create a Drawable with input background
#Override
public Drawable getBackground() {
return ContextCompat.getDrawable(getContext(), R.drawable.input_background);
}
your Drawable input_brackground can be like:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid
android:color="#color/inputBackgroundColor">
</solid>
</shape>
And apply EditText subclassed setBackground after TextInputLayout set error like:
private void showTheError(String error){
textInputLayout.setError(error);
editTextSubclassed.setBackgroundColor(getResources().getColor(R.color.inputBackgroundColor));
}
Similar to Shahin Mursalov's answer, I call method init() in the costructor to store the background drawable when the view is created. Also I've overriden method setBackgroundResource() to store the background as well, and return it from getBackground():
public class CustomEditText extends EditText{
private Drawable backgroundDrawable;
public EditTextContext context) {
super(context);
this.context = context;
}
public EditTextContext context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init(attrs);
}
public EditText(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.context = context;
init(attrs);
}
public void init(AttributeSet attrs){
TypedArray attributes = context.obtainStyledAttributes(attrs, new int[]{android.R.attr.background});
this.backgroundDrawable = attributes.getDrawable(0);
attributes.recycle();
}
#Override
public void setBackgroundResource(#DrawableRes int resId) {
this.backgroundDrawable = ContextCompat.getDrawable(context, resId);
super.setBackgroundResource(resId);
}
#Override
public Drawable getBackground() {
if (backgroundDrawable != null){
return backgroundDrawable;
} else {
return super.getBackground();
}
}
}
This way I can specify a different background in my layout and change it programatically.
first your EditText background selector_bg_edit should be like that
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:drawable="#drawable/code_view_item"
android:state_focused="true"/>
<item android:drawable="#drawable/sign_in_fields"
android:state_focused="false"/>
<item android:drawable="#drawable/sign_in_fields"
android:state_active="false"/>
</selector>
And the most important thing is to put in the drawable to be transparent color like this
code_view_item.xml
<stroke android:width="1dp" android:color="#15A859" />
<solid android:color="#00FFFFFF" />
<corners android:radius="12dp"/>
What worked for me is just putting this below lines in my custom edit text class :
override fun getBackground(): Drawable {
return this.context.getDrawable(R.drawable.myDrawableResource)
}
My solution:
yourEdit.background.clearColorFilter()
if (TextUtils.isEmpty(userName)) {
register_UserName_layout.setError("Insert Username");
txtInpit.setColorFilter(R.color.white);
}
How can I change the color of my scrollbar in a recyclerView?
I have the scrollbar but I want to change its color.
My recyclerView is like this:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/recyclerView"
android:layout_below="#id/my_toolbar"
android:layout_above="#+id/progressBar"
android:scrollbars="vertical"
/>
You can do this by including following line of code in your Recyclerview
android:scrollbarThumbVertical="#drawable/yoursdrawablefile
The drawable file in my case is:
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<gradient android:startColor="#000" android:endColor="#000"
android:angle="45"/>
<corners android:radius="6dp" />
</shape>
In case you want to further style your scrollbars, create two drawable resource file in your drawable folder as 1. scrollbar_track and 2. scrollbar_thumb
scrollbar_track.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="0"
android:endColor="#9BA3C5"
android:startColor="#8388A4" />
<corners android:radius="6dp" />
</shape>
scrollbar_thumb.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" >
<gradient
android:angle="0"
android:endColor="#b20111"
android:startColor="#cf1d2d" />
<corners android:radius="6dp" />
</shape>
Now, create a style named scrollbar_style in your styles.xml file as:
<style name="scrollbar_style">
<item name="android:scrollbarAlwaysDrawVerticalTrack">true</item>
<item name="android:scrollbarStyle">outsideOverlay</item>
<item name="android:scrollbars">vertical</item>
<item name="android:fadeScrollbars">true</item>
<item name="android:scrollbarThumbVertical">#drawable/scrollbar_thumb</item>
<item name="android:scrollbarTrackVertical">#drawable/scrollbar_track</item>
<item name="android:scrollbarSize">8dp</item>
<item name="android:scrollbarFadeDuration">800</item>
<item name="android:scrollbarDefaultDelayBeforeFade">500</item>
</style>
Finally, to apply this style to the scrollbar in your recyclerview, add
style="#style/scrollbar_style"
to your recyclerview.
In your case:
<android.support.v7.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/recyclerView"
style="#style/scrollbar_style"
android:layout_below="#id/my_toolbar"
android:layout_above="#+id/progressBar"
android:scrollbars="vertical"
/>
If you need to change the color in runtime, this is the way.
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.support.annotation.ColorInt;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
/**
* Created on 22.3.2016.
*
* #author Bojan Kseneman
* #description A recycler view that will draw the scroll bar with a different color
*/
public class CustomScrollBarRecyclerView extends RecyclerView {
private int scrollBarColor = Color.RED;
public CustomScrollBarRecyclerView(Context context) {
super(context);
}
public CustomScrollBarRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomScrollBarRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void setScrollBarColor(#ColorInt int scrollBarColor) {
this.scrollBarColor = scrollBarColor;
}
/**
* Called by Android {#link android.view.View#onDrawScrollBars(Canvas)}
**/
protected void onDrawHorizontalScrollBar(Canvas canvas, Drawable scrollBar, int l, int t, int r, int b) {
scrollBar.setColorFilter(scrollBarColor, PorterDuff.Mode.SRC_ATOP);
scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
/**
* Called by Android {#link android.view.View#onDrawScrollBars(Canvas)}
**/
protected void onDrawVerticalScrollBar(Canvas canvas, Drawable scrollBar, int l, int t, int r, int b) {
scrollBar.setColorFilter(scrollBarColor, PorterDuff.Mode.SRC_ATOP);
scrollBar.setBounds(l, t, r, b);
scrollBar.draw(canvas);
}
}
These methods are defined in View class, so the same princible should work of other views like ScrollView and ListView.
How to set a custom style or other background selector drawable for the SwitchPreference widget in Android?
(Note: not the regular Switch widget, I mean the standart SwitchPreference widget that used in PreferenceActivity / PreferenceFragment)
You have to create a custom layout for the switch itself and you can apply it dynamically like.
preference.setWidgetLayoutResource(R.layout.custom_switch);
But I'll go into details and show you exactly how to achieve this.
So, you define your preference in an xml file like preferences.xml
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" >
<PreferenceCategory android:title="YOUR_CATEGORY_TITLE" >
<SwitchPreference
android:key="SWITCH"
android:title="YOUR_TITLE_FOR_SWITCH" />
</PreferenceCategory>
</PreferenceScreen>
Then read it in the onCreate() method inside your PreferenceActivty class:
SwitchPreference pref = (SwitchPreference) findPreference(getString(R.string.SWITCH));
//pref.setChecked(true); // You can check it already if needed to true or false or a value you have stored persistently
pref.setWidgetLayoutResource(R.layout.custom_switch); // THIS IS THE KEY OF ALL THIS. HERE YOU SET A CUSTOM LAYOUT FOR THE WIDGET
pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
#Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
// Here you can enable/disable whatever you need to
return true;
}
});
The custom_switch layout looks like this:
<?xml version="1.0" encoding="utf-8"?>
<Switch xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/custom_switch_item"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textColor="#android:color/white"
android:textIsSelectable="false"
android:textSize="18sp"
android:textStyle="bold"
android:track="#drawable/switch_track"
android:thumb="#drawable/switch_thumb"/>
And for the switch you'll have 2 selectors for the track and thumb properties.
The drawables for these selectors can be generated with the Android Holo Color Generator, which was suggested by tasomaniac. In this case, all you have to do, is to copy the content of the generated drawable folders(only for the drawable-hdpi, drawable-mdpi, drawable-xhdpi, drawable-xxhdpi). But you can create custom views for each state you need.
Here is how these selectors will look like:
switch_track:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/switch_bg_focused" android:state_focused="true"/>
<item android:drawable="#drawable/switch_bg"/>
</selector>
switch_thumb:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:drawable="#drawable/switch_thumb_disabled" android:state_enabled="false"/>
<item android:drawable="#drawable/switch_thumb_pressed" android:state_pressed="true"/>
<item android:drawable="#drawable/switch_thumb_activated" android:state_checked="true"/>
<item android:drawable="#drawable/switch_thumb"/>
</selector>
And that's pretty much it. This solution helped me out. If I omitted something, please let me know and I'll correct the issues.
You can use the below website to generate style for your Switch.
http://android-holo-colors.com/
And then you can use following libraries to custom implementation of the regular Switch. These libraries also include SwitchPreference alternative.
https://github.com/BoD/android-switch-backport
https://github.com/ankri/SwitchCompatLibrary
One way of doing this is to subclass the SwitchPreference and override the onBindView method. In doing so, you'll want to still call super.onBindView(view) in that method, but then find the Switch in the child views and style it as appropriate:
package com.example;
import android.annotation.SuppressLint;
import android.content.Context;
import android.preference.SwitchPreference;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Switch;
import com.example.R;
public class CustomSwitchPreference extends SwitchPreference {
#SuppressLint("NewApi")
public CustomSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
public CustomSwitchPreference(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public CustomSwitchPreference(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomSwitchPreference(Context context) {
super(context);
}
#Override
protected void onBindView(View view) {
super.onBindView(view);
Switch theSwitch = findSwitchInChildviews((ViewGroup) view);
if (theSwitch!=null) {
//do styling here
theSwitch.setThumbResource(R.drawable.new_thumb_resource);
}
}
private Switch findSwitchInChildviews(ViewGroup view) {
for (int i=0;i<view.getChildCount();i++) {
View thisChildview = view.getChildAt(i);
if (thisChildview instanceof Switch) {
return (Switch)thisChildview;
}
else if (thisChildview instanceof ViewGroup) {
Switch theSwitch = findSwitchInChildviews((ViewGroup) thisChildview);
if (theSwitch!=null) return theSwitch;
}
}
return null;
}
}
Create a style in your style.xml file and give it Widget.AppCompat.CompoundButton.Switch parent.
<style name="theme_switch_compat" parent="Widget.AppCompat.CompoundButton.Switch">
<item name="colorAccent">#color/YourColorAccent</item>
</style>
Then you can use the link below to complete your theme
How to change the track color of a SwitchCompat
I'm working on an application that contains some buttons defined via layout.xml like this
<?xml version="1.0" encoding="utf-8"?>
<Button xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#drawable/largebutton" >
</Button>
#drawable/largebutton looks like this
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" >
<shape>
<gradient android:startColor="#color/menu_button_active_start" android:endColor="#color/menu_button_active_end" android:angle="270" />
<stroke android:width="#dimen/largebutton_stroke" android:color="#color/menu_button_stroke" />
<corners android:radius="#dimen/largebutton_radius" />
<padding android:left="#dimen/largebutton_padding_leftright" android:top="#dimen/largebutton_padding_topbottom" android:right="#dimen/largebutton_padding_leftright" android:bottom="#dimen/largebutton_padding_topbottom" />
</shape>
</item>
<item android:state_focused="true" >
<shape>
<gradient android:startColor="#color/menu_button_focused_start" android:endColor="#color/menu_button_focused_end" android:angle="270" />
<stroke android:width="#dimen/largebutton_stroke" android:color="#color/menu_button_focused_stroke" />
<corners android:radius="#dimen/largebutton_radius" />
<padding android:left="#dimen/largebutton_padding_leftright" android:top="#dimen/largebutton_padding_topbottom" android:right="#dimen/largebutton_padding_leftright" android:bottom="#dimen/largebutton_padding_topbottom" />
</shape>
</item>
.....
</selector>
All properties like padding, stroke, radius are the same, except gradient colors in different states. My problem is that my application has to have more styles. You can imagine it as you have list of colors and when you choose one application changes all colors to selected one. So if you have 20 colors, 20 different xmls isn't the right way.
Both startColor and endColor values for all android:states are downloaded from web and saved to DB and I don't know how many of them are there.
Is there any way to achieve this behavior? I've searched all forums and the most of answers were that it is imposible. I found one 'solution' overwriting colors.xml but it doesn't seems to be the best solution to me.
So my question is, can I dynamically change color in colors.xml? Something like this
List<Colors> colors = downloadColorsFromWeb();
Button b = new Button;
b.setDrawable(drawable.with(colors));
Thank you all in advance.
nosko.
You could probably dynamically generate a drawable for each color you download. Check the GradientDrawable class. I think you can provide start/end colors during the initialization and set the stroke and corner radius properties after that. But you'll have to find out about the padding yourself. I am not sure.
After you create the drawable, you can use it in the button's setBackgroundDrawable
edit: probably setting the button's padding would do the trick
edit2: you can setState to the drawable but I am not sure how to set different background drawable for each state of the button.
Thank you #stan0 for your reply, it helped a lot, especially GradientDrawable class.
I've wrote simple class that creates button and can set style depending on its state. Maybe it helps to someone :)
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.GradientDrawable.Orientation;
import android.util.AttributeSet;
import android.widget.Button;
/**
* #author nosko
*
*/
public class TabButton extends Button {
private Context c;
private GradientDrawable selected, focused, pressed, normal;
public void setNormalState(GradientDrawable gd) {
this.normal = gd;
}
public void setSelectedState(GradientDrawable gd) {
this.selected = gd;
}
public void setFocusedState(GradientDrawable gd) {
this.focused = gd;
}
public void setPressedState(GradientDrawable gd) {
this.pressed = gd;
}
public TabButton(Context context, AttributeSet attrs) {
super(context, attrs);
// TODO Auto-generated constructor stub
selected = pressed = focused = normal = new GradientDrawable(Orientation.TOP_BOTTOM, new int[] { Color.WHITE, Color.DKGRAY });
this.c = context;
this.setPadding(8, 8, 8, 8);
}
/**
* Change colors when button's state changes
*/
protected void drawableStateChanged() {
normal.setCornerRadius(8);
normal.setStroke(2, Color.parseColor(c.getResources().getString(R.color.tab_button_border)));
normal.setShape(GradientDrawable.RECTANGLE);
this.setBackgroundDrawable(normal);
if (isSelected()) {
selected.setCornerRadius(8);
selected.setStroke(2, Color.parseColor(c.getResources().getString(R.color.tab_button_border)));
selected.setShape(GradientDrawable.RECTANGLE);
this.setBackgroundDrawable(selected);
}
if (isFocused()) {
focused.setCornerRadius(8);
focused.setStroke(2, Color.parseColor(c.getResources().getString(R.color.tab_button_border)));
focused.setShape(GradientDrawable.RECTANGLE);
this.setBackgroundDrawable(focused);
}
if (isPressed()) {
pressed.setCornerRadius(8);
pressed.setStroke(2, Color.parseColor(c.getResources().getString(R.color.tab_button_border)));
pressed.setShape(GradientDrawable.RECTANGLE);
this.setBackgroundDrawable(pressed);
}
}
}
and use it like this
TabButton b = new TabButton(context, null);
b.setNormalState(new GradientDrawable(Orientation.TOP_BOTTOM, new int[] { Color.RED, Color.CYAN }));
b.setSelectedState(new GradientDrawable(Orientation.TOP_BOTTOM, new int[] { Color.YELLOW, Color.BLUE }));
b.setFocusedState(new GradientDrawable(Orientation.TOP_BOTTOM, new int[] { Color.YELLOW, Color.GREEN }));
b.setPressedState(new GradientDrawable(Orientation.TOP_BOTTOM, new int[] { Color.YELLOW, Color.BLACK }));
My question is how to add a shadow to text when TextView is selected or View that TextView is in gets selected. For example I have a CheckedTextView which changes background according to type of selection. I also made a text selector which changes color on differents states. Now I would like to add a shadow when for example View gets selected. So it changes background color, text color and creates a shadow. This is my text selector:
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_focused="true"
android:state_pressed="false"
android:color="#android:color/white"
style="#style/DarkShadow"/>
<item
android:state_focused="true"
android:state_pressed="true"
android:color="#android:color/white"
style="#style/DarkShadow"/>
<item
android:state_focused="false"
android:state_pressed="true"
android:color="#android:color/white"
style="#style/DarkShadow"/>
<item
android:color="#color/primary_text_light_disable_only"/>
and style:
<style name="DarkShadow">
<item name="android:shadowColor">#BB000000</item>
<item name="android:shadowRadius">2.75</item>
</style>
Now text gets properly highlighted but no shadows appear. Does anyone know how to solve this?
This is a current limitation of the Android SDK.
I extended TextView for it to work, you can use it freely:
CustomTextView.java:
import android.widget.TextView;
import android.util.AttributeSet;
import android.content.res.TypedArray;
import android.content.Context;
import com.client.R;
public class CustomTextView extends TextView
{
private static String TAG = "CustomTextView";
private ColorStateList mShadowColors;
private float mShadowDx;
private float mShadowDy;
private float mShadowRadius;
public CustomTextView(Context context)
{
super(context);
}
public CustomTextView(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context, attrs);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(context, attrs);
}
/**
* Initialization process
*
* #param context
* #param attrs
* #param defStyle
*/
private void init(Context context, AttributeSet attrs, int defStyle)
{
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView, defStyle, 0);
final int attributeCount = a.getIndexCount();
for (int i = 0; i < attributeCount; i++) {
int curAttr = a.getIndex(i);
switch (curAttr) {
case R.styleable.CustomTextView_shadowColors:
mShadowColors = a.getColorStateList(curAttr);
break;
case R.styleable.CustomTextView_android_shadowDx:
mShadowDx = a.getFloat(curAttr, 0);
break;
case R.styleable.CustomTextView_android_shadowDy:
mShadowDy = a.getFloat(curAttr, 0);
break;
case R.styleable.CustomTextView_android_shadowRadius:
mShadowRadius = a.getFloat(curAttr, 0);
break;
default:
break;
}
}
a.recycle();
updateShadowColor();
}
private void updateShadowColor()
{
if (mShadowColors != null) {
setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColors.getColorForState(getDrawableState(), 0));
invalidate();
}
}
#Override
protected void drawableStateChanged()
{
super.drawableStateChanged();
updateShadowColor();
}
}
You also need to add this to your attr.xml (or create one):
attr.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Theme">
<attr format="reference" name="CustomTextView"/>
</declare-styleable>
<declare-styleable name="CustomTextView">
<attr name="shadowColors" format="color|reference"/>
<attr name="android:shadowDx"/>
<attr name="android:shadowDy"/>
<attr name="android:shadowRadius"/>
</declare-styleable>
</resources>
So finally you'll be able to use it in your xmls, like this:
<com.client.ui.textviews.CustomTextView
xmlns:client="http://schemas.android.com/apk/res/com.client"
android:id="#+id/join_text"
android:shadowDx="1"
android:shadowDy="1"
android:shadowRadius="1"
client:shadowColors="#color/btn_green_shadow_color"/>
Where #color/btn_green_shadow_color points to a selector such a this:
<?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:color/white"/>
<item android:state_pressed="true" android:color="#color/BzDarkGray"/>
<item android:color="#android:color/black"/>
</selector>
If you are not familiar with how to use custom attributes (with the custom xml namespace I used), please refer to this good StackOverFlow question.
Yeah, I ran into the same problem, you can change the text color using a selector in xml, but not the shadowcolor.
So in order to solve the problem, you might have to extend CheckedTextView or whatever View you need, and then override onDraw(Canvas canvas) according to the state of the View
Thus, you need to use
public void setShadowLayer (float radius, float dx, float dy, int color) defined in here
for example:
#Override
protected void onDraw(Canvas canvas) {
if(isPressed()){
setShadowLayer(1, 0, 1, Color.RED);
}else{
if(isFocused()){
setShadowLayer(1, 0, 1, Color.WHITE);
}else{
setShadowLayer(1, 0, 1, Color.BLACK);
}
}
super.onDraw(canvas);
}
I hope that works
This is what I ended up doing:
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if(isPressed()) {
setShadowLayer(15, 0, 0, getTextColors().getDefaultColor());
} else {
setShadowLayer(0, 0, 0, Color.TRANSPARENT);
}
}