如何定义和使用 IOn[X]SetListenerImplementor

How do I define and use IOn[X]SetListenerImplementor

我正在阅读 Xamarin 的源代码并遇到了这两个对话框:TimePickerDialog and DatePickerDialog

阅读这些内容时,我发现有一个正在实例化的对象,我无法在其他任何地方找到它。

我想知道这些监听器实现者的目的是什么,以及如何真正着手创建我自己的(或者我是否需要自己创建)。我的目标是创建一些自定义 PickerDialog,所以我正在检查它们是如何实现的。

这是TimePickerDialog

using System;
using System.Collections.Generic;
using Android.Runtime;

namespace Android.App {

    public partial class TimePickerDialog {

        public TimePickerDialog (Android.Content.Context context, EventHandler<TimeSetEventArgs> callBack, int hourOfDay, int minute, bool is24HourView) 
            // ** The IOnTimeSetListenerImplementor here **
            : this (context, new IOnTimeSetListenerImplementor () { Handler = callBack }, hourOfDay, minute, is24HourView) {}

        public TimePickerDialog (Android.Content.Context context, int theme, EventHandler<TimeSetEventArgs> callBack, int hourOfDay, int minute, bool is24HourView) 
            : this (context, theme, new IOnTimeSetListenerImplementor () { Handler = callBack }, hourOfDay, minute, is24HourView) {}

    }
}

这是DatePickerDialog

using System;
using System.Collections.Generic;
using Android.Runtime;

namespace Android.App {

    public partial class DatePickerDialog {

        public partial class DateSetEventArgs {

            public DateTime Date {
                get { return new DateTime (Year, Month + 1, DayOfMonth); }
            }

#if ANDROID_24
            [Obsolete ("This parameter in DateTimePickerDialog constructor is removed in Android API, so it will vanish from this automatically generated type too.")]
            public int MonthOfYear {
                get { return Month; }
            }
#else
            public int Month {
                get { return monthOfYear; }
            }
#endif
        }

        public DatePickerDialog (Android.Content.Context context, EventHandler<DateSetEventArgs> callBack, int year, int monthOfYear, int dayOfMonth) 
            // ** The IOnDateSetListenerImplementor here **
            : this (context, new IOnDateSetListenerImplementor () { Handler = callBack }, year, monthOfYear, dayOfMonth) {}

        public DatePickerDialog (Android.Content.Context context, int theme, EventHandler<DateSetEventArgs> callBack, int year, int monthOfYear, int dayOfMonth) 
            : this (context, theme, new IOnDateSetListenerImplementor () { Handler = callBack }, year, monthOfYear, dayOfMonth) {}

        public void UpdateDate (DateTime date)
        {
            UpdateDate (date.Year, date.Month - 1, date.Day);
        }
    }
}

额外问题:为什么这两个 类 定义为部分 类?我无法在此回购中的其他任何地方找到其他部分。可能是我搜索的不对,也可能是为了以后的扩展?

感谢您的帮助!

我在搜索 Android.App.DatePickerDialog 的元数据时发现了以下内容(通过 Right-Clicking 在 Visual Studio 中输入 class 名称并选择转到定义很容易):

#region Assembly Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=84e04ff9cfb79065
// C:\Program Files (x86)\Microsoft Visual Studio19\Community\Common7\IDE\ReferenceAssemblies\Microsoft\Framework\MonoAndroid\v11.0\Mono.Android.dll
#endregion

#nullable enable

using Android.Content;
using Android.Runtime;
using Android.Widget;
using Java.Interop;
using System;
using System.ComponentModel;
using System.Diagnostics;

namespace Android.App
{
    [Register("android/app/DatePickerDialog", DoNotGenerateAcw = true)]
    public class DatePickerDialog : AlertDialog, IDialogInterfaceOnClickListener, IJavaObject, IDisposable, IJavaPeerable, DatePicker.IOnDateChangedListener
    {
        [Register(".ctor", "(Landroid/content/Context;)V", "", ApiSince = 24)]
        public DatePickerDialog(Context context);
        [Register(".ctor", "(Landroid/content/Context;I)V", "", ApiSince = 24)]
        public DatePickerDialog(Context context, int themeResId);
        public DatePickerDialog(Context context, EventHandler<DateSetEventArgs> callBack, int year, int monthOfYear, int dayOfMonth);
        [Register(".ctor", "(Landroid/content/Context;Landroid/app/DatePickerDialog$OnDateSetListener;III)V", "")]
        public DatePickerDialog(Context context, IOnDateSetListener? listener, int year, int month, int dayOfMonth);
        public DatePickerDialog(Context context, int theme, EventHandler<DateSetEventArgs> callBack, int year, int monthOfYear, int dayOfMonth);
        [Register(".ctor", "(Landroid/content/Context;ILandroid/app/DatePickerDialog$OnDateSetListener;III)V", "")]
        public DatePickerDialog(Context context, int themeResId, IOnDateSetListener? listener, int year, int monthOfYear, int dayOfMonth);
        protected DatePickerDialog(IntPtr javaReference, JniHandleOwnership transfer);

        public virtual DatePicker DatePicker { get; }
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        public override JniPeerMembers JniPeerMembers { get; }
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected override Type ThresholdType { get; }
        [DebuggerBrowsable(DebuggerBrowsableState.Never)]
        [EditorBrowsable(EditorBrowsableState.Never)]
        protected override IntPtr ThresholdClass { get; }

        public event EventHandler<DateSetEventArgs> DateSet;

        [Register("onClick", "(Landroid/content/DialogInterface;I)V", "GetOnClick_Landroid_content_DialogInterface_IHandler")]
        public virtual void OnClick(IDialogInterface dialog, int which);
        [Register("onDateChanged", "(Landroid/widget/DatePicker;III)V", "GetOnDateChanged_Landroid_widget_DatePicker_IIIHandler")]
        public virtual void OnDateChanged(DatePicker view, int year, int month, int dayOfMonth);
        [Register("setOnDateSetListener", "(Landroid/app/DatePickerDialog$OnDateSetListener;)V", "GetSetOnDateSetListener_Landroid_app_DatePickerDialog_OnDateSetListener_Handler", ApiSince = 24)]
        public virtual void SetOnDateSetListener(IOnDateSetListener? listener);
        public void UpdateDate(DateTime date);
        [Register("updateDate", "(III)V", "GetUpdateDate_IIIHandler")]
        public virtual void UpdateDate(int year, int month, int dayOfMonth);

        [Register("android/app/DatePickerDialog$OnDateSetListener", "", "Android.App.DatePickerDialog/IOnDateSetListenerInvoker")]
        public interface IOnDateSetListener : IJavaObject, IDisposable, IJavaPeerable
        {
            [Register("onDateSet", "(Landroid/widget/DatePicker;III)V", "GetOnDateSet_Landroid_widget_DatePicker_IIIHandler:Android.App.DatePickerDialog/IOnDateSetListenerInvoker, Mono.Android, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null")]
            void OnDateSet(DatePicker? view, int year, int month, int dayOfMonth);
        }

        public class DateSetEventArgs : EventArgs
        {
            public DateSetEventArgs(int year, int month, int dayOfMonth);

            public DateTime Date { get; }
            [Obsolete("This parameter in DateTimePickerDialog constructor is removed in Android API, so it will vanish from this automatically generated type too.")]
            public int MonthOfYear { get; }
            public int Year { get; }
            public int Month { get; }
            public int DayOfMonth { get; }
        }
    }
}

我看到 IOnDateSetListener 接口是在 class 中定义的。我还注意到 class 不再标记为 partial

然后我也找到了 DatePickerDialog+IOnDateSetListener.xml:

<Type Name="DatePickerDialog+IOnDateSetListener" FullName="Android.App.DatePickerDialog+IOnDateSetListener">
  <TypeSignature Language="C#" Value="public interface DatePickerDialog.IOnDateSetListener : Android.Runtime.IJavaObject, IDisposable, Java.Interop.IJavaPeerable" />
  <TypeSignature Language="ILAsm" Value=".class nested public interface auto ansi abstract DatePickerDialog/IOnDateSetListener implements class Android.Runtime.IJavaObject, class Java.Interop.IJavaPeerable, class System.IDisposable" />
  <TypeSignature Language="DocId" Value="T:Android.App.DatePickerDialog.IOnDateSetListener" />
  <TypeSignature Language="F#" Value="type DatePickerDialog.IOnDateSetListener = interface&#xA;    interface IJavaObject&#xA;    interface IDisposable&#xA;    interface IJavaPeerable" />

...

 <AttributeName Language="C#">[Android.Runtime.Register("android/app/DatePickerDialog$OnDateSetListener", "", "Android.App.DatePickerDialog/IOnDateSetListenerInvoker")]</AttributeName>
      <AttributeName Language="F#">[&lt;Android.Runtime.Register("android/app/DatePickerDialog$OnDateSetListener", "", "Android.App.DatePickerDialog/IOnDateSetListenerInvoker")&gt;]</AttributeName>
   
...

<a href="https://developer.android.com/reference/android/app/DatePickerDialog$OnDateSetListener" title="Reference documentation">Android platform documentation</a>

...

DatePickerDialog.xml:

<Type Name="DatePickerDialog" FullName="Android.App.DatePickerDialog">
  <TypeSignature Language="C#" Value="public class DatePickerDialog : Android.App.AlertDialog, Android.Content.IDialogInterfaceOnClickListener, Android.Widget.DatePicker.IOnDateChangedListener, IDisposable, Java.Interop.IJavaPeerable" />
  <TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit DatePickerDialog extends Android.App.AlertDialog implements class Android.Content.IDialogInterfaceOnClickListener, class Android.Runtime.IJavaObject, class Android.Widget.DatePicker/IOnDateChangedListener, class Java.Interop.IJavaPeerable, class System.IDisposable" />
  <TypeSignature Language="DocId" Value="T:Android.App.DatePickerDialog" />
  <TypeSignature Language="F#" Value="type DatePickerDialog = class&#xA;    inherit AlertDialog&#xA;    interface IDialogInterfaceOnClickListener&#xA;    interface IJavaObject&#xA;    interface IDisposable&#xA;    interface IJavaPeerable&#xA;    interface DatePicker.IOnDateChangedListener" />
  <AssemblyInfo>
    <AssemblyName>Mono.Android</AssemblyName>
    <AssemblyVersion>0.0.0.0</AssemblyVersion>
  </AssemblyInfo>
  <Base>
    <BaseTypeName>Android.App.AlertDialog</BaseTypeName>
  </Base>
  <Interfaces>
    <Interface>
      <InterfaceName>Android.Content.IDialogInterfaceOnClickListener</InterfaceName>
    </Interface>
    <Interface>
      <InterfaceName>Android.Runtime.IJavaObject</InterfaceName>
    </Interface>
    <Interface>
      <InterfaceName>Android.Widget.DatePicker+IOnDateChangedListener</InterfaceName>
    </Interface>
...

上述两个文件都提到了它。我最好的猜测是,有些构建事件使用此信息来构建 cross-language 方法并将它们注入部分 classes。 c# DatePickerDialog(来自元数据)可能是编译时事件将问题中提到的 DatePickerDialog 与构建 Xamarin 库时包含在 XML 文件中的信息合并的结果并打包。

侦听器接口似乎特定于 DatePickerTimePicker 的行为,并且似乎并非在所有情况下都是必需的。

无需尝试复制该行为,创建一个扩展 AlertDialog and calling the SetView 方法的自定义对话框可能就足够了。

编辑: Xamarin Community Toolkit 中还包含一个 Popup class,这使得制作自定义对话框(如问题中引用的对话框)变得非常容易。请参阅文档 here