You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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失效,可完全自定义对话框布局:

  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>
  1. 修改渲染器的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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 09:37:04