Android layout Mono Android:Java Android自定义视图JNI不在xml布局中调用构造函数

Android layout Mono Android:Java Android自定义视图JNI不在xml布局中调用构造函数,android-layout,java-native-interface,xamarin.android,Android Layout,Java Native Interface,Xamarin.android,我们正在为Android使用Mono,我们希望使用一些用JavaAndroid编写的自定义视图子类。我们创建了一个C#“桥”类,以通过JNI公开Java类。当通过C#bridge类从C#调用时,我们公开的方法重写和自定义方法工作正常,但当我们在XML布局中使用该类(通过引用视图的完全限定java命名空间)时,它似乎从未通过C#调用构造函数,因此C#类型没有正确设置 XML布局 当我们在程序中的断点处停止时,我们看到图像视图的本地类型为Android.Widgets.ImageView,但保存值{

我们正在为Android使用Mono,我们希望使用一些用JavaAndroid编写的自定义视图子类。我们创建了一个C#“桥”类,以通过JNI公开Java类。当通过C#bridge类从C#调用时,我们公开的方法重写和自定义方法工作正常,但当我们在XML布局中使用该类(通过引用视图的完全限定java命名空间)时,它似乎从未通过C#调用构造函数,因此C#类型没有正确设置

XML布局 当我们在程序中的断点处停止时,我们看到图像视图的本地类型为Android.Widgets.ImageView,但保存值{com.example.widget。ImageView@4057f5d8}。我们认为该类型也应该显示Example.Widgets.ImageView,但我们不知道如何让.NET使用该类型而不是继承的Android类型(Android.Widgets.ImageView

在XML布局中使用通过JNI公开的视图时,如何让.NET正确调用构造函数


提前感谢

问题是我没有完整地记录/解释
RegisterAttribute.DonotGenerateCW
的功能,对此我必须道歉

具体来说,问题在于:Android布局XML只能引用Java类型。通常这不是问题,正如在构建时生成的,为每个C类型提供Java类型,这些C类型的子类
Java.Lang.Object

然而,Android可调用包装器是特殊的:它们包含Java
native
方法声明,Android可调用包装器构造函数调用Mono for Android运行时来创建相关的C#类。(参见上面url中的示例,注意构造函数主体调用
mono.android.TypeManager.Activate()

然而,您的示例完全忽略了所有这些,因为您的布局XML没有引用Android可调用包装器,而是引用您的Java类型,并且您的Java类型没有调用
mono.Android.TypeManager.Activate()
的构造函数

结果是,发生的事情正是您告诉它发生的事情:您的Java类型被实例化,并且由于没有“管道”将Java实例与(将要)创建的C#实例关联(即
mono.android.TypeManager.Activate()
call),因此不会创建C#实例,这就是您看到的行为

所有这些听起来对我来说都是完全正常的,但我是写这篇文章的人,所以我有偏见

那么,你希望发生什么?如果您真的想要一个手工编写的Java
ImageView
,那么只有一个合理的C#构造函数可以调用:
(IntPtr,jnihandleowner)
构造函数。缺少的只是Java类型和C#类型之间的映射,您可以通过以下方式在应用程序启动过程中“某处”进行映射:

Android.Runtime.TypeManager(“com/example/widget/ImageView”,
typeof(例如.Widgets.ImageView));
如果您已经准备好了映射,那么当托管代码中出现
com.example.widget.ImageView
实例时,它将使用
(IntPtr,JniHandleOwnership)
构造函数用
example.Widgets.ImageView
实例包装

相反,如果您希望调用C#
(上下文上下文、IAttributeSet attrs、int defStyle)
构造函数,则应绕过包装类型,只需使用C#代码:

名称空间示例.Widgets{
公共类ImageView:global::Android.Widget.ImageView{
...

总之,
RegisterAttribute.DonotGenerateCW
是“特殊的”:这意味着您正在“别名”一个现有的Java类型,并且应该跳过“正常的”Android可调用包装器生成。这允许工作正常(您不能有两个具有相同完全限定名的不同类型),但增加了另一组复杂性。

我想知道这是否是由于自定义视图被命名为ImageView(与其父类类似)而导致的错误。能否尝试更改名称,看看是否遇到相同的问题?啊,太好了--静态构造函数中的TypeManager.RegisterType修复了它。谢谢!
  <merge xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent"
                  android:layout_height="fill_parent" >
        <com.example.widget.ImageView:id="@+id/custom_view" /> 
  </merge>
namespace Example.Widgets {
  [Register ("com/example/widget/ImageView", DoNotGenerateAcw=true)]
  public class ImageView : global::Android.Widget.ImageView {
    private new static IntPtr class_ref = JNIEnv.FindClass("com/example/widget/ImageView");
    private static IntPtr id_ctor_Landroid_content_Context_;
    private static IntPtr id_ctor_Landroid_content_Context_Landroid_util_AttributeSet_;
    private static IntPtr id_ctor_Landroid_content_Context_Landroid_util_AttributeSet_I;
    ...

    protected override IntPtr ThresholdClass {
        get {
            return ImageView.class_ref;
        }
    }

    protected override Type ThresholdType {
        get {
            return typeof(ImageView);
        }
    }

    protected ImageView (IntPtr javaReference, JniHandleOwnership transfer) : base (javaReference, transfer) {
    }

    // --V-- this should get called by the android layout inflater, but it never does (nor do any of the other public constructors here)
    [Register (".ctor", "(Landroid/content/Context;Landroid/util/AttributeSet;)V", "")]
    public ImageView (Context context, IAttributeSet attrs) : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer)
    {
        Log.Debug("ImageView","Calling C# constructor (Landroid/content/Context;Landroid/util/AttributeSet;)V");
        if (base.Handle != IntPtr.Zero)
        {
            return;
        }
        if (base.GetType () != typeof(ImageView))
        {
            base.SetHandle (JNIEnv.CreateInstance (base.GetType (), "(Landroid/content/Context;Landroid/util/AttributeSet;)V", new JValue[]
            {
                new JValue (JNIEnv.ToJniHandle (context)),
                new JValue (JNIEnv.ToJniHandle (attrs))
            }), JniHandleOwnership.TransferLocalRef);
            return;
        }
        if (ImageView.id_ctor_Landroid_content_Context_Landroid_util_AttributeSet_ == IntPtr.Zero)
        {
            ImageView.id_ctor_Landroid_content_Context_Landroid_util_AttributeSet_ = JNIEnv.GetMethodID (ImageView.class_ref, "<init>", "(Landroid/content/Context;Landroid/util/AttributeSet;)V");
        }
        base.SetHandle (JNIEnv.NewObject (ImageView.class_ref, ImageView.id_ctor_Landroid_content_Context_Landroid_util_AttributeSet_, new JValue[]
        {
            new JValue (JNIEnv.ToJniHandle (context)),
            new JValue (JNIEnv.ToJniHandle (attrs))
        }), JniHandleOwnership.TransferLocalRef);
    }



    [Register (".ctor", "(Landroid/content/Context;)V", "")]
    public ImageView (Context context) : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer) {
      ...
    }


    [Register (".ctor", "(Landroid/content/Context;Landroid/util/AttributeSet;I)V", "")]
    public ImageView (Context context, IAttributeSet attrs, int defStyle) : base (IntPtr.Zero, JniHandleOwnership.DoNotTransfer)    {
      ...
    }

    ...
  package com.example.widget;
  public class ImageView extends android.widget.ImageView {

      //--V-- This constructor DOES get called here (in java)
      public ImageView(Context context, AttributeSet attrs) {
          super(context, attrs);
          Log.d(TAG, "Called Java constructor (context, attrs)");
          ...
      }
  }