如何*动态*改变每种颜色 iOS/Android?例如。在 Droid 代码中,覆盖 "Styles.xml" 中的 "colorAccent"
How *dynamically* change EVERY color iOS/Android? E.g. in Droid code, override "colorAccent" in "Styles.xml"
我正在向 Xamarin Forms 应用添加颜色主题。
主题被指定为动态变化,具体取决于用户所站的客户端位置 at/near。我们有 "light" 和 "dark" 主题客户,有多种调色板;因此需要能够更改 all 显示项目的所有颜色。动态地。
到目前为止,我的动态主题适用于几乎所有类型的视图。
在 Android 上为 iOS 样式(垂直滚轮)Xamarin.Forms.Picker
着色时,我 卡在两个细节上 :
问题一:如何改变背景颜色?
问题 2:如何更改 "Cancel" 按钮的文字颜色?
这个基于 Android NumberPicker
的 XF 视图,似乎不是开箱即用的完全可自定义颜色。从最新的 XF 开始,4.3.0.991211.
----- 我试过的:-----
一个。在 Style.xml
资源文件中设置 Android 主题颜色 静态 。成功。但它是静态的。
乙。下载 Xamarin Forms Light/Dark Theme Sample.
在 Android 上构建并 运行。转到其 "Select Theme" 选择器。选择浅色主题。再次转到选择器 - 白色背景,到目前为止,一切都很好。选择深色主题。再次转到选择器 - 选择器仍然是黑白的。这里没有解决方案。
C。在网上找到如何使用 AlertDialog "builder" 动态更改 AlertDialog
s "OK"/"Cancel" 按钮文本颜色,并获取资源 ID。成功实施了。不确定如何将相同的技术应用于 Picker。
问题3:如何发现XF Picker使用的Android控件中使用了哪些资源ID?
D.克隆 Xamarin Forms 源代码。将 Droid PickerRenderer 复制到我的代码中。大部分代码不能运行,因为它依赖于internal
classes/functions。让我的继承自内置的,并注释掉任何不需要/不会编译的代码。
自定义 OnElementChanged
,因为那是设置 TextColor
的地方。
在自定义渲染器源代码中,IPickerRenderer.OnClick()
构建一个包含 NumberPicker
的 AlertDialog
。我没有看到任何设置背景颜色或 "Cancel" 按钮文本颜色的逻辑。但至少我们知道了Android类。 参见上面的问题 3。
E.至少 having a small number of predefined themes might be a solution. Or Android theme.ApplyStyle - 但这可以是 dynamic 风格吗?我还没有测试过这些是否成功地改变了我所坚持的细节。 [我试过后会回来报告的。]
F. 重新启动应用程序 可以更改主题:any-color themes。这是我最后的选择;首先看看我是否可以在 C# 中完全控制颜色,而无需重新启动。
注意:SO 和 Xamarin.Forums 上有很多帖子一般 讨论 Xamarin Forms 中的主题。我已经过了那个点,所以我不会在这里列出这些,除了提到 Clint St.Laurent's theming post,它(快速浏览一下)看起来与我所做的相似。 在我的测试中,我只剩下少量微小的细节 - Xamarin Forms 无法将主题颜色应用于这些细节。
G。其他看起来相关的 SO 帖子 - 但不执行问题 1 和 2 中列出的确切任务:
。显示自定义 PickerRenderer。但没有显示如何在 Q 1 和 2 中设置颜色。
另一个。同样。
How to really change primary and accent color in android lollipop - 我在 E
和 F
中提到了答案。如果我无法在自定义 PickerRenderer 中通过 C# 代码实现结果,我将调查这些。
Xamarin 论坛帖子:
How to change the colorAccent at runtime?。给出的唯一答案不会改变主题 colorAccent
。它只是改变了应用栏的颜色。
澄清:有大量文档、论坛和博客文章描述了 Xamarin Forms 主题化的一般机制。不是在寻找答案。我只是想解决 少量缺失的细节 ,截至最新的稳定 Xamarin Forms,4.3.0.991211。 希望 Xamarin Forms 成功地为所有类型控件的 100% 细节设置主题的那一天会到来——到那时这个问答将过时。
问题中 D.
的实现。在代码中作为注释给出的学分。
此 自定义 Android PickerRenderer 动态设置 3 种颜色:文本(蓝色)、"wheel" 文本后面的背景(浅橙色)、按钮(暗红色)。
澄清:static Android 主题有深色背景。目标是动态地从深色主题更改为浅色主题。为了使已成功设置的颜色非常明显,使用了强烈着色的颜色。
文字之间的白色"dividers"我还没有上色。我在 Xamarin 或 Android 文档中看到了分隔线可绘制对象的提及。抓住它并设置它的颜色应该很简单。 编辑:可能 titleDivider
在 Change picker control pop-up background and text colors, or in . OR selectionDividerField
in Tapa Save's answer.
中的 Tiago Flores 代码片段中
注意:我没有添加自定义 XF 属性 来控制按钮颜色;硬编码为 Color.Accent
.
using Android.App;
using Android.Util;
using Android.Views;
using Android.Widget;
using System;
using System.ComponentModel;
using System.Linq;
using Orientation = Android.Widget.Orientation;
using Android.Content;
using Android.Text;
using Android.Text.Style;
using Java.Lang;
// PickerEditText
using Xamarin.Forms.Platform.Android;
using AColor = Android.Graphics.Color;
using Picker = Xamarin.Forms.Picker;
[assembly: Xamarin.Forms.ExportRenderer(typeof(Picker), typeof(LCPickerRenderer))]
namespace Xamarin.Forms.Platform.Android
{
// Based on Xamarin.Forms source code, modified.
public class LCPickerRenderer : PickerRenderer, IPickerRenderer //ViewRenderer<Picker, EditText>, IPickerRenderer
{
AlertDialog _dialog;
NumberPicker _picker;
public LCPickerRenderer(Context context) : base(context)
{
//AutoPackage = false;
}
[Obsolete("This constructor is obsolete as of version 2.5. Please use PickerRenderer(Context) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public LCPickerRenderer()
{
//AutoPackage = false;
}
IElementController ElementController => Element as IElementController;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
if (e.OldElement != null) {
//((INotifyCollectionChanged)e.OldElement.Items).CollectionChanged -= RowsCollectionChanged;
}
base.OnElementChanged(e);
if (e.NewElement != null) {
// --- custom work ---
if (Control != null) {
SetTextColor();
SetBackgroundColor();
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Picker.BackgroundColorProperty.PropertyName) {
SetBackgroundColor();
} else if (e.PropertyName == Picker.TextColorProperty.PropertyName) {
SetTextColor();
}
//if (e.PropertyName == Picker.TitleProperty.PropertyName || e.PropertyName == Picker.TitleColorProperty.PropertyName)
// UpdatePicker();
//else if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
// UpdatePicker();
//else if (e.PropertyName == Picker.CharacterSpacingProperty.PropertyName)
// UpdateCharacterSpacing();
//else if (e.PropertyName == Picker.TextColorProperty.PropertyName)
// UpdateTextColor();
//else if (e.PropertyName == Picker.FontAttributesProperty.PropertyName || e.PropertyName == Picker.FontFamilyProperty.PropertyName || e.PropertyName == Picker.FontSizeProperty.PropertyName)
// UpdateFont();
}
void IPickerRenderer.OnClick()
{
Picker model = Element;
if (_dialog != null)
return;
var xfTextColor = Element.TextColor;
_picker = new NumberPicker(Context);
if (model.Items != null && model.Items.Any()) {
_picker.MaxValue = model.Items.Count - 1;
_picker.MinValue = 0;
_picker.SetDisplayedValues(model.Items.ToArray());
_picker.WrapSelectorWheel = false;
_picker.DescendantFocusability = DescendantFocusability.BlockDescendants;
_picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(_picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
if (!Element.IsSet(Picker.TitleColorProperty)) {
builder.SetTitle(model.Title ?? "");
} else {
var title = new SpannableString(model.Title ?? "");
title.SetSpan(new ForegroundColorSpan(model.TitleColor.ToAndroid()), 0, title.Length(), SpanTypes.ExclusiveExclusive);
builder.SetTitle(title);
}
// TODO: get texts (and whether to show) from XF Element.
builder.SetNegativeButton(global::Android.Resource.String.Cancel, (s, a) => {
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
_dialog = null;
});
builder.SetPositiveButton(global::Android.Resource.String.Ok, (s, a) => {
ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, _picker.Value);
// It is possible for the Content of the Page to be changed on SelectedIndexChanged.
// In this case, the Element & Control will no longer exist.
if (Element != null) {
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (sender, args) => {
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
_dialog?.Dispose();
_dialog = null;
};
_dialog.Show();
// TODO: Control this via a custom XF property.
var buttonColor = Color.Accent.ToAndroid();
// From
var btnCancel = _dialog.GetButton((int)DialogInterface.ButtonNegative);
// "?" in case no such button was attached.
btnCancel?.SetTextColor(buttonColor);
var btnOK = _dialog.GetButton((int)DialogInterface.ButtonPositive);
btnOK?.SetTextColor(buttonColor);
SetTextColor();
SetBackgroundColor();
}
private void SetTextColor()
{
Control?.SetTextColor(Element.TextColor.ToAndroid());
if (_picker != null)
SetTextColor(_picker, Element.TextColor.ToAndroid());
}
private void SetBackgroundColor()
{
Control?.SetBackgroundColor(Element.BackgroundColor.ToAndroid());
_picker?.SetBackgroundColor(Element.BackgroundColor.ToAndroid());
_dialog?.Window.SetBackgroundDrawable(new global::Android.Graphics.Drawables.ColorDrawable(Element.BackgroundColor.ToAndroid()));
}
// From
// TODO: When support API 29, this needs to be modified re ?
public static bool SetTextColor(NumberPicker numberPicker, AColor color)
{
var sdkInt = global::Android.OS.Build.VERSION.SdkInt;
//var limitSdk = global::Android.OS.Build.VERSION_CODES.Q;
int count = numberPicker.ChildCount;
for (int i = 0; i < count; i++) {
var child = numberPicker.GetChildAt(i);
if (child.GetType() == typeof(EditText)) {
try {
var selectorWheelPaintField = numberPicker.Class
.GetDeclaredField("mSelectorWheelPaint");
selectorWheelPaintField.Accessible = true;
EditText editText = (EditText)child;
editText.SetTextColor(color);
var paint = (global::Android.Graphics.Paint)selectorWheelPaintField.Get(numberPicker);
paint.Color = color;
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;
}
}
}
奖励:为 Android AlertDialog 设置样式:
大多数对话框都可以使用 AlertDialog.Builder 在 Android 上构建(在您的 Android 项目代码中)。这可以在 无需 创建自定义渲染器的情况下完成。在此代码中,我使用了来自我的 XF 应用程序资源的不同名称的颜色。 Google Xamarin Form 中的颜色主题化技术,了解如何将此类资源设置为不同的颜色值:
// Extracted from a larger class. Some of these not used in the code here.
using System;
using System.Collections.Generic;
using System.IO;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.Provider;
using Android.Views;
using Android.Widget;
using Android.Support.V4.Content;
using Android.Support.V4.App;
using Android.Support.V7.App;
// For Extension: color.ToAndroid.
using Xamarin.Forms.Platform.Android;
public static partial class UI
{
// If cancelHandler is null, no Cancel button will be added.
// If noHandler is not null, then "OK" is renamed "Yes".
public static void ShowAlertDialog(string title, string message,
EventHandler okHandler, EventHandler cancelHandler,
EventHandler noHandler = null, bool textIsYes = false)
{
bool hasCancel = (cancelHandler != null);
bool hasNo = (noHandler != null);
string okOrYes = PS.LocalizedString((textIsYes || hasNo ? "Yes" : "OK"));
// When both cancel and no, is "Cancel".
// When neither cancel or no, defaults to "Cancel", which does nothing.
// The only time this is "No", is if there is a No button, but no Cancel button.
string cancelOrNo = PS.LocalizedString((hasNo && !hasCancel ? "No" : "Cancel"));
EventHandler cancelOrNoHandler = (hasCancel ? cancelHandler : noHandler);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.it);
var titleOb = new Android.Text.SpannableString(title);
// TODO: Add custom XF property to set this.
var titleColor = (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["MaxForegroundColor"];
titleOb.SetSpan(new Android.Text.Style.ForegroundColorSpan(titleColor.ToAndroid()), 0, title.Length, Android.Text.SpanTypes.ExclusiveExclusive);
builder.SetTitle(titleOb);
builder.SetMessage(message);
if (hasCancel || hasNo) {
builder.SetNegativeButton(cancelOrNo,
(s, e) => {
cancelOrNoHandler?.Invoke(null, EventArgs.Empty);
});
}
// Only used when BOTH Cancel and No buttons.
if (hasNo && hasCancel)
builder.SetNeutralButton(PS.LocalizedString("No"),
(s, e) => { noHandler.Invoke(null, EventArgs.Empty); });
builder.SetPositiveButton(okOrYes, (s, e) => {
if (okHandler != null)
okHandler.Invoke(null, EventArgs.Empty);
});
AlertDialog dialog = builder.Show();
SetAlertStyle(dialog);
}
/// <summary>
/// This doesn't set Title Color. Set that earlier, while building the dialog.
/// </summary>
/// <param name="dialog"></param>
private static void SetAlertStyle(AlertDialog dialog)
{
// Use these if succeed in changing both title and background colors.
var foreColor = (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["ForegroundColor"];
var accentAgainstBack = (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["AccentSaturated25Fore33"];
var backColor = (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["BackgroundColor"];
SetBackgroundColor(dialog, backColor);
//// DIDN'T WORK: No effect on title color. Set earlier, while building the dialog.
//int alertViewId = Android.App.Application.Context.Resources.GetIdentifier("alertTitle", "id", "android");
//SetItemTextColor(dialog, "alertTitle", accentAgainstBack);
SetItemTextColor(dialog, "message", foreColor);
SetItemTextColor(dialog, "button1", (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["AccentSaturated25Fore33"]);
SetItemTextColor(dialog, "button2", (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["ForegroundColor"]);
}
private static void SetItemTextColor(AlertDialog dialog, string itemResourceName, Xamarin.Forms.Color color)
{
int textId = Android.App.Application.Context.Resources.GetIdentifier(itemResourceName, "id", "android");
TextView text = dialog.FindViewById<TextView>(textId);
text?.SetTextColor(color.ToAndroid());
}
private static void SetBackgroundColor(AlertDialog dialog, Xamarin.Forms.Color xfColor)
{
dialog?.Window.SetBackgroundDrawable(new global::Android.Graphics.Drawables.ColorDrawable(xfColor.ToAndroid()));
}
}
我正在向 Xamarin Forms 应用添加颜色主题。 主题被指定为动态变化,具体取决于用户所站的客户端位置 at/near。我们有 "light" 和 "dark" 主题客户,有多种调色板;因此需要能够更改 all 显示项目的所有颜色。动态地。
到目前为止,我的动态主题适用于几乎所有类型的视图。
在 Android 上为 iOS 样式(垂直滚轮)Xamarin.Forms.Picker
着色时,我 卡在两个细节上 :
问题一:如何改变背景颜色?
问题 2:如何更改 "Cancel" 按钮的文字颜色?
这个基于 Android NumberPicker
的 XF 视图,似乎不是开箱即用的完全可自定义颜色。从最新的 XF 开始,4.3.0.991211.
----- 我试过的:-----
一个。在 Style.xml
资源文件中设置 Android 主题颜色 静态 。成功。但它是静态的。
乙。下载 Xamarin Forms Light/Dark Theme Sample.
在 Android 上构建并 运行。转到其 "Select Theme" 选择器。选择浅色主题。再次转到选择器 - 白色背景,到目前为止,一切都很好。选择深色主题。再次转到选择器 - 选择器仍然是黑白的。这里没有解决方案。
C。在网上找到如何使用 AlertDialog "builder" 动态更改 AlertDialog
s "OK"/"Cancel" 按钮文本颜色,并获取资源 ID。成功实施了。不确定如何将相同的技术应用于 Picker。
问题3:如何发现XF Picker使用的Android控件中使用了哪些资源ID?
D.克隆 Xamarin Forms 源代码。将 Droid PickerRenderer 复制到我的代码中。大部分代码不能运行,因为它依赖于internal
classes/functions。让我的继承自内置的,并注释掉任何不需要/不会编译的代码。
自定义 OnElementChanged
,因为那是设置 TextColor
的地方。
在自定义渲染器源代码中,IPickerRenderer.OnClick()
构建一个包含 NumberPicker
的 AlertDialog
。我没有看到任何设置背景颜色或 "Cancel" 按钮文本颜色的逻辑。但至少我们知道了Android类。 参见上面的问题 3。
E.至少 having a small number of predefined themes might be a solution. Or Android theme.ApplyStyle - 但这可以是 dynamic 风格吗?我还没有测试过这些是否成功地改变了我所坚持的细节。 [我试过后会回来报告的。]
F. 重新启动应用程序 可以更改主题:any-color themes。这是我最后的选择;首先看看我是否可以在 C# 中完全控制颜色,而无需重新启动。
注意:SO 和 Xamarin.Forums 上有很多帖子一般 讨论 Xamarin Forms 中的主题。我已经过了那个点,所以我不会在这里列出这些,除了提到 Clint St.Laurent's theming post,它(快速浏览一下)看起来与我所做的相似。 在我的测试中,我只剩下少量微小的细节 - Xamarin Forms 无法将主题颜色应用于这些细节。
G。其他看起来相关的 SO 帖子 - 但不执行问题 1 和 2 中列出的确切任务:
另一个
How to really change primary and accent color in android lollipop - 我在 E
和 F
中提到了答案。如果我无法在自定义 PickerRenderer 中通过 C# 代码实现结果,我将调查这些。
Xamarin 论坛帖子:
How to change the colorAccent at runtime?。给出的唯一答案不会改变主题 colorAccent
。它只是改变了应用栏的颜色。
澄清:有大量文档、论坛和博客文章描述了 Xamarin Forms 主题化的一般机制。不是在寻找答案。我只是想解决 少量缺失的细节 ,截至最新的稳定 Xamarin Forms,4.3.0.991211。 希望 Xamarin Forms 成功地为所有类型控件的 100% 细节设置主题的那一天会到来——到那时这个问答将过时。
问题中 D.
的实现。在代码中作为注释给出的学分。
此 自定义 Android PickerRenderer 动态设置 3 种颜色:文本(蓝色)、"wheel" 文本后面的背景(浅橙色)、按钮(暗红色)。
澄清:static Android 主题有深色背景。目标是动态地从深色主题更改为浅色主题。为了使已成功设置的颜色非常明显,使用了强烈着色的颜色。
文字之间的白色"dividers"我还没有上色。我在 Xamarin 或 Android 文档中看到了分隔线可绘制对象的提及。抓住它并设置它的颜色应该很简单。 编辑:可能 titleDivider
在 Change picker control pop-up background and text colors, or in selectionDividerField
in Tapa Save's answer.
注意:我没有添加自定义 XF 属性 来控制按钮颜色;硬编码为 Color.Accent
.
using Android.App;
using Android.Util;
using Android.Views;
using Android.Widget;
using System;
using System.ComponentModel;
using System.Linq;
using Orientation = Android.Widget.Orientation;
using Android.Content;
using Android.Text;
using Android.Text.Style;
using Java.Lang;
// PickerEditText
using Xamarin.Forms.Platform.Android;
using AColor = Android.Graphics.Color;
using Picker = Xamarin.Forms.Picker;
[assembly: Xamarin.Forms.ExportRenderer(typeof(Picker), typeof(LCPickerRenderer))]
namespace Xamarin.Forms.Platform.Android
{
// Based on Xamarin.Forms source code, modified.
public class LCPickerRenderer : PickerRenderer, IPickerRenderer //ViewRenderer<Picker, EditText>, IPickerRenderer
{
AlertDialog _dialog;
NumberPicker _picker;
public LCPickerRenderer(Context context) : base(context)
{
//AutoPackage = false;
}
[Obsolete("This constructor is obsolete as of version 2.5. Please use PickerRenderer(Context) instead.")]
[EditorBrowsable(EditorBrowsableState.Never)]
public LCPickerRenderer()
{
//AutoPackage = false;
}
IElementController ElementController => Element as IElementController;
protected override void OnElementChanged(ElementChangedEventArgs<Picker> e)
{
if (e.OldElement != null) {
//((INotifyCollectionChanged)e.OldElement.Items).CollectionChanged -= RowsCollectionChanged;
}
base.OnElementChanged(e);
if (e.NewElement != null) {
// --- custom work ---
if (Control != null) {
SetTextColor();
SetBackgroundColor();
}
}
}
protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
{
base.OnElementPropertyChanged(sender, e);
if (e.PropertyName == Picker.BackgroundColorProperty.PropertyName) {
SetBackgroundColor();
} else if (e.PropertyName == Picker.TextColorProperty.PropertyName) {
SetTextColor();
}
//if (e.PropertyName == Picker.TitleProperty.PropertyName || e.PropertyName == Picker.TitleColorProperty.PropertyName)
// UpdatePicker();
//else if (e.PropertyName == Picker.SelectedIndexProperty.PropertyName)
// UpdatePicker();
//else if (e.PropertyName == Picker.CharacterSpacingProperty.PropertyName)
// UpdateCharacterSpacing();
//else if (e.PropertyName == Picker.TextColorProperty.PropertyName)
// UpdateTextColor();
//else if (e.PropertyName == Picker.FontAttributesProperty.PropertyName || e.PropertyName == Picker.FontFamilyProperty.PropertyName || e.PropertyName == Picker.FontSizeProperty.PropertyName)
// UpdateFont();
}
void IPickerRenderer.OnClick()
{
Picker model = Element;
if (_dialog != null)
return;
var xfTextColor = Element.TextColor;
_picker = new NumberPicker(Context);
if (model.Items != null && model.Items.Any()) {
_picker.MaxValue = model.Items.Count - 1;
_picker.MinValue = 0;
_picker.SetDisplayedValues(model.Items.ToArray());
_picker.WrapSelectorWheel = false;
_picker.DescendantFocusability = DescendantFocusability.BlockDescendants;
_picker.Value = model.SelectedIndex;
}
var layout = new LinearLayout(Context) { Orientation = Orientation.Vertical };
layout.AddView(_picker);
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, true);
var builder = new AlertDialog.Builder(Context);
builder.SetView(layout);
if (!Element.IsSet(Picker.TitleColorProperty)) {
builder.SetTitle(model.Title ?? "");
} else {
var title = new SpannableString(model.Title ?? "");
title.SetSpan(new ForegroundColorSpan(model.TitleColor.ToAndroid()), 0, title.Length(), SpanTypes.ExclusiveExclusive);
builder.SetTitle(title);
}
// TODO: get texts (and whether to show) from XF Element.
builder.SetNegativeButton(global::Android.Resource.String.Cancel, (s, a) => {
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
_dialog = null;
});
builder.SetPositiveButton(global::Android.Resource.String.Ok, (s, a) => {
ElementController.SetValueFromRenderer(Picker.SelectedIndexProperty, _picker.Value);
// It is possible for the Content of the Page to be changed on SelectedIndexChanged.
// In this case, the Element & Control will no longer exist.
if (Element != null) {
if (model.Items.Count > 0 && Element.SelectedIndex >= 0)
Control.Text = model.Items[Element.SelectedIndex];
ElementController.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
}
_dialog = null;
});
_dialog = builder.Create();
_dialog.DismissEvent += (sender, args) => {
ElementController?.SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false);
_dialog?.Dispose();
_dialog = null;
};
_dialog.Show();
// TODO: Control this via a custom XF property.
var buttonColor = Color.Accent.ToAndroid();
// From
var btnCancel = _dialog.GetButton((int)DialogInterface.ButtonNegative);
// "?" in case no such button was attached.
btnCancel?.SetTextColor(buttonColor);
var btnOK = _dialog.GetButton((int)DialogInterface.ButtonPositive);
btnOK?.SetTextColor(buttonColor);
SetTextColor();
SetBackgroundColor();
}
private void SetTextColor()
{
Control?.SetTextColor(Element.TextColor.ToAndroid());
if (_picker != null)
SetTextColor(_picker, Element.TextColor.ToAndroid());
}
private void SetBackgroundColor()
{
Control?.SetBackgroundColor(Element.BackgroundColor.ToAndroid());
_picker?.SetBackgroundColor(Element.BackgroundColor.ToAndroid());
_dialog?.Window.SetBackgroundDrawable(new global::Android.Graphics.Drawables.ColorDrawable(Element.BackgroundColor.ToAndroid()));
}
// From
// TODO: When support API 29, this needs to be modified re ?
public static bool SetTextColor(NumberPicker numberPicker, AColor color)
{
var sdkInt = global::Android.OS.Build.VERSION.SdkInt;
//var limitSdk = global::Android.OS.Build.VERSION_CODES.Q;
int count = numberPicker.ChildCount;
for (int i = 0; i < count; i++) {
var child = numberPicker.GetChildAt(i);
if (child.GetType() == typeof(EditText)) {
try {
var selectorWheelPaintField = numberPicker.Class
.GetDeclaredField("mSelectorWheelPaint");
selectorWheelPaintField.Accessible = true;
EditText editText = (EditText)child;
editText.SetTextColor(color);
var paint = (global::Android.Graphics.Paint)selectorWheelPaintField.Get(numberPicker);
paint.Color = color;
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;
}
}
}
奖励:为 Android AlertDialog 设置样式:
大多数对话框都可以使用 AlertDialog.Builder 在 Android 上构建(在您的 Android 项目代码中)。这可以在 无需 创建自定义渲染器的情况下完成。在此代码中,我使用了来自我的 XF 应用程序资源的不同名称的颜色。 Google Xamarin Form 中的颜色主题化技术,了解如何将此类资源设置为不同的颜色值:
// Extracted from a larger class. Some of these not used in the code here.
using System;
using System.Collections.Generic;
using System.IO;
using Android.Content;
using Android.Content.PM;
using Android.Graphics;
using Android.Provider;
using Android.Views;
using Android.Widget;
using Android.Support.V4.Content;
using Android.Support.V4.App;
using Android.Support.V7.App;
// For Extension: color.ToAndroid.
using Xamarin.Forms.Platform.Android;
public static partial class UI
{
// If cancelHandler is null, no Cancel button will be added.
// If noHandler is not null, then "OK" is renamed "Yes".
public static void ShowAlertDialog(string title, string message,
EventHandler okHandler, EventHandler cancelHandler,
EventHandler noHandler = null, bool textIsYes = false)
{
bool hasCancel = (cancelHandler != null);
bool hasNo = (noHandler != null);
string okOrYes = PS.LocalizedString((textIsYes || hasNo ? "Yes" : "OK"));
// When both cancel and no, is "Cancel".
// When neither cancel or no, defaults to "Cancel", which does nothing.
// The only time this is "No", is if there is a No button, but no Cancel button.
string cancelOrNo = PS.LocalizedString((hasNo && !hasCancel ? "No" : "Cancel"));
EventHandler cancelOrNoHandler = (hasCancel ? cancelHandler : noHandler);
AlertDialog.Builder builder = new AlertDialog.Builder(MainActivity.it);
var titleOb = new Android.Text.SpannableString(title);
// TODO: Add custom XF property to set this.
var titleColor = (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["MaxForegroundColor"];
titleOb.SetSpan(new Android.Text.Style.ForegroundColorSpan(titleColor.ToAndroid()), 0, title.Length, Android.Text.SpanTypes.ExclusiveExclusive);
builder.SetTitle(titleOb);
builder.SetMessage(message);
if (hasCancel || hasNo) {
builder.SetNegativeButton(cancelOrNo,
(s, e) => {
cancelOrNoHandler?.Invoke(null, EventArgs.Empty);
});
}
// Only used when BOTH Cancel and No buttons.
if (hasNo && hasCancel)
builder.SetNeutralButton(PS.LocalizedString("No"),
(s, e) => { noHandler.Invoke(null, EventArgs.Empty); });
builder.SetPositiveButton(okOrYes, (s, e) => {
if (okHandler != null)
okHandler.Invoke(null, EventArgs.Empty);
});
AlertDialog dialog = builder.Show();
SetAlertStyle(dialog);
}
/// <summary>
/// This doesn't set Title Color. Set that earlier, while building the dialog.
/// </summary>
/// <param name="dialog"></param>
private static void SetAlertStyle(AlertDialog dialog)
{
// Use these if succeed in changing both title and background colors.
var foreColor = (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["ForegroundColor"];
var accentAgainstBack = (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["AccentSaturated25Fore33"];
var backColor = (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["BackgroundColor"];
SetBackgroundColor(dialog, backColor);
//// DIDN'T WORK: No effect on title color. Set earlier, while building the dialog.
//int alertViewId = Android.App.Application.Context.Resources.GetIdentifier("alertTitle", "id", "android");
//SetItemTextColor(dialog, "alertTitle", accentAgainstBack);
SetItemTextColor(dialog, "message", foreColor);
SetItemTextColor(dialog, "button1", (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["AccentSaturated25Fore33"]);
SetItemTextColor(dialog, "button2", (Xamarin.Forms.Color)Xamarin.Forms.Application.Current.Resources["ForegroundColor"]);
}
private static void SetItemTextColor(AlertDialog dialog, string itemResourceName, Xamarin.Forms.Color color)
{
int textId = Android.App.Application.Context.Resources.GetIdentifier(itemResourceName, "id", "android");
TextView text = dialog.FindViewById<TextView>(textId);
text?.SetTextColor(color.ToAndroid());
}
private static void SetBackgroundColor(AlertDialog dialog, Xamarin.Forms.Color xfColor)
{
dialog?.Window.SetBackgroundDrawable(new global::Android.Graphics.Drawables.ColorDrawable(xfColor.ToAndroid()));
}
}