MonoDroid: Error al llamar al constructor de la vista personalizada – TwoDScrollView

Estoy construyendo una aplicación de Android que utiliza el TwoDScrollView personalizado que se encuentra aquí:

http://blog.gorges.us/2010/06/android-two-dimensional-scrollview/

Esta misma clase se puede encontrar referenciada en varios otros sitios web, y otros en Stack Overflow han hecho preguntas al respecto. Lo estaba usando en una aplicación anterior de Android que estaba construyendo usando Java / Eclipse, y estaba teniendo éxito.

Con mi aplicación actual, quería usar C # y MonoDroid. Decidí reescribir toda la clase TwoDScrollView en C #. Después de volver a escribirlo, y luego usarlo en un diseño XML, obtengo las siguientes excepciones cuando trato de ejecutar mi código:

System.NotSupportedException ha sido lanzado. No se puede activar la instancia de tipo MyProject.TwoDScrollView desde el identificador nativo 44f4d310.

System.Exception: no se encontró ningún constructor para MyProject.TwoDScrollView ::. Ctor (System.IntPtr, Android.Runtime.JniHandleOwnership) …… con más texto que sigue ….

Mi diseño XML es el siguiente:

     

Siga las instrucciones en el siguiente enlace sobre el uso de vistas personalizadas en el diseño XML en MonoDroid: http://docs.xamarin.com/android/advanced_topics/using_custom_views_in_a_layout

Los constructores de la clase TwoDScrollView tienen el siguiente aspecto:

 public TwoDScrollView(Context context) : base(context) { initTwoDScrollView(); } public TwoDScrollView(Context context, IAttributeSet attrs) : base(context, attrs) { initTwoDScrollView(); } public TwoDScrollView(Context context, IAttributeSet attrs, int defStyle) : base(context, attrs, defStyle) { initTwoDScrollView(); } 

Los mismos constructores existen en la versión de C # como en la versión de Java (que puede encontrar en el enlace de arriba). Alguna idea sobre lo que podría estar yendo mal? Puedo publicar el código completo de C # de mi TwoDScrollView si a alguien le gustaría verlo. Es esencialmente lo mismo que el bit de código de Java para bit, excepto reescrito en C #.

¡Gracias por cualquier ayuda!

¡Felicidades! Has golpeado una abstracción con fugas. : – /

El problema es el siguiente: para bien o para mal , las llamadas a métodos virtuales de los constructores invocan la implementación del método más derivado. C # es lo mismo que Java a este respecto; considere el siguiente progtwig:

 using System; class Base { public Base () { Console.WriteLine ("Base..ctor"); M (); } public virtual void M () { Console.WriteLine ("Base.M"); } } class Derived : Base { public Derived () { Console.WriteLine ("Derived..ctor"); } public override void M () { Console.WriteLine ("Derived.M"); } } static class Demo { public static void Main () { new Derived (); } } 

Cuando se ejecuta, la salida es:

 Base..ctor Derived.M Derived..ctor 

Es decir, el método Derived.M() se invoca antes de que se haya ejecutado el constructor Derived .

En Mono para Android, las cosas se vuelven más … complicadas. El constructor de Android Callable Wrapper (ACW) es invocado por Java y es responsable de crear la instancia par C # y mapear la instancia Java a la instancia C # . Sin embargo , si se invoca un método virtual desde el constructor de Java, el método se enviará antes de que haya una instancia de C # para invocar el método.

Deja que se hunda un poco.

No sé qué método desencadena el escenario para su código específico (el fragmento de código que proporcionó funciona bien), pero tenemos un ejemplo que coincide con este escenario: LogTextBox anula la propiedad TextView.DefaultMovementMethod y el constructor de TextView invoca el método getDefaultMovementMethod() El resultado es que Android intenta invocar LogTextBox.DefaultMovementMethod antes de que LogTextBox una instancia de LogTextBox .

Entonces, ¿qué hace Mono para Android? Mono para Android creó el ACW y, por lo tanto, sabe a qué tipo de C # debe delegarse el método getDefaultMovementMethod() . Lo que no tiene es una instancia, porque uno no ha sido creado. Entonces Mono para Android crea una instancia del tipo apropiado … a través del (IntPtr, JniHandleOwnership) y genera un error si no se puede encontrar este constructor.

Una vez que el constructor de TextView (en este caso) termina de ejecutarse, se ejecutará el constructor de ACW de LogTextBox , en cuyo momento Mono para Android irá “aha! Ya hemos creado una instancia de C # para esta instancia de Java”, y luego invocará el constructor apropiado en la instancia ya creada. Lo que significa que para una sola instancia, se ejecutarán dos constructores: el (IntPtr, JniHandleOwnership) y (más tarde) el constructor (Context, IAttributeSet, int) .

El mensaje de error dice:

System.Exception: No constructor found for MyProject.TwoDScrollView::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)

Intente agregar un constructor como dice y vea si eso ayuda:

public TwoDScrollView (IntPtr a, JniHandleOwnership b) : base (a, b) { }

Tuve el mismo problema con una imagen personalizada y la respuesta para jpobst ciertamente solucionó el problema por completo:

 public CircularImageView(Context context) :base(context) { init (context, null, 0); } public CircularImageView(Context context, IAttributeSet attrs) : base(context, attrs) { init (context, attrs, Resource.Attribute.circularImageViewStyle); } public CircularImageView(Context context, IAttributeSet attrs, int defStyle) :base(context, attrs, defStyle) { init(context, attrs, defStyle); } public CircularImageView (IntPtr a, JniHandleOwnership b) : base (a, b) { } 

Estaba usando un renderizador de vista de lista personalizado, pero ninguna de las soluciones me funcionó. Pero retrasar la base.Dispose método de base.Dispose me ayudó a arreglar el locking, probablemente esto le da al Android mono, la oportunidad de inicializar la instancia de proxy.

 Xamarin.Forms.Device.BeginInvokeOnMainThread(base.Dispose); 

¡No veo ningún accidente ahora!