17

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?

klemzy
  • 492
  • 1
  • 6
  • 12
  • I knew about Color State List and State List Drawable, but shadow seems to be a particular property of TextView. Probably shadow attributes just get ignored. I would have used some onStateChange method for views, setting dinamically the shadow, but I've just looked for it and surprisingly it exists only for drawables! I would then ask: is it possible to get a view state change? – bigstones Jan 20 '11 at 23:54

3 Answers3

21

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.

Community
  • 1
  • 1
Gilbert
  • 868
  • 8
  • 14
  • I must have misread your post - probably got it confused with another - I thought you'd created new attributes for shadowDx/Dy/Radius. I've deleted the comment. Thanks. – nmw Oct 04 '12 at 15:08
  • @Gilbert I am getting a binary XML file error inflating class customtextview. Any ideas why? – Ashley Staggs Oct 05 '12 at 16:53
  • @AshleyStaggs I think I need a bit more info on what you're trying to do, and about the error you're getting. – Gilbert Oct 06 '12 at 14:58
  • 1
    Everyone, pls. star: http://code.google.com/p/android/issues/detail?id=17689 & link to this post. Maybe it gets included. – Sebastian Roth Oct 17 '12 at 09:47
  • "R.styleable.CustomTextView_android_shadowDx" not able to resolve "CustomTextView_android_shadowDx" .. any suggestions @Gilbert – r4jiv007 Sep 12 '13 at 06:33
  • @r4jiv007 Can you please share the relevant snippets of code? – Gilbert Sep 28 '13 at 10:58
3

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

raukodraug
  • 11,519
  • 4
  • 35
  • 37
  • 1
    This code as written does not work sense setShadowLayer ends up redrawing so you end up in an infinite draw loop. But you can put it in onFocusChanged or set an OnFocusChangeListener on an instance. – miguel Jun 10 '11 at 09:12
  • @miguel agreed. setShadowLayer calls invalidate. – nmw Oct 03 '12 at 09:45
2

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);
    }
}
Roberto B.
  • 3,065
  • 2
  • 18
  • 11