How to add shadow to TextView on selection/focus - android

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);
}
}

Related

Use paddingBottom for all TextView based on font size

I use in my application a custom font called Barlow. However, I need to specify a bottomPadding in each TextView because the font isn't centered vertically.
Here's what I did:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
...
<item name="android:textViewStyle">#style/Widget.Comblat.TextView</item>
<item name="textAppearanceBody1">#style/TextAppearance.Comblat.Body1</item>
<item name="textAppearanceHeadline3">#style/TextAppearance.Comblat.H3</item>
...
</style>
<!-- TextView -->
<style name="Widget.Comblat.TextView" parent="Widget.MaterialComponents.TextView">
<item name="android:paddingBottom">1.6dp</item>
</style>
<!-- CustomFont -->
<style name="TextAppearance.Comblat.Body1" parent="TextAppearance.MaterialComponents.Body1">
<item name="fontFamily">#font/mbarlow_condensed_regular</item>
<item name="android:textStyle">normal</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">16sp</item>
<item name="android:letterSpacing">0</item>
<item name="android:includeFontPadding">false</item>
<item name="android:textColor">#color/colorDarkPrimary60</item>
</style>
<style name="TextAppearance.Comblat.H3" parent="TextAppearance.MaterialComponents.Headline3">
<item name="fontFamily">#font/mbarlow_condensed_semibold</item>
<item name="android:textColor">#color/colorDarkPrimary</item>
<item name="android:textStyle">normal</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">48sp</item>
<item name="android:includeFontPadding">false</item>
<item name="android:letterSpacing">-0.023</item>
</style>
Here I set paddingBottom to 1.6dp. But this is working only on the Body1 font (my formula for paddingBottom is 0.1*font_size), for H3 I would like to set 4.8dp.
Is there a way to set a paddingBottom for all TextView but depending on the font size ? I supposed it is possible programmatically, or by Overriding the TextView.
Of course I could set a custom style on each TextView in my layouts, but hey, I'm lazy guys
Thanks!
Well, I created a custom TextView, and replace all TextView with my own.
Here's the code:
public class BarlowTextView extends MaterialTextView {
public static final float RATIO_FONT_PADDING = 0.1f;
private int dpAsPixels = 0;
public BarlowTextView(#NonNull Context context) {
super(context);
float scale = getResources().getDisplayMetrics().density;
dpAsPixels = (int) (getTextSize() * scale * RATIO_FONT_PADDING);
}
public BarlowTextView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
float scale = getResources().getDisplayMetrics().density;
dpAsPixels = (int) (getTextSize() * scale * RATIO_FONT_PADDING);
}
public BarlowTextView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
float scale = getResources().getDisplayMetrics().density;
dpAsPixels = (int) (getTextSize() * scale * RATIO_FONT_PADDING);
}
public BarlowTextView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
float scale = getResources().getDisplayMetrics().density;
dpAsPixels = (int) (getTextSize() * scale * RATIO_FONT_PADDING);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setPaddingRelative(0 , 0 , 0 , dpAsPixels);
}
}

Different theme styles for not all views

In my Android app I have two different themes (light and dark).
For example:
<style name="AppThemeDark" parent="Theme.AppCompat">
<item name="colorPrimary">#android:color/black</item>
<item name="colorPrimaryDark">#android:color/black</item>
<item name="colorAccent">#android:color/holo_red_dark</item>
<item name="android:textColor">#android:color/white</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="AppThemeLight" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="windowActionModeOverlay">true</item>
</style>
So, now I can apply, for example, different text colors to a TextView (white for dark theme and black for light):
<item name="android:textViewStyle">#style/TextViewDark</item>
<style name="TextViewDark">
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
But it will apply to all TextViews.
The main question, is it possible to make in XML (not programmatically) next:
Light theme: Half of TextViews text color black, and another half green.
Black theme: TextViews that black in Light theme - red, and another half - blue (which are green in Light theme).
Create 2 classes extends TextView
public class OneTextView extends TextView {
public OneTextView(Context context) {
super(context);
init(context);
}
public OneTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public OneTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
int[] attrs = new int[] { R.attr.myFirstColor};
TypedArray ta = context.obtainStyledAttributes(attrs);
int appColor = ta.getColor(0, 0);
ta.recycle();
// set theme color
setTextColor(appColor);
}
}
public class SecondTextView extends TextView {
public SecondTextView(Context context) {
super(context);
init(context);
}
public SecondTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SecondTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
int[] attrs = new int[] { R.attr.mySecondColor};
TypedArray ta = context.obtainStyledAttributes(attrs);
int appColor = ta.getColor(0, 0);
ta.recycle();
// set theme color
setTextColor(appColor);
}
}
each class you can use in xml like this
<com.route.to.class.OneTextView
android:layout_width="match_parent"
android:layout_height="match_parent" />
OneTextView can have black and red colors
SecondTextView can have green and blue colors
define attr.xml in values
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="myFirstColor" format="color" />
<attr name="mySecondColor" format="color" />
</resources>
Then in your styles.xml, define colors for each theme:
<style name="Theme.MyApp" parent="#style/Theme.Light">
<item name="myFirstColor">#color/black</item>
<item name="mySecondColor">#color/green</item>
</style>
<style name="Theme.MyApp.Dark" parent="#style/Theme.Dark">
<item name="myFirstColor">#color/green</item>
<item name="mySecondColor">#color/blue</item>
</style>
you have defined in styles.xml
<style name="TextViewDark">
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
<style name="TextViewLight">
<item name="android:textColor">#color/green</item>
</style>
then you can use it in main.xml
<LinearLayout>
<TextView
android:id="#+id/light_text_view"
android:text"i´m use light theme"
style="#style/TextViewLight"/>
<TextView
android:id="#+id/dark_text_view"
android:text"i´m use darktheme"
style="#style/TextViewDark"/>
</LinearLayout>
you don´t need to define styles in AppThemes

Android: How to custom-declare XML namespace in styles.xml?

I'm trying to put the custom XML namespace in the styles.xml and inherit it in the layout. I don't know how to declare the custom XML namespace in the styles.xml as I do in layout xml (e.g. xmlns:app="http://schemas.android.com/tools").
How do I use custom XML namespace in the styles.xml?
What I have:
The font asset, ReallyCoolFont.ttf is saved in the asset/fonts.
my_layout.xml:
<TextView
<!-- more attributes here -->
app:customFont="fonts/ReallyCoolFont.ttf"
<!-- more attributes here -->
</TextView>
styles.xml:
<style name="CoolTextView">
<!-- more items here -->
<!-- more items here -->
</style>
What I'd like to have:
my_layout.xml:
<TextView
<!-- more attributes here -->
style="#style/CoolTextView
<!-- more attributes here -->
</TextView>
styles.xml:
<style name="CoolTextView">
<!-- more items here -->
<item name="app:customFont">ReallyCoolFont.ttf</item>
<!-- more items here -->
</style>
Error I get:
Error:(1403, 21) No resource found that matches the given name: attr 'app:customFont'.
You need to define an attribute for your fonts in attr.xml file in res folder:
<attr name="myfonts" format="string" />
And you need to define custom style for your TextView and here we use our defined attribute(myfonts):
<declare-styleable name="MyCustomStyle">
<attr name="myfonts" />
</declare-styleable>
Then styles can be declared:
<style name="CoolTextView">
<item name="myfonts">ReallyCoolFont.ttf</item>
</style>
summary of what you have so far:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="myfonts" format="string">
</attr>
<declare-styleable name="MyCustomStyle">
<attr name="myfonts" />
</declare-styleable>
<style name="CoolTextView">
<item name="myfonts">ReallyCoolFont.ttf</item>
</style>
</resources>
4)Now your layout would be:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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" >
<com.example.MyCustomTextView
android:id="#+id/result"
style="#style/CoolTextView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="HELLO WORLD!"
android:textSize="24dp"
android:gravity="center" >
</com.example.MyCustomTextView>
</RelativeLayout>
5)and your MyCustomTextView is:
public class MyCustomTextView extends TextView {
private static final String TAG = "TextView";
public MyCustomTextView(Context context) {
super(context);
}
public MyCustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
settingFont(context, attrs);
}
public MyCustomTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
settingFont(context, attrs);
}
private void settingFont(Context ctx, AttributeSet attrs) {
TypedArray a = ctx.obtainStyledAttributes(attrs, R.styleable.MyCustomStyle);
String customFont = a.getString(R.styleable.MyCustomStyle_myfonts);
Typeface tf = null;
try {
tf = Typeface.createFromAsset(ctx.getAssets(), customFont);
} catch (Exception e) {
Log.e(TAG,e.getMessage());
a.recycle();
return;
}
setTypeface(tf);
a.recycle();
}
}
I assumed you put the font in asset not in asset/fonts directory.
also I highly recommend read this.
You don't need to add any prefix to reference your custom attributes in the style resource files. Doing it like this will work just fine:
<style name="CoolTextView">
<item name="customFont">ReallyCoolFont.ttf</item>
</style>
The answer is to NOT specify the namespace in the style.
<?xml version="1.0" encoding="utf-8" ?>
<resources xmlns:custom="http://schemas.android.com/apk/res/com.custom.project">
<style name="CustomStyle">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="customAttr">value</item> <!-- tee hee -->
</style>
</resources>
You don't need any prefixes, it will work without them. This is code from one of my projects, which works just fine
<style name="defaultTriangle">
<item name="triangleColor">#FF33B5E5</item>
<item name="triangleStrokeColor">#android:color/black</item>
<item name="triangleStrokeWidth">3dp</item>
</style>
<si.kseneman.views.Triangle
style="#style/defaultTriangle"
android:layout_width="match_parent"
android:layout_height="0dip"
android:layout_weight="1"
android:padding="10dp"
android:rotation="0"
/>
I've done next for custom font
CustomTextView
public class KlavikaTextView extends TextView {
private final static int KLAVIKA_BOLD = 0;
private final static int KLAVIKA_BOLD_ITALIC = 1;
private final static int KLAVIKA_LIGHT = 2;
private final static int KLAVIKA_LIGHT_ITALIC = 3;
private final static int KLAVIKA_MEDIUM = 4;
private final static int KLAVIKA_MEDIUM_ITALIC = 5;
private final static int KLAVIKA_REGULAR = 6;
private final static int KLAVIKA_REGULAR_ITALIC = 7;
public KlavikaTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
parseAttributes(context, attrs);
}
public KlavikaTextView(Context context, AttributeSet attrs) {
super(context, attrs);
parseAttributes(context, attrs);
}
public KlavikaTextView(Context context) {
super(context);
}
private void parseAttributes(Context context, AttributeSet attrs) {
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.KlavikaTextView);
// The value 0 is a default, but shouldn't ever be used since the attr is an enum
int typeface = values.getInt(R.styleable.KlavikaTextView_typeface, KLAVIKA_REGULAR);
// You can instantiate your typeface anywhere, I would suggest as a
// singleton somewhere to avoid unnecessary copies
switch (typeface) {
case KLAVIKA_BOLD:
setTypeface(App.klavikaBold);
break;
case KLAVIKA_BOLD_ITALIC:
setTypeface(App.klavikaBoldItalic);
break;
case KLAVIKA_LIGHT:
setTypeface(App.klavikaLight);
break;
case KLAVIKA_LIGHT_ITALIC:
setTypeface(App.klavikaLightItalic);
break;
case KLAVIKA_MEDIUM:
setTypeface(App.klavikaMedium);
break;
case KLAVIKA_MEDIUM_ITALIC:
setTypeface(App.klavikaMediumItalic);
break;
case KLAVIKA_REGULAR_ITALIC:
setTypeface(App.klavikaRegularItalic);
break;
case KLAVIKA_REGULAR:
default:
setTypeface(App.klavikaRegular);
break;
}
}}
Then in values I've created attr.xml
<!-- Define the values for the attribute -->
<attr name="typeface" format="enum">
<enum name="klavika_bold" value="0" />
<enum name="klavika_bold_italic" value="1" />
<enum name="klavika_light" value="2" />
<enum name="klavika_light_italic" value="3" />
<enum name="klavika_medium" value="4" />
<enum name="klavika_medium_italic" value="5" />
<enum name="klavika_regular" value="6" />
<enum name="klavika_regular_italic" value="7" />
</attr>
<!--
Tell Android that the class "KlavikaTextView" can be styled,
and which attributes it supports-->
<declare-styleable name="KlavikaTextView">
<attr name="typeface" />
</declare-styleable>
Next created the style
<style name="TextView.Example">
<item name="typeface">klavika_bold</item>
</style>
This style you can use for your xml Layouts
style="#style/TextView.Example"
<?xml version="1.0" encoding="utf-8"?>
<resources
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:custom="http://schemas.android.com/apk/res/com.my.project">
<style name="my_style"> <item name="custom:tag">some_value</item> </style>
</resources>
you are trying to apply XML namespacing to an attribute value, which won't work. In this case, you should specify the package name directly, like this:
<style name="my_style"> <item name="com.my.project:tag">some_value</item> </style>
Fast solution via code!
String pathFont = "fonts/ReallyCoolFont.ttf";
TextView text = (TextView) findViewById(R.id.TextView1);
Typeface fontFace = Typeface.createFromAsset( getAssets(), pathFont );
text.setTypeface( fontFace );
Third-party libraries , solve the problem, in XML!
1 - https://github.com/leok7v/android-textview-custom-fonts
2 - https://github.com/ragunathjawahar/android-typeface-textview
My suggestion
You will have other needs, and for each component
you will have to customize a class.
Another problem you have other layouts and N TextView components
for maintenance you will have a lot of work .
I use this method in projects in the OnCreate of my activity
if I need to change the font I have to do this
only in the OnCreate method of each activity .
private static final String FONT = "ReallyCoolFont.ttf";
public static void allTextView(final Context context, final View root) {
String fontPath = FONT;
try {
if (root instanceof ViewGroup) {
ViewGroup viewGroup = (ViewGroup) root;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++)
allTextView(context, viewGroup.getChildAt(i) );
} else if (root instanceof TextView)
((TextView) root).setTypeface(Typeface.createFromAsset(context.getAssets(), fontPath));
} catch (Exception e) {
e.printStackTrace();
}
}
// call in OnCreate Activity
allTextView(this, findViewById(R.layout.main) );
The custom attributes are defined using <declare-styleable> tags; usually the file is called attrs.xml. The namespace declaration will have your app's package name in it.
The whole process is described here: Creating a View Class | Android Developers

ProgressBar not an instance of LayerDrawable

I am trying to implement my own custom progressbar using 9 patch images. I am looking at the Sound ProgressBar example.
http://www.jagsaund.com/blog/2011/11/6/customizing-your-progress-bar-part-one.html
I have two problems that I have been unable to figure out why its happening:
No matter what I do my progressbar is not an instance of
layerdrawable, I am not sure why this is happening so I am unable to
get reference to the layer I want and change it grammatically.
The 9 patch image starts off stretched to the entire width of the container (relativelayout) not sure how to tell it to not
automatically scale so I can control its width grammatically.
Here are the following code snipets:
Drawable progressbar.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#android:id/background"
android:drawable="#drawable/progressbar_bg" >
</item>
<item android:id="#+id/progress">
<bitmap
android:src="#drawable/progressbar"
android:tileMode="repeat" />
</item>
</layer-list>
Style.xml
<style name="Widget">
</style>
<style name="Widget.ProgressBar">
<item name="android:indeterminateOnly">true</item>
<item name="android:indeterminateBehavior">repeat</item>
<item name="android:indeterminateDuration">3500</item>
<item name="android:minWidth">48dip</item>
<item name="android:maxWidth">48dip</item>
<item name="android:minHeight">48dip</item>
<item name="android:maxHeight">48dip</item>
</style>
<style name="Widget.ProgressBar.RegularProgressBar">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">#drawable/progressbar</item>
<item name="android:indeterminateDrawable">#android:drawable/progress_indeterminate_horizontal</item>
<item name="android:minHeight">1dip</item>
<item name="android:maxHeight">10dip</item>
</style>
Custom View inside XML layout file:
<com.custom.ahmad.views.NfcProgressBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/timeOutProgress"
android:layout_above="#+id/tvStart"
style="#style/Widget.ProgressBar.RegularProgressBar" />
Finally The custom Progressbar class:
public class NfcProgressBar extends ProgressBar {
private String text = "";
private int textColor = Color.BLACK;
private float textSize = 15;
public NfcProgressBar(Context context) {
super(context);
}
public NfcProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NfcProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected synchronized void onDraw(Canvas canvas) {
Drawable progressDrawable = this.getProgressDrawable();
Log.i("Testing", "progressDrawable : " + ((progressDrawable != null) ? progressDrawable : "null"));
Log.i("Testing", "progressDrawable instanceof LayerDrawable : " + ((progressDrawable instanceof LayerDrawable)? "true" : "false"));
if (progressDrawable != null && progressDrawable instanceof LayerDrawable) {
// Get the layer and do some programming work.
}
}

Change background of EditText's error message

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);
}

Categories

Resources