改变NumberPicker的文本颜色

63

我已经查看了大部分与此相关的帖子,但没有提供有效答案。样式化NumberPicker不起作用(如此线程所述:NumberPicker textColour

将style属性设置为具有颜色项的样式对numberPicker也没有任何影响。在numberPicker XML上设置textColor属性也无效。

我最接近的方法是使用numberPicker将其getChildAt()转换为EditText,然后在该EditText上进行setColor(),但这仅在初始化时更改子项的颜色一次,然后每次从那里选择它时都更改;这也不是我想要的。

有什么帮助吗?谢谢

11个回答

106

我尝试并成功的解决方法是:

在styles.xml中添加:

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

然后在你的布局中像这样使用它:

  <NumberPicker
    android:id="@+id/dialogPicker"
    android:theme="@style/AppTheme.Picker"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_marginTop="15dp" />

2
这是最简单和最好的答案。+1 - smoothBlue
1
绝对是最简单的解决方案!这也很容易与我的自定义数字选择器相结合,我使用了主题@android:style/Theme.Dialog来显示+和-按钮,而不是滑块效果。现在,我只需将父级定义为我的先前主题(而不是Theme.AppCompat.Light.NoActionBar),它就神奇地结合起来工作了。 - P Kuijpers
这是最佳答案。我希望我能够给予超过+1的赞成票。 - Mike
3
@z3n105 你知道如何更改选中颜色的属性吗? - Neon Warge
3
这个答案会改变数字选择器中所有字段的颜色,我的意思是未选中字段的颜色也会改变。但是如果我只想改变所选字段呢?! - MMG
可能有点晚了,但如果有人找不到styles.xml文件: 这些样式已经迁移到values\themes.xml中(包括白天和黑夜的可选主题)。 - Malte Ge

74

这段代码应该可以解决你的问题。你遇到的问题是因为在构建NumberPicker时,它会获取EditText的textColor并将其分配给画笔,以便可以用相同的颜色在编辑文本上方和下方绘制数字。

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

4
对我很有用。你拯救了我,让我没有跳楼的想法。 - Amit Gupta
14
你只需要在你的主题中重写 textColorPrimary,无需使用反射API。请注意不要改变原意,并保持通俗易懂。 - Vikram
2
同意。但必须指出,这也会改变您整个应用程序中的文本颜色,而不仅仅是日期时间选择器,这可能不适合。问题特定于NumberPicker。 - Simon
2
@Simon 由于某些原因,我错过了你的回复!我猜你忘记在评论中添加(at)Vikram了。你说:“..这也会改变整个应用程序的文本颜色..”。好吧,我们可以使用ContextThemeWrapper来创建LayoutInflater,以充气date/time/numberpicker。这样,我们重写的textColorPrimary仅影响选择器,使其他所有内容保持不变。但是,我确实意识到你的解决方案也是可以的。我在某个地方读到过:反射是利用内省在运行时进行修改的能力。因此,你对反射的使用是合理的。 - Vikram
3
我不喜欢给回答点踩,这个回答虽然付出了很多努力并且也可以用,但是与下面@Vikram的回答相比要差得多,所以我会给它点踩,因为我认为对社区来说, Vikram的回答更有帮助。 - Carl Manaster
显示剩余12条评论

40

我不确定为什么你需要深入了解Java Reflection API。这只是一个简单的样式问题。你需要覆盖的属性是:textColorPrimary

<style name="AppTheme" parent="@android:style/Theme.Holo.Light">
    ....
    <item name="android:textColorPrimary">#ff0000</item>
</style>
如果您在 Dialog 中使用 TimePicker,请在对话框的主题中覆盖 android:textColorPrimary
就是这样。 其他信息: 下面是Yoann Hercouet评论的见解:

这个解决方案不仅会改变 NumberPicker 上的颜色,它是对 A LOT 组件的全局更改

这是正确的,但它忽略了我所暗示的可能性。此外,“全局”意味着影响整个应用。可以通过仅将此主题应用于包含 NumberPicker 的活动来将其限制为活动范围。但是,我同意,这仍然可能过于“腐蚀性”。
这里的想法是以某种方式将 textColorPrimary = INTENDED_COLOR 注入到 NumberPicker 将看到的主题中。有多种方法可以实现这一点。以下是其中一种方法:
res/values/styles.xml 中定义一个基础风格:
<style name="NumberPickerTextColorStyle">
    <item name="android:textColorPrimary">@color/intended_color</item>
</style>

现在,创建一个自定义的 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);
    }
}

最后,在您的布局中使用 ThemedNumberPicker

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

我们已成功控制textColorPrimary=INTENDED_COLOR对我们应用程序的影响。

当然,这只是其中一种选择。例如,如果您正在填充一个包含NumberPicker的布局,则可以使用:

// In this case, the layout contains <NumberPicker... />, not <ThemedNumberPicker... />
LayoutInflater.from(new ContextThemeWrapper(context, R.style.NumberPickerTextColorStyle))
    .inflate(R.layout.number_picker_layout, ...);

6
更好的方法。 - Chad Bingham
@Milad 如果你能提供/发布关于你的应用程序设置的更多细节,我可能可以帮助你解决问题。 - Vikram
也不适用于我。但是使用这种方法可以解决问题:http://stackoverflow.com/questions/13882707/need-help-by-creating-a-dialog-with-2-numberpickers - Henrique de Sousa
这个解决方案不仅仅改变了NumberPicker的颜色,它是一个全局性的改变,将会影响到许多组件。 - Yoann Hercouet
1
@YoannHercouet 谢谢你指出这一点。让我意识到我的回答可能没有我想象的那么清晰。请检查我所做的“编辑”。 - Vikram
显示剩余2条评论

8

以下是来自上面答案的Xamarin片段,包括TextSize和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;
    }

ComplexUnitType类未找到。 - Rahul Rastogi
你在哪里调用它?是从自定义渲染器中吗? - testing
我在NumberPicker的代码中找不到addView - Eduardo Reis
2
非常感谢!我已经寻找了一段时间。如果有人正在使用Xamarin.Android,您可以将此片段放入MainActivity或Fragment中。您可以通过FindViewById获取您的NumberPicker,并将其传递给该方法。 - Nicolás de Ory

8

对于我来说,在我的主题中设置android:textColorPrimary并没有起作用,查看了NumberPicker的源代码后发现它根据EditText输入决定文本颜色,因此需要设置android:editTextColor

<style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
    <item name="android:editTextColor">@color/dark_gray</item>
</style>

我相信Dinidiniz已经指出了这一点。 - ToolmakerSteve

5

不要改变每个文本的颜色以达到你想要的效果,最好只改变所有编辑框 editText 的颜色。实际上,NumberPicker 有一个子 EditText 显示数字。

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

这对我很有用。尽管我的按钮上有白色文本,但它们并没有改变。


你好,我想只更改选定的EditText颜色,你的代码可以实现吗? - MMG

4

基于反射拒绝在 Android SDK >= 29 上,最好修改 Simon 的答案:

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

在SDK版本大于等于29时,NumberPicker有一个名为setTextColor()的方法。


警告:反射访问 mSelectionDivider,该属性不属于公共 SDK,因此可能会在未来的 Android 版本中发生更改。 - Sadique Khan

2

我采用了@Andreas Merz的解决方案并更新了他的代码。他使用的赋值方式和函数签名/调用未找到。我正在使用min API 19。以下是对我有效的代码。

/**
 * Based on https://dev59.com/kGAg5IYBdhLWcg3w9fDm#26657169
 * @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();
            }
        }
    }
}

注意:Andreas的代码是为Xamarin Android编写的。而这个答案是针对“普通”的Android代码(Java)的,就像原始问题一样。与被接受的答案相比,它增加了字体和字号的设置。 - ToolmakerSteve

1
接受的答案过于复杂。对我有效的更简单的方法是覆盖我正在使用的主题的textColorPrimary属性。
<style name="Theme.MyTheme" parent="@android:style/Theme.Holo.NoActionBar.Fullscreen" >
        <item name="android:textColorPrimary">#000000</item>
</style>

它完成了工作,效果相当好!

1
对于Kotlin用户,可以使用这个扩展函数(基于@Tapa Save的答案)。
fun NumberPicker.changeTextColor(color: Int) {
    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
        val count: Int = childCount
        for (i in 0 until count) {
            val child: View = getChildAt(i)
            if (child is EditText) {
                try {
                    child.setTextColor(color)
                    invalidate()
                    val selectorWheelPaintField: Field = this.javaClass.getDeclaredField("mSelectorWheelPaint")
                    var accessible: Boolean = selectorWheelPaintField.isAccessible
                    selectorWheelPaintField.isAccessible = true
                    (selectorWheelPaintField.get(this) as Paint).color = color
                    selectorWheelPaintField.isAccessible = accessible
                    invalidate()
                    val selectionDividerField: Field = this.javaClass.getDeclaredField("mSelectionDivider")
                    accessible = selectionDividerField.isAccessible()
                    selectionDividerField.isAccessible = true
                    selectionDividerField.set(this, null)
                    selectionDividerField.isAccessible = accessible
                    invalidate()
                } catch (ignore: Exception) { }
            }
        }
    } else {
        textColor = color
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接