An implementation of D.
in the question. Credits given as comments in the code.
This custom Android PickerRenderer dynamically sets 3 colors: text (blue), background behind the "wheel" of texts (light orange), buttons (dark red).

Clarification: the static Android theme has a dark background. The goal is to dynamically change from that dark theme to a light theme. To make it very obvious what colors have been successfully set, strongly tinted colors are used.
The white "dividers" between the texts I haven't colored yet. I've seen mention in either Xamarin or Android docs of a divider drawable. It should be straightforward to get hold of that, and set its color. EDIT: Maybe titleDivider
in Tiago Flores code snippet in Change picker control pop-up background and text colors, or in Vahid's answer. OR selectionDividerField
in Tapa Save's answer.
NOTE: I haven't added a custom XF property to control the button color; hard-coded to 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 https://stackoverflow.com/a/56555965/199364.
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 https://stackoverflow.com/a/26657169/199364
// TODO: When support API 29, this needs to be modified re https://stackoverflow.com/a/56883356/199364 ?
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;
}
}
}
BONUS: Style an Android AlertDialog:

Most dialogs can be built on Android (in your Android project code) using AlertDialog.Builder. This can be done without making a custom Renderer. In this code, I use variously named colors from my XF Application's Resources. Google techniques for color theming in Xamarin Form, to see how to set such resources to different color values:
// 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()));
}
}