33

ich eine Android-Anwendung am Bau, die der custom-built TwoDScrollView hier verwendet gefunden:MonoDroid: Fehler beim Konstruktor von benutzerdefinierter Ansicht aufrufen - TwoDScrollView

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

an mehreren anderen Websites zu finden ist Diese gleiche Klasse verweist und andere auf Stack Overflow haben Fragen dazu gestellt. Ich benutzte es in einer früheren Android-Anwendung, die ich mit Java/Eclipse erstellte, und ich hatte Erfolg.

Mit meiner aktuellen Anwendung wollte ich C# und MonoDroid verwenden. Ich beschloss, die gesamte TwoDScrollView-Klasse in C# neu zu schreiben. Nach dem Umschreiben und dann in einigen XML-Layouts erhalte ich beim Ausführen meines Codes die folgenden Ausnahmen:

System.NotSupportedException wurde ausgelöst. Instanz des Typs MyProject.TwoDScrollView vom systemeigenen Handle 44f4d310 kann nicht aktiviert werden.

System.Exception:. Kein Konstruktor für MyProject.TwoDScrollView gefunden :: Ctor (System.IntPtr, Android.Runtime.JniHandleOwnership) ...... mit mehr Text, dass folgt ....

Mein Layout XML ist wie folgt:

<?xml version="1.0" encoding="utf-8"?> 
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" 
    android:orientation="vertical" 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent" 
    > 

<myproject.TwoDScrollView 
    android:layout_width="fill_parent" 
    android:layout_height="fill_parent"> 

</myproject.TwoDScrollView> 

</RelativeLayout> 

der Anleitung unter folgendem Link auf benutzerdefinierte Ansichten in Layout XML in MonoDroid mit: http://docs.xamarin.com/android/advanced_topics/using_custom_views_in_a_layout

Die Konstrukteure auf die TwoDScrollView Klasse wie folgt aussehen:

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(); 
} 

Die gleichen Konstrukteure in der C# Version existieren, wie in der Java-Version (die Sie auf den obigen Link zu finden). Irgendeine Idee, was könnte schief gehen? Ich kann den vollständigen C# -Code meiner TwoDScrollView veröffentlichen, wenn jemand sie sehen möchte. Es ist im Wesentlichen das gleiche wie der Java-Code Bit für Bit - außer in C# neu geschrieben.

Danke für jede Hilfe!

Antwort

70

Herzlichen Glückwunsch! Sie haben eine undichte Abstraktion erreicht. : -/

Das Problem ist das: for better or worse Aufrufe der virtuellen Methode von Konstruktoren rufen die abgeleitete Methodenimplementierung auf. C# ist in dieser Hinsicht dasselbe wie Java; Sehen Sie das folgende Programm:

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(); 
    } 
} 

Wenn er gestartet wird, ist der Ausgang:

Base..ctor 
Derived.M 
Derived..ctor 

Das heißt, die Derived.M() Methode aufgerufen wird, bevor der Derived Konstruktor ausgeführt wurde.

In Mono für Android werden die Dinge komplizierter. Der Konstruktor Android Callable Wrapper (ACW) wird von Java aufgerufen und ist verantwortlich für die Erstellung des peer C# instance and mapping the Java instance to the C# instance. Allerdings wird, wenn eine virtuelle Methode vom Java-Konstruktor aufgerufen wird, die Methode ausgelöst, bevor eine C# -Instanz zum Aufrufen der Methode vorhanden ist!

Lassen Sie das ein wenig sinken.

Ich weiß nicht, welche Methode das Szenario für Ihre spezifischen Code auslöst (das Codefragment Sie funktioniert gut zur Verfügung gestellt), aber wir haben eine Probe haben, die dieses Szenario trifft: LogTextBoxoverrides die TextView.DefaultMovementMethod Eigenschaft und die TextView constructor invokes the getDefaultMovementMethod() method. Das Ergebnis ist, dass Android versucht, LogTextBox.DefaultMovementMethod aufzurufen, bevor eine LogTextBox Instanz überhaupt existiert.

Was macht Mono für Android? Mono für Android erstellt die ACW und weiß somit, welche C# Typ der getDefaultMovementMethod() Methode delegiert werden sollte. Was es nicht hat, ist eine Instanz, weil eine nicht erstellt wurde. Also Mono für Android erstellt eine Instanz des entsprechenden Typs ... über den (IntPtr, JniHandleOwnership)-Konstruktor und generiert einen Fehler, wenn dieser Konstruktor nicht gefunden werden kann.

Sobald die (in diesem Fall) TextView Konstruktor Ausführung beendet, die LogTextBox ‚s ACW Konstruktor wird ausgeführt, an welchem ​​Punkt Mono für Android gehen‚Aha! Wir haben bereits ein C# Beispiel für diese Java-Instanz erstellt‘, und wird dann aufrufen den entsprechenden Konstruktor auf die bereits erstellte Instanz. Das bedeutet, dass für eine einzelne Instanz zwei Konstruktoren ausgeführt werden: (IntPtr, JniHandleOwnership) constructor und (später) (Context, IAttributeSet, int) constructor.

+0

vielen dank! Das war sehr hilfreich. Ich muss deinen Beitrag erneut lesen und ihn ein wenig sinken lassen. Dann schaue ich mir meinen Code mit diesem neuen Wissen an und sehe, wo mein Problem liegen könnte. Ich werde einen weiteren Kommentar posten, wenn ich etwas finde. – David

+0

Ich habe einfachen Code, der das Problem reproduziert (Entschuldigung für die lange Zeit seit der ursprünglichen Frage). Fügen Sie mithilfe des obigen Codebeispiels einfach eine Methode hinzu: public override void RequestLayout(). Override RequestLayout reproduziert das Problem. Überschreiben OnLayout() wird es auch reproduzieren ... aber nicht ganz so zuverlässig (immer noch nicht herausfinden, die genauen Fälle, in denen es tut). – David

+0

Hallo Jonp, ist es möglich, dass dieses Problem jetzt in der neuesten Xamarin Android Version 4.12.2 behoben ist? – ForceMagic

24

Die Fehlermeldung lautet:

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

Versuchen einen Konstruktor Hinzufügen wie er sagt, und sehen, ob das hilft:

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

+2

Sie meinen, ich sollte die logische Antwort versuchen? ;-) Also, ja, dein Vorschlag behebt tatsächlich das Problem wie beschrieben meine ursprüngliche Frage (wie in: es wird die Fehlermeldung los, aber das Verhalten der Anwendung ist immer noch falsch - die Ansicht wird nicht angezeigt), aber Es löst nicht wirklich das Problem, das den Kern des Problems darstellt: Warum sucht es überhaupt überhaupt nach diesem Konstruktor? Der aufgerufene Konstruktor gehört nicht zu den Standardkonstruktoren für eine benutzerdefinierte Android-Ansichtsklasse. Die Standardkonstruktoren, die man erstellen soll, sind die, die ich oben aufgelistet habe. – David

+4

@David: Warum sucht es überhaupt nach dem '(IntPtr, JniHandleOwnership)' Konstruktor? Undichte Abstraktionen. Siehe meine Antwort: http://stackoverflow.com/a/10603714/83444 – jonp

+0

Ich hatte das gleiche Problem mit der Instanziierung einer Klasse und das hat mein Problem gelöst. – SubqueryCrunch

8

hatte ich das gleiche Problem mit einem benutzerdefinierten Imageview und die Antwort für jpobst fixierte sicherlich das Problem vollständig:

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) 
     { 
     } 
1

ich benutzerdefinierte Listenansicht Renderer wurde mit, aber keiner des Arbeitsarounds für mich gearbeitet. Aber die base.Dispose Methode zu verzögern half mir, den Absturz zu beheben, wahrscheinlich gibt dies dem Mono-Android die Chance, die Proxy-Instanz zu initialisieren.

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

Ich sehe keine Abstürze jetzt!

+0

wo hast du das geschrieben? –

+1

Innerhalb Ihrer ListViewRenderer-Klasse, überschreiben Sie die Dispose-Methode und rufen Sie die Base.dispose (disposing) wie oben erwähnt –

+0

scheint es, dass es mein Problem gelöst hat !! Ich suchte so viel und nichts konnte lösen ... danke !!! –