AppCompatDialog:无法更改文本颜色

AppCompatDialog: Impossible to change Text Color

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

我的应用程序使用 DARK 主题 (Theme.AppCompat.NoActionBar),但对于对话框我想要一个 LIGHT 主题。我正在使用 Build Tools、Support Library 和 compileSdkVersion 到 25,这很重要。

我可以更改对话框中的其他所有内容(标题、背景、window 背景)但不能更改继续使用的主要和重音文本颜色深色主题的(白色)设置,导致白色背景上的白色文本。

我在 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 中的背景和按钮样式,但未应用您在屏幕截图中看到的文本颜色:

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) 手动指定其他奇异常数

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 版本中使用过这个解决方案,不记得是什么问题了,但无论如何,没有解决现在的问题:(

谁能告诉我这是怎么回事?

这里的问题是您在 AlertDialog 上设置的自定义 View。虽然您通常为 AlertDialog 设置了某个主题,但 View 正在被 Activity 的主题夸大,它没有那些覆盖的颜色属性值。

有几种方法可以解决这个问题。

• 在 ActivityContext 周围用自定义 R.style 创建一个 ContextThemeWrapper,并获得 LayoutInflater.from() 那。

ContextThemeWrapper ctw = new ContextThemeWrapper(getActivity(), R.style.AppCompatAlertDialogStyle);
LayoutInflater inflater = LayoutInflater.from(getActivity());
View hostView = mHostView = inflater.inflate(layoutId, null);
...

• 正如 OP rupps 所发现的那样,AlertDialog.Builder 已经将 alertDialogTheme 包裹在给定的 Context 上,并且其 getContext() 方法将 return 适当的 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.BuildergetContext() 方法的文档:

/**
* 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">
    ...

• 不用自己处理 inflation,布局的 ID 可以在 BuildersetView() 调用中传递,它会随着 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(...);
    ...
}