AppCompatDialog:无法更改文本颜色

5

我正在努力改变AppCompat DialogFragments的文本颜色。

我的应用程序使用DARK主题(Theme.AppCompat.NoActionBar),但对于对话框,我想要一个LIGHT主题。我使用Build Tools、Support Library和compileSdkVersion 25,是否有影响呢?

我能够更改对话框中的所有其他内容(标题、背景、窗口背景),但无法更改主要和强调文字颜色,它们继续使用黑暗主题的白色设置,导致在白色背景上出现白色文本。

我尝试了许多类似的解决方案,例如在SO中:

1) 简单的方法:在styles.xml文件中:

 <!-- Application theme. -->
<style name="AppTheme" parent="AppBaseTheme">
    <item name="alertDialogTheme">@style/AppCompatAlertDialogStyle</item>
    <item name="android:alertDialogTheme">@style/AppCompatAlertDialogStyle</item>
</style>

<style name="AppCompatAlertDialogStyle" parent="Theme.AppCompat.Light.Dialog.Alert">
    <!-- ignored !!!! -->
    <item name="colorPrimary">#ff0000</item>
    <!-- ignored !!!! -->
    <item name="colorPrimaryDark">#ff0000</item>
    <!-- ignored !!!! -->
    <item name="colorAccent">#ff0000</item>
    <!-- ignored !!!! -->
    <item name="android:textColorPrimary">#F040FF</item>
    <!-- ignored !!!! -->
    <item name="android:textColor">#F040FF</item>

</style>

使用此解决方案,AppCompat.Light.Dialog.Alert的背景和按钮样式已应用,但正如您在屏幕截图中所看到的那样,文本颜色未应用:

enter image description here

2) 在创建AlertDialog时手动指定样式:
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(), R.style.AppCompatAlertDialogStyle);
    LayoutInflater inflater = getActivity().getLayoutInflater();
    View hostView = mHostView = inflater.inflate(layoutId, null);

有同样的问题。背景浅,文字也浅。

3) 使用 ContextWrapper:

    ContextThemeWrapper ctw = new ContextThemeWrapper(getActivity(),  R.style.AppCompatAlertDialogStyle);
    AlertDialog.Builder builder = new AlertDialog.Builder(ctw);

什么都没有 :( 发生了同样的事情

4) 手动指定其他奇特常量 我看到了许多这里SO上的帖子,比如

Theme_DeviceDefault_Light_Dialog_Alert
THEME_DEVICE_DEFAULT_LIGHT

这只是一次不抱有太大希望的尝试,但无论如何文本内容没有改变。

5) 在片段中指定样式而非在对话框中指定

    Dialog_Meta newFragment = new Dialog_Meta();
    newFragment.setStyle(DialogFragment.STYLE_NORMAL, R.style.AppCompatAlertDialogStyle);
    newFragment.show(fragmentManager, TAG);

我曾经在一个非常旧的API版本中使用过这个解决方案,但是我不记得当时出了什么问题,无论如何,它并不能解决当前的问题 :(

有人能告诉我发生了什么吗?


为确保你使用的是android.support.v7.app.AlertDialog,而不是android.app.AlertDialog,对吧? - Mike M.
谢谢你的提示。是的,我正在使用v7.app.AlertDialog! - rupps
顺便问一下,我能否使用(1)中的解决方案 -在styles.xml中指定样式- 并获得(我猜)自动换行的上下文,这样我就不需要在Java部分硬编码样式了? - rupps
1
嗯,我脑海中暂时想不到什么。在我有机会提供答案之前还需要一点时间,所以我会在此期间好好思考。 - Mike M.
1
太棒了!!!!!如此简单,如此优雅!这是最好的解决方案。非常感谢您,先生。 - rupps
显示剩余3条评论
1个回答

2
问题出在你在AlertDialog上设置的自定义View。虽然你已经为AlertDialog总体设置了特定主题,但是该View正在使用Activity的主题进行填充,而且没有被重写的颜色属性值。
有几种解决方法。
• 使用自定义R.style创建ContextThemeWrapper,将其包装在Activity的上下文中,然后获取LayoutInflater.from()。
ContextThemeWrapper ctw = new ContextThemeWrapper(getActivity(), R.style.AppCompatAlertDialogStyle);
LayoutInflater inflater = LayoutInflater.from(getActivity());
View hostView = mHostView = inflater.inflate(layoutId, null);
...

• 如发现者rupps所述,AlertDialog.Builder已经在给定的Context上包装了alertDialogTheme,并且其getContext()方法将返回适当的ContextThemeWrapper,可用于Inflater。

AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
LayoutInflater inflater = LayoutInflater.from(builder.getContext()); // THIS IS THE KEY
View hostView = mHostView = inflater.inflate(layoutId, null);
...

从Google的 AlertDialog.Builder 的 getContext() 方法文档中得知:

/**
* Returns a {@link Context} with the appropriate theme for dialogs created by this
* Builder.
* Applications should use this Context for obtaining LayoutInflaters for inflating views
* that will be used in the resulting dialogs, as it will cause views to be inflated with
* the correct theme.
*
* @return A Context for built Dialogs.
*/
public Context getContext() {
    ...

• 主题可以在Dialog的布局的根View上设置android:theme属性。

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:theme="@style/AppCompatAlertDialogStyle">
    ...

• 不需要自己处理布局的膨胀,可以在BuildersetView()调用中传递布局的ID,并使用alertDialogTheme进行膨胀。

然而,使用这种方法,直到显示Dialog时才能使用来自布局的View对象。在DialogFragment中,这将在onStart()方法中发生。

@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
    AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
    builder.setView(R.layout.dialog);
    return builder.create();
}

@Override
public void onStart() {
    super.onStart();

    final Dialog dialog = getDialog();
    dialog.findViewById(R.id.dialog_button).setOnClickListener(...);
    ...
}

一个有趣的副作用是,这个视图(及其子孙)的getContext()将返回一个包装原始上下文的ContextThemeWrapper。我之所以发现它,是因为我在一些点击处理程序中将上下文转换为“Activity”。因此,在我的情况下,我必须在某些地方使用getBaseContext()来获取原始上下文。 - rupps
是的,那很有道理。你确定需要从那些“View”中使用“Context”吗?在你的“DialogFragment”类中,你应该随时可以访问getActivity() - Mike M.
1
是的,你说得对...问题是,我的DialogFragment是一个通用的视图包装器,有时会在对话框内使用,但在更大屏幕的情况下也会在普通布局中使用。不管怎样,这是一个边缘情况,有一个简单的解决方案。不过还是谢谢你的提示! - rupps
啊,非常好。我甚至没有想到那个。它已经为你包装好了。不错。 - Mike M.

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