Android layout Mono Android:Java Android自定义视图JNI不在xml布局中调用构造函数
我们正在为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正确调用构造函数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,但保存值{
提前感谢问题是我没有完整地记录/解释
RegisterAttribute.DonotGenerateCW
的功能,对此我必须道歉
具体来说,问题在于:Android布局XML只能引用Java类型。通常这不是问题,正如在构建时生成的,为每个C类型提供Java类型,这些C类型的子类Java.Lang.Object
然而,Android可调用包装器是特殊的:它们包含Javanative
方法声明,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#实例,这就是您看到的行为
所有这些听起来对我来说都是完全正常的,但我是写这篇文章的人,所以我有偏见
那么,你希望发生什么?如果您真的想要一个手工编写的JavaImageView
,那么只有一个合理的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)");
...
}
}