Ответ 1
Я нахожу, что стиль - это прохождение вашего пути через фреймворк. Что (почти всегда) происходит от реализации виджета. Где, я нахожу, повсюду. Я попытаюсь изо всех сил объяснить процесс через ваш конкретный вариант использования - кнопки (-ы) AlertDialog.
Начиная с:
У вас уже есть это: мы начинаем с исходного кода виджета. Мы специально пытаемся найти - где кнопки AlertDialog получают свой текстовый цвет. Итак, мы начинаем с того, чтобы посмотреть, откуда берутся эти кнопки. Являются ли они явно созданы во время выполнения? Или они определены в макете xml, который раздувается?
В исходном коде мы обнаруживаем, что mAlert
обрабатывает параметры кнопки между прочим:
public void setButton(int whichButton, CharSequence text, Message msg) {
mAlert.setButton(whichButton, text, null, msg);
}
mAlert
является экземпляром AlertController
. В его конструкторе мы обнаруживаем, что атрибут alertDialogStyle
определяет макет xml:
TypedArray a = context.obtainStyledAttributes(null,
com.android.internal.R.styleable.AlertDialog,
com.android.internal.R.attr.alertDialogStyle, 0);
mAlertDialogLayout =
a.getResourceId(
com.android.internal.R.styleable.AlertDialog_layout,
com.android.internal.R.layout.alert_dialog);
Итак, макет, на который мы должны обратить внимание, - alert_dialog.xml
- [sdk_folder]/platforms/android-21/data/res/layout/alert_dialog.xml
:
Макет xml довольно длинный. Это важная часть:
<LinearLayout>
....
....
<LinearLayout android:id="@+id/buttonPanel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:minHeight="54dip"
android:orientation="vertical" >
<LinearLayout
style="?android:attr/buttonBarStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingTop="4dip"
android:paddingStart="2dip"
android:paddingEnd="2dip"
android:measureWithLargestChild="true">
<LinearLayout android:id="@+id/leftSpacer"
android:layout_weight="0.25"
android:layout_width="0dip"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone" />
<Button android:id="@+id/button1"
android:layout_width="0dip"
android:layout_gravity="start"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<Button android:id="@+id/button3"
android:layout_width="0dip"
android:layout_gravity="center_horizontal"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<Button android:id="@+id/button2"
android:layout_width="0dip"
android:layout_gravity="end"
android:layout_weight="1"
style="?android:attr/buttonBarButtonStyle"
android:maxLines="2"
android:layout_height="wrap_content" />
<LinearLayout android:id="@+id/rightSpacer"
android:layout_width="0dip"
android:layout_weight="0.25"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:visibility="gone" />
</LinearLayout>
Теперь мы знаем, что кнопки получают стиль, удерживаемый атрибутом buttonBarButtonStyle
.
Перейдите к [sdk_folder]/platforms/android-21/data/res/values/themes.material.xml
и выполните поиск buttonBarButtonStyle
:
<!-- Defined under `<style name="Theme.Material">` -->
<item name="buttonBarButtonStyle">@style/Widget.Material.Button.ButtonBar.AlertDialog</item>
<!-- Defined under `<style name="Theme.Material.Light">` -->
<item name="buttonBarButtonStyle">@style/Widget.Material.Light.Button.ButtonBar.AlertDialog</item>
В зависимости от того, какая тема родительской активности, buttonBarButtonStyle
будет ссылаться на один из этих двух стилей. Пока давайте предположим, что ваша тема активности расширяет Theme.Material
. Мы посмотрим на @style/Widget.Material.Button.ButtonBar.AlertDialog
:
Откройте [sdk_folder]/platforms/android-21/data/res/values/styles_material.xml
и выполните поиск Widget.Material.Button.ButtonBar.AlertDialog
:
<!-- Alert dialog button bar button -->
<style name="Widget.Material.Button.ButtonBar.AlertDialog" parent="Widget.Material.Button.Borderless.Colored">
<item name="minWidth">64dp</item>
<item name="maxLines">2</item>
<item name="minHeight">@dimen/alert_dialog_button_bar_height</item>
</style>
Хорошо. Но эти значения не помогают нам определять цвет текста кнопки. Теперь мы должны посмотреть на родительский стиль - Widget.Material.Button.Borderless.Colored
:
<!-- Colored borderless ink button -->
<style name="Widget.Material.Button.Borderless.Colored">
<item name="textColor">?attr/colorAccent</item>
<item name="stateListAnimator">@anim/disabled_anim_material</item>
</style>
Наконец, мы находим textColor
- и его поставленный attr/colorAccent
, инициализированный в Theme.Material
:
<item name="colorAccent">@color/accent_material_dark</item>
Для Theme.Material.Light
, colorAccent
определяется как:
<item name="colorAccent">@color/accent_material_light</item>
Найдите [sdk_folder]/platforms/android-21/data/res/values/colors_material.xml
и найдите эти цвета:
<color name="accent_material_dark">@color/material_deep_teal_200</color>
<color name="accent_material_light">@color/material_deep_teal_500</color>
<color name="material_deep_teal_200">#ff80cbc4</color>
<color name="material_deep_teal_500">#ff009688</color>
Снимок экрана с AlertDialog и соответствующего текстового цвета:
Shortcut
Иногда его легче читать значение цвета (как показано на рисунке выше) и искать его с помощью AndroidXRef. Этот подход не был бы полезен в вашем случае, так как #80cbc4
указал бы только на его цвет акцента. Вам все равно нужно найти Widget.Material.Button.Borderless.Colored
и привязать его атрибутом buttonBarButtonStyle
.
Изменение текстового цвета кнопки:
В идеале мы должны создать стиль, который расширяет Widget.Material.Button.ButtonBar.AlertDialog
, переопределяет android:textColor
внутри него и присваивает ему атрибут buttonBarButtonStyle
. Но это не сработает - ваш проект не будет компилироваться. Это связано с тем, что Widget.Material.Button.ButtonBar.AlertDialog
является непубличным и, следовательно, не может быть расширен. Вы можете подтвердить это, установив Ссылка.
Мы сделаем следующее: расширьте родительский стиль Widget.Material.Button.ButtonBar.AlertDialog
- Widget.Material.Button.Borderless.Colored
, который является общедоступным.
<style name="CusButtonBarButtonStyle"
parent="@android:style/Widget.Material.Button.Borderless.Colored">
<!-- Yellow -->
<item name="android:textColor">#ffffff00</item>
<!-- From Widget.Material.Button.ButtonBar.AlertDialog -->
<item name="android:minWidth">64dp</item>
<item name="android:maxLines">2</item>
<item name="android:minHeight">@dimen/alert_dialog_button_bar_height</item>
</style>
Обратите внимание, что после переопределения android:textColor
мы добавим еще 3 элемента. Это из непубличного стиля Widget.Material.Button.ButtonBar.AlertDialog
. Поскольку мы не можем распространять его напрямую, мы должны включать в себя элементы, которые он определяет. Примечание: значение (-ы) размера должно быть просмотрено и перенесено в соответствующие res/values(-xxxxx)/dimens.xml
файлы в вашем проекте.
Стиль CusButtonBarButtonStyle
будет присвоен атрибуту buttonBarButtonStyle
. Но вопрос в том, как об этом знает AlertDialog? Из исходного кода:
protected AlertDialog(Context context) {
this(context, resolveDialogTheme(context, 0), true);
}
Передача 0
, поскольку второй аргумент для resolveDialogTheme(Context, int)
закончится в else
:
static int resolveDialogTheme(Context context, int resid) {
if (resid == THEME_TRADITIONAL) {
....
} else {
TypedValue outValue = new TypedValue();
context.getTheme().resolveAttribute(
com.android.internal.R.attr.alertDialogTheme,
outValue, true);
return outValue.resourceId;
}
}
Теперь мы знаем, что тема поддерживается атрибутом alertDialogTheme
. Затем мы рассмотрим, что указывает alertDialogTheme
. Значение этого атрибута будет зависеть от родительской темы вашего действия. Перейдите в папку sdk и найдите values/themes_material.xml
внутри android-21. Найдите alertDialogTheme
. Результаты:
<!-- Defined under `<style name="Theme.Material">` -->
<item name="alertDialogTheme">@style/Theme.Material.Dialog.Alert</item>
<!-- Defined under `<style name="Theme.Material.Light">` -->
<item name="alertDialogTheme">@style/Theme.Material.Light.Dialog.Alert</item>
<!-- Defined under `<style name="Theme.Material.Settings">` -->
<item name="alertDialogTheme">@style/Theme.Material.Settings.Dialog.Alert</item>
Итак, в зависимости от вашей основной темы активности alertDialogTheme
будет удерживать одно из этих 3 значений. Чтобы сообщить AlertDialog о CusButtonBarButtonStyle
, мы должны переопределить атрибут alertDialogTheme
в нашей теме приложения. Скажем, мы используем Theme.Material
в качестве базовой темы.
<style name="AppTheme" parent="android:Theme.Material">
<item name="android:alertDialogTheme">@style/CusAlertDialogTheme</item>
</style>
Сверху мы знаем, что alertDialogTheme
указывает на Theme.Material.Dialog.Alert
, когда ваша базовая тема вашего приложения Theme.Material
. Итак, CusAlertDialogTheme
должен иметь Theme.Material.Dialog.Alert
в качестве родителя:
<style name="CusAlertDialogTheme"
parent="android:Theme.Material.Dialog.Alert">
<item name="android:buttonBarButtonStyle">@style/CusButtonBarButtonStyle</item>
</style>
Результат:
Итак, вместо того, чтобы принимать рыбу, вы можете научить меня рыбе вместо этого?
По крайней мере, я надеюсь объяснить, где находятся рыбы.
P.S. Я понимаю, что я опубликовал мамонта.