我发现样式设计就是通过框架进行调试。 “什么”(几乎总是)来自小部件的实现。而“何处”,我发现到处都有。 我将尽力通过您特定的用例 - 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
:
<!
<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的截图和相应的文本颜色:
![enter image description here](https://istack.dev59.com/7RAEC.webp)
快捷方式:
有时,阅读颜色值(如上图)并使用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
。由于我们无法直接扩展它,因此必须包含它定义的项目。注意:必须查找
dimen值并将其转移到适当的
res/values(-xxxxx)/dimens.xml
文件中。
属性
buttonBarButtonStyle
将被分配为样式
CusButtonBarButtonStyle
。但问题是,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文件夹并找到android-21中的values/themes_material.xml
。搜索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
将拥有以下三个值之一。为了让 AlertDialog 知道 CusButtonBarButtonStyle
,我们需要在应用程序的主题中覆盖属性 alertDialogTheme
。比如说,我们使用 Theme.Material
作为基础主题。
<style name="AppTheme" parent="android:Theme.Material">
<item name="android:alertDialogTheme">@style/CusAlertDialogTheme</item>
</style>
从上文可以得知,当你的应用程序的基本主题是Theme.Material
时,alertDialogTheme
指向Theme.Material.Dialog.Alert
。因此,CusAlertDialogTheme
的父级应该是Theme.Material.Dialog.Alert
:
<style name="CusAlertDialogTheme"
parent="android:Theme.Material.Dialog.Alert">
<item name="android:buttonBarButtonStyle">@style/CusButtonBarButtonStyle</item>
</style>
结果:
![在此输入图片描述](https://istack.dev59.com/JKbgN.webp)
所以,与其接受鱼来吃,你能教我如何捕鱼吗?
至少,我希望已经解释了鱼在哪里。
P.S. 我意识到我发了一篇巨大的文章。