55

I've looked at most all of the threads on this and none provided an answer that works. Styling the NumberPicker does not work (as per this thread: NumberPicker textColour)

Setting the style attribute on the numberPicker to a style that has a color item does not have any effect either. Nor does setting the textColor attribute on the numberPicker XML do anything.

Closest I've got to this is using the numberPicker to cast its getChildAt() to an EditText and then do setColor() on that EditText, but that only changes the color of the child once upon initialization and then every time it is selected from thereon; not what I am looking for either.

Any help? Thanks

Community
  • 1
  • 1
Odaym
  • 1,777
  • 1
  • 18
  • 31

10 Answers10

86

The solution I tried and worked for me is:

In styles.xml add:

<style name="AppTheme.Picker" parent="Theme.AppCompat.Light.NoActionBar" >
    <item name="android:textColorPrimary">@android:color/black</item>
</style>

Then use it like this inside your layout:

  <NumberPicker
    android:id="@+id/dialogPicker"
    android:theme="@style/AppTheme.Picker"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="15dp" />
z3n105
  • 1,630
  • 11
  • 13
  • 1
    This is the simplest and best answer overall. +1 – smoothBlue Mar 20 '16 at 06:34
  • 1
    Definately the simplest solution! This is also easy to combine with my custom numberpicker, for which used the theme @android:style/Theme.Dialog to show + and - buttons instead of the slider effect. Now I simply defined the parent as my previous theme (instead of Theme.AppCompat.Light.NoActionBar), and it magically works combined. – P Kuijpers Jul 29 '16 at 09:46
  • This is the best answer. I wish I could up-vote more then +1. – Mike Jan 02 '18 at 05:27
  • 3
    @z3n105 do you know the property for changing the selection color? – Neon Warge Aug 25 '18 at 14:18
  • 1
    This answer changes color of all fields of number picker, I mean color of unselected fields will change, too. But what if I want to change just selected field?! – MMG Sep 12 '20 at 11:48
74

This code should solve your problem. The problem you are experiencing is because during the construction of NumberPicker it captures the EditText textColor and assigns to to a paint so it can draw the numbers above and below the edit text with the same color.

import java.lang.reflect.Field;

public static void setNumberPickerTextColor(NumberPicker numberPicker, int color)
{

    try{
        Field selectorWheelPaintField = numberPicker.getClass()
            .getDeclaredField("mSelectorWheelPaint");
        selectorWheelPaintField.setAccessible(true);
        ((Paint)selectorWheelPaintField.get(numberPicker)).setColor(color);
    }
    catch(NoSuchFieldException e){
        Log.w("setNumberPickerTextColor", e);
    }
    catch(IllegalAccessException e){
        Log.w("setNumberPickerTextColor", e);
    }
    catch(IllegalArgumentException e){
        Log.w("setNumberPickerTextColor", e);
    }

    final int count = numberPicker.getChildCount();
    for(int i = 0; i < count; i++){
        View child = numberPicker.getChildAt(i);
        if(child instanceof EditText)
            ((EditText)child).setTextColor(color);
    }
    numberPicker.invalidate();  
}
Borzh
  • 4,450
  • 2
  • 41
  • 58
Simon
  • 9,933
  • 44
  • 46
  • 2
    This worked! even with net.simonvt.numberpicker ! It made me crazy that no matter what style I specified, I kept ending up with black text! (black text on black background = useless) – Someone Somewhere Apr 28 '14 at 05:31
  • Thanks much for this useful snippet! +1. – ninehundreds Aug 21 '14 at 01:46
  • 4
    Worked for me. You saved me from jumping off my apartment. – Amit Gupta Feb 03 '15 at 13:41
  • 14
    All you need to do is override `textColorPrimary` in your theme. Calling on Reflection API is unnecessary in this case. – Vikram Mar 28 '15 at 08:15
  • 2
    Agreed. However it must be said that this also changes the text color in your entire app and not just the date time picker which may not suite. The question is specific to NumberPicker. – Simon Mar 28 '15 at 08:21
  • 1
    @Vikram, to be honest all it **should** have required is `android:textColor="@android:color/white"` – Someone Somewhere Aug 09 '15 at 21:27
  • 2
    @Simon For some reason I missed your reply! I guess you forgot to add `(at)Vikram` to your comment. You said: `..this also changes the text color in your entire app..`. Well, we can use `ContextThemeWrapper` to create the `LayoutInflater` that inflates the `date/time/numberpicker`. This way, our overridden `textColorPrimary` affects only the picker, leaving everything else intact. BUT, I do realize that your solution is alright too. I read this somewhere: *Reflection is the ability to make modifications at runtime by making use of introspection*. So, your usage of reflection is well justified. – Vikram Sep 05 '15 at 16:33
  • @SomeoneSomewhere Eccentricities of the little green robot. – Vikram Sep 05 '15 at 16:34
  • 3
    I don't like to downvote, and this answer shows good effort, and works, but it is so much worse than @Vikram's answer below that I am downvoting, because I think the community will be better served by Vikram's answer. – Carl Manaster Oct 08 '15 at 00:13
  • Awesome! great solution! – Ciro Mine Feb 19 '16 at 18:19
  • If there is a user interaction, the color changes to the default one. @Simon – Sahana Prabhakar Jan 18 '18 at 07:06
  • @SomeoneSomewhere tried to implement the above code but it is not working for me can you please tell me Where I did the mistake https://stackoverflow.com/questions/50258539/how-to-change-the-timepicker-color – Nikson May 09 '18 at 17:04
  • are you getting a "NoSuchFieldException"? Since this code snippet is a while old, I presume that Google has changed the layout and changed the name of the field. It might be better to give Vikram's answer a go, see https://stackoverflow.com/a/29314989/550471 – Someone Somewhere May 09 '18 at 17:26
  • I don't believe this is the best answer. I believe the best answer now is by z3n105 – Hashim Akhtar Dec 19 '18 at 20:38
  • 1
    I tried this but not successful. If you log the getChildCount value it always returns 1. If you change the colour you find out that the one child is the selected value and it shows this in the layout inspector as well. Is it the normal NumberPicker that was being used? I also noticed that when the value changed the colour was also lost but returns if you tap on the selected value. – Clinton Sep 19 '19 at 06:17
  • 1
    check z3n105 answer, this is a better solution as android changes this method and you can not access to this method via reflection now – Masoud Darzi Apr 28 '20 at 09:51
  • @Simon Thanks for the answer. It changes color of selected field first time number picker is shown. But if I click on number picker or scroll it, colors will return to default color. What is the problem?! – MMG Sep 13 '20 at 03:00
37

Not sure why you would need to dive into Java Reflection API for this. Its a simple styling matter. The attribute that you need to override is: textColorPrimary.

<style name="AppTheme" parent="@android:style/Theme.Holo.Light">
    ....
    <item name="android:textColorPrimary">#ff0000</item>
</style>

If you're using the TimePicker inside a Dialog, override android:textColorPrimary in the dialog's theme.

That's about it.

Additional info:

Here's an insightful comment by Yoann Hercouet:

This solution does not change only the color on the NumberPicker, it is a global change that will impact A LOT of components

This is correct, but it overlooks the possibilities I am hinting at. Moreover, global implies app-wide impact. That can be limited to activity-scope by applying this theme only to activities containing the NumberPicker. But, I agree, this may still be too corrosive.

The idea here is to somehow inject textColorPrimary=INTENDED_COLOR into the theme that will be seen by NumberPicker. There are multiple ways to achieve this. Here's one way:

Define a bare-bone style in res/values/styles.xml:

<style name="NumberPickerTextColorStyle">
    <item name="android:textColorPrimary">@color/intended_color</item>
</style>

Now, create a custom NumberPicker:

public class ThemedNumberPicker extends NumberPicker {

    public ThemedNumberPicker(Context context) {
        this(context, null);
    }

    public ThemedNumberPicker(Context context, AttributeSet attrs) {
        // wrap the current context in the style we defined before
        super(new ContextThemeWrapper(context, R.style.NumberPickerTextColorStyle), attrs);
    }
}

Finally, use ThemedNumberPicker in your layout(s):

<package.name.ThemedNumberPicker
    android:id="@+id/numberPicker"
    ....
    ....
    .... />

We have successfully contained the impact that textColorPrimary=INTENDED_COLOR has on our app.

This is of course just one option. For example, if you were inflating a layout containing a NumberPicker, you could use:

// In this case, the layout contains <NumberPicker... />, not <ThemedNumberPicker... />
LayoutInflater.from(new ContextThemeWrapper(context, R.style.NumberPickerTextColorStyle))
    .inflate(R.layout.number_picker_layout, ...);
Community
  • 1
  • 1
Vikram
  • 49,828
  • 11
  • 86
  • 118
  • 6
    A much better approach. – Chad Bingham Jun 22 '15 at 17:29
  • @Milad If you can give/post some more details about your app setup, I can probably help you out. – Vikram Nov 18 '15 at 21:47
  • doesn't work on me either. But using this method worked: http://stackoverflow.com/questions/13882707/need-help-by-creating-a-dialog-with-2-numberpickers – Henrique de Sousa Nov 28 '15 at 18:44
  • @HenriquedeSousa Like I said to user Milad - if you can post your style setup, I can probably help you out. Moreover, the answer you linked to doesn't make sense (unfortunately). See my comment on that answer. – Vikram Nov 30 '15 at 05:15
  • It had to do with using the right LayoutInflater. The one from SystemService would not work. – Henrique de Sousa Nov 30 '15 at 10:58
  • This solution does not change only the color on the NumberPicker, it is a global change that will impact A LOT of components. – Yoann Hercouet Jan 23 '16 at 15:24
  • 1
    @YoannHercouet Thank you for pointing this out. Made me realise that my answer may not be as clear as I had thought. Please check the `edit` I've made to it. – Vikram Jan 30 '16 at 19:46
8

Here is a Xamarin Snippet from the answer above with TextSize and TextStyle Bold

public static bool SetNumberPickerTextColorAndSize(NumberPicker numberPicker, Color color, ComplexUnitType complexUnitType, float textSize, TypefaceStyle style)
    {
        int count = numberPicker.ChildCount;
        for (int i = 0; i < count; i++)
        {
            View child = numberPicker.GetChildAt(i);
            if (child.GetType() == typeof(EditText))
            {
                try
                {
                    Field selectorWheelPaintField = numberPicker.Class
                                                                .GetDeclaredField("mSelectorWheelPaint");
                    selectorWheelPaintField.Accessible = true;

                    EditText editText = (EditText) child;
                    editText.SetTextSize(complexUnitType, textSize);
                    editText.SetTypeface(editText.Typeface, style);
                    editText.SetTextColor(color);

                    Paint paint = (Paint) selectorWheelPaintField.Get(numberPicker);
                    paint.TextSize =  TypedValue.ApplyDimension(complexUnitType, textSize, numberPicker.Resources.DisplayMetrics);
                    paint.Color = color;
                    paint.SetTypeface(editText.Typeface);

                    numberPicker.Invalidate();
                    return true;
                }
                catch (NoSuchFieldException e)
                {
                    Log.Warn("setNumberPickerTextColor", e);
                }
                catch (IllegalAccessException e)
                {
                    Log.Warn("setNumberPickerTextColor", e);
                }
                catch (IllegalArgumentException e)
                {
                    Log.Warn("setNumberPickerTextColor", e);
                }
            }
        }
        return false;
    }
Andreas Merz
  • 89
  • 1
  • 2
6

For me setting android:textColorPrimary in my theme did nothing, looking at the source code for the NumberPicker it decides the text color from the EditText input thus one need to set the android:editTextColor instead.

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:editTextColor">@color/dark_gray</item>
</style>
Arbitur
  • 36,899
  • 22
  • 86
  • 123
5

Instead of changing every text color to the color you want, better just changing all editText color. NumberPicker actually has a child EditText that display the numbers.

<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat.NoActionBar">
        <!-- Change edit color here. -->
        <item name="android:editTextColor">#000000</item>
</style>

This worked for me. And although I have white text in the buttons, they havent changed.

Dinidiniz
  • 571
  • 6
  • 13
4

Based on reflection reject on Android SDK >= 29 better to modify Simon's answer:

public void setNumberPickerTextColor(NumberPicker numberPicker, int color){

    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {

        final int count = numberPicker.getChildCount();
        for (int i = 0; i < count; i++) {
            View child = numberPicker.getChildAt(i);
            if (child instanceof EditText) {
                try {
                    ((EditText) child).setTextColor(color);
                    numberPicker.invalidate();

                    Field selectorWheelPaintField = numberPicker.getClass().getDeclaredField("mSelectorWheelPaint");
                    boolean accessible = selectorWheelPaintField.isAccessible();
                    selectorWheelPaintField.setAccessible(true);
                    ((Paint) selectorWheelPaintField.get(numberPicker)).setColor(color);
                    selectorWheelPaintField.setAccessible(accessible);
                    numberPicker.invalidate();

                    Field selectionDividerField = numberPicker.getClass().getDeclaredField("mSelectionDivider");
                    accessible = selectionDividerField.isAccessible();
                    selectionDividerField.setAccessible(true);
                    selectionDividerField.set(numberPicker, null);
                    selectionDividerField.setAccessible(accessible);
                    numberPicker.invalidate();
                } catch (Exception exception) {
                    Logger.exc(exception);
                }
            }
        }
    } else {

        numberPicker.setTextColor(color);
    }
}

In SDK >= 29 NumberPicker have .setTextColor() method.

Tapa Save
  • 4,276
  • 4
  • 30
  • 52
2

I took the solution of @Andreas Merz and updated his code. The way things were assigned and the functions signatures/calls he used were not found. I am using min API 19. Here is the code that worked for me.

/**
 * Based on https://stackoverflow.com/a/26657169/2313889
 * @param picker
 * @param color
 * @param unit
 * @param textSize
 * @param typeface
 * @return
 */
private void formatNumberPickerText(NumberPicker picker, int color,
                                    int unit, float textSize,
                                    Typeface typeface) {
    int count = picker.getChildCount();
    for (int i = 0; i < count; i++) {
        View child = picker.getChildAt(i);
        if (child instanceof EditText) {
            try {
                Class clazz = picker.getClass();
                Field field = clazz.getDeclaredField("mSelectorWheelPaint");
                field.setAccessible(true);

                EditText editText = (EditText) child;
                editText.setTextSize(unit, textSize);
                editText.setTypeface(typeface);
                editText.setTextColor(color);

                Paint paint = (Paint) field.get(picker);
                paint.setTextSize(TypedValue.applyDimension(
                        unit, textSize, getResources().getDisplayMetrics()
                ));
                paint.setColor(color);
                paint.setTypeface(typeface);

                picker.invalidate();
                return;

            } catch (IllegalAccessException | NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    }
}
Eduardo Reis
  • 1,068
  • 12
  • 29
  • NOTE: Andreas code was for *Xamarin* Android. This answer is for "normal" Android code (Java), as was the original question. In comparison to the accepted answer, it adds setting of typeface and font size. – ToolmakerSteve Nov 30 '19 at 00:25
1

The accepted answer is overly complicated. A much simpler approach that worked for me was to override the: textColorPrimary attribute of the theme I was using.

<style name="Theme.MyTheme" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
        <item name="android:textColorPrimary">#000000</item>
</style>

It did the job quite well!

mgm
  • 1,238
  • 13
  • 16
-1

It's easy with my NumberPicker library.

<com.github.tomeees.scrollpicker.ScrollPicker
    ...
    app:textColor="..."
    />