如何在Xamarin中完美本地化DatePicker/TimePicker及对话框ActionBar
问题:DatePicker/TimePicker应用内语言切换时的本地化完善
需求:应用内切换语言时,DatePicker、TimePicker的默认文本需同步为本地化文本。
当前状态:已通过自定义渲染器修改DatePicker对话框的Cancel、Done按钮文本,但对话框的ActionBar部分未实现本地化;系统设置切换语言时DatePicker可完美本地化。
现有自定义渲染器代码:
using Android.App; using Android.Content; using Android.OS; using Android.Runtime; using Android.Views; using Android.Widget; using App2.Resources; using Java.Util; using System; using System.Collections.Generic; using System.Linq; using System.Text; using Xamarin.CommunityToolkit.Helpers; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; using DatePicker = Xamarin.Forms.DatePicker; [assembly: ExportRenderer(typeof(Xamarin.Forms.DatePicker), typeof(App2.Droid.CustomRenderer.LocalizationDatePicker))] namespace App2.Droid.CustomRenderer { public class LocalizationDatePicker : DatePickerRenderer { DatePickerDialog pickerDialog; private Xamarin.Forms.DatePicker datePicker; public LocalizationDatePicker(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.DatePicker> e) { base.OnElementChanged(e); //Disposing if (e.OldElement != null) { datePicker = null; } //Creating if (e.NewElement != null) { datePicker = e.NewElement; } Locale locale = new Java.Util.Locale(LocalizationResourceManager.Current.CurrentCulture.Name.Split('-')[0]); Control.TextLocale = locale; Android.Content.Res.Configuration config = new Android.Content.Res.Configuration(); config.SetLocale(locale); Locale.SetDefault(Locale.Category.Display, locale); Resources.Configuration.SetLocale(locale); } protected override DatePickerDialog CreateDatePickerDialog(int year, int month, int day) { pickerDialog = new DatePickerDialog(Context, (o, e) => { datePicker.Date = new DateTime(e.Year, e.Month, e.DayOfMonth, 0, 0, 0, LocalizationResourceManager.Current.CurrentCulture.Calendar); ((IElementController)datePicker).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); }, year, month, day); //ok pickerDialog.SetButton((int)DialogButtonType.Positive, AppResources.Accept, OnDone); //cancel pickerDialog.SetButton((int)DialogButtonType.Negative, AppResources.Cancel, OnCancel); return pickerDialog; } private void OnCancel(object sender, DialogClickEventArgs e) { datePicker.Unfocus(); } private void OnDone(object sender, DialogClickEventArgs e) { datePicker.Date = ((DatePickerDialog)sender).DatePicker.DateTime; datePicker.Unfocus(); } } }
解决方案
一、DatePicker对话框ActionBar本地化实现
方案1:通过本地化Context强制系统对话框适配
无需自定义布局,直接让系统对话框使用当前应用内Locale渲染所有元素(包括ActionBar),修改CreateDatePickerDialog方法:
protected override DatePickerDialog CreateDatePickerDialog(int year, int month, int day) { // 创建绑定当前Locale的Context var currentLocale = new Java.Util.Locale(LocalizationResourceManager.Current.CurrentCulture.Name.Split('-')[0]); var config = new Android.Content.Res.Configuration(Context.Resources.Configuration); config.SetLocale(currentLocale); var localizedContext = new ContextWrapper(Context.CreateConfigurationContext(config)); // 使用本地化Context创建对话框 pickerDialog = new DatePickerDialog(localizedContext, (o, e) => { datePicker.Date = new DateTime(e.Year, e.Month, e.DayOfMonth, 0, 0, 0, LocalizationResourceManager.Current.CurrentCulture.Calendar); ((IElementController)datePicker).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); }, year, month, day); // 设置按钮本地化文本 pickerDialog.SetButton((int)DialogButtonType.Positive, AppResources.Accept, OnDone); pickerDialog.SetButton((int)DialogButtonType.Negative, AppResources.Cancel, OnCancel); return pickerDialog; }
方案2:自定义对话框布局(兼容性更强)
如果系统版本差异导致方案1失效,可完全自定义对话框布局:
- 在Android项目
Resources/layout下创建custom_date_picker_dialog.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <!-- 自定义标题栏(替代ActionBar) --> <LinearLayout android:layout_width="match_parent" android:layout_height="wrap_content" android:background="?attr/colorPrimary" android:orientation="horizontal" android:padding="16dp"> <TextView android:id="@+id/dialog_title" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_weight="1" android:textColor="@android:color/white" android:textSize="18sp" /> <Button android:id="@+id/dialog_cancel" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/white" /> <Button android:id="@+id/dialog_done" android:layout_width="wrap_content" android:layout_height="wrap_content" android:textColor="@android:color/white" /> </LinearLayout> <!-- 原生DatePicker控件 --> <DatePicker android:id="@+id/date_picker" android:layout_width="match_parent" android:layout_height="wrap_content" /> </LinearLayout>
- 修改渲染器的
CreateDatePickerDialog方法:
protected override DatePickerDialog CreateDatePickerDialog(int year, int month, int day) { // 加载自定义布局 var inflater = LayoutInflater.From(Context); var dialogView = inflater.Inflate(Resource.Layout.custom_date_picker_dialog, null); // 获取控件 var datePicker = dialogView.FindViewById<DatePicker>(Resource.Id.date_picker); var dialogTitle = dialogView.FindViewById<TextView>(Resource.Id.dialog_title); var cancelBtn = dialogView.FindViewById<Button>(Resource.Id.dialog_cancel); var doneBtn = dialogView.FindViewById<Button>(Resource.Id.dialog_done); // 设置初始日期 datePicker.Init(year, month, day, (sender, e) => {}); // 绑定本地化文本 dialogTitle.Text = AppResources.SelectDate; // 替换为你的本地化资源键 cancelBtn.Text = AppResources.Cancel; doneBtn.Text = AppResources.Accept; // 创建自定义对话框 var dialog = new Dialog(Context); dialog.SetContentView(dialogView); dialog.Window.SetLayout(ViewGroup.LayoutParams.MatchParent, ViewGroup.LayoutParams.WrapContent); // 绑定按钮事件 cancelBtn.Click += (s, e) => { this.datePicker.Unfocus(); dialog.Dismiss(); }; doneBtn.Click += (s, e) => { this.datePicker.Date = new DateTime(datePicker.Year, datePicker.Month + 1, datePicker.DayOfMonth, 0, 0, 0, LocalizationResourceManager.Current.CurrentCulture.Calendar); ((IElementController)this.datePicker).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); this.datePicker.Unfocus(); dialog.Dismiss(); }; // 隐藏原生对话框按钮,返回空壳对象适配Xamarin要求 var emptyDialog = new DatePickerDialog(Context, (o, e) => {}, year, month, day); emptyDialog.Cancelable = true; // 聚焦时显示自定义对话框 Control.Click += (s, e) => dialog.Show(); return emptyDialog; }
二、TimePicker本地化实现
参照DatePicker的方案,创建对应的自定义渲染器:
using Android.App; using Android.Content; using Android.Widget; using App2.Resources; using Java.Util; using Xamarin.CommunityToolkit.Helpers; using Xamarin.Forms; using Xamarin.Forms.Platform.Android; using TimePicker = Xamarin.Forms.TimePicker; [assembly: ExportRenderer(typeof(Xamarin.Forms.TimePicker), typeof(App2.Droid.CustomRenderer.LocalizationTimePicker))] namespace App2.Droid.CustomRenderer { public class LocalizationTimePicker : TimePickerRenderer { TimePickerDialog pickerDialog; private Xamarin.Forms.TimePicker timePicker; public LocalizationTimePicker(Context context) : base(context) { } protected override void OnElementChanged(ElementChangedEventArgs<Xamarin.Forms.TimePicker> e) { base.OnElementChanged(e); if (e.OldElement != null) { timePicker = null; } if (e.NewElement != null) { timePicker = e.NewElement; } var locale = new Java.Util.Locale(LocalizationResourceManager.Current.CurrentCulture.Name.Split('-')[0]); Control.TextLocale = locale; var config = new Android.Content.Res.Configuration(); config.SetLocale(locale); Locale.SetDefault(Locale.Category.Display, locale); Resources.Configuration.SetLocale(locale); } protected override TimePickerDialog CreateTimePickerDialog(int hour, int minute) { // 使用本地化Context创建对话框 var currentLocale = new Java.Util.Locale(LocalizationResourceManager.Current.CurrentCulture.Name.Split('-')[0]); var config = new Android.Content.Res.Configuration(Context.Resources.Configuration); config.SetLocale(currentLocale); var localizedContext = new ContextWrapper(Context.CreateConfigurationContext(config)); pickerDialog = new TimePickerDialog(localizedContext, (o, e) => { timePicker.Time = new TimeSpan(e.HourOfDay, e.Minute, 0); ((IElementController)timePicker).SetValueFromRenderer(VisualElement.IsFocusedPropertyKey, false); }, hour, minute, true); // 设置按钮本地化文本 pickerDialog.SetButton((int)DialogButtonType.Positive, AppResources.Accept, OnDone); pickerDialog.SetButton((int)DialogButtonType.Negative, AppResources.Cancel, OnCancel); return pickerDialog; } private void OnCancel(object sender, DialogClickEventArgs e) { timePicker.Unfocus(); } private void OnDone(object sender, DialogClickEventArgs e) { timePicker.Time = ((TimePickerDialog)sender).TimePicker.CurrentTime; timePicker.Unfocus(); } } }
三、注意事项
- 确保
LocalizationResourceManager在应用内切换语言后,已正确更新CurrentCulture并触发UI刷新 - 应用内切换语言后,需让DatePicker/TimePicker重新获取焦点,才能加载新的本地化对话框
- Android 12+版本系统对话框样式变化较大,自定义布局方案兼容性更优
内容的提问来源于stack exchange,提问作者Sunday
相关产品推荐
相关产品推荐

