33

sto costruendo un'applicazione Android che utilizza il TwoDScrollView fuoriserie trovato qui:MonoDroid: errore quando si chiama il costruttore di visualizzazione personalizzata - TwoDScrollView

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

Questa stessa classe può essere trovata riferimento a diversi altri siti web e altri su Stack Overflow hanno posto domande riguardo ad esso. Lo stavo usando in una precedente applicazione Android che stavo creando usando Java/Eclipse, e stavo avendo successo.

Con la mia applicazione corrente, volevo usare C# e MonoDroid. Ho deciso di riscrivere l'intera classe TwoDScrollView in C#. Dopo averlo riscritto e utilizzato in XML di layout, ottengo le seguenti eccezioni quando provo a eseguire il mio codice:

System.NotSupportedException è stato lanciato. Impossibile attivare l'istanza di tipo MyProject.TwoDScrollView dall'impugnatura nativa 44f4d310.

System.Exception:. Nessun costruttore trovata per MyProject.TwoDScrollView :: ctor (System.IntPtr, Android.Runtime.JniHandleOwnership) ...... con più testo che segue ....

mio layout XML è la seguente:

<?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> 

per le istruzioni al seguente link sull'utilizzo di visualizzazioni personalizzate nel layout XML in MonoDroid: http://docs.xamarin.com/android/advanced_topics/using_custom_views_in_a_layout

I costruttori alla classe sguardo TwoDScrollView come segue:

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

esistono le stesse costruttori nella versione C#, come nella versione Java (che potete trovare al link qui sotto). Qualche idea su cosa potrebbe andare storto? Posso pubblicare il codice C# completo del mio TwoDScrollView se qualcuno vorrebbe vederlo. È essenzialmente uguale al bit di codice Java per bit, tranne che riscritto in C#.

Grazie per qualsiasi aiuto!

risposta

70

Congratulazioni! Hai colpito un'astrazione che perde. : -/

Il problema è questo: for better or worse, le chiamate di metodo virtuali dai costruttori richiamano l'implementazione del metodo più derivato. C# è uguale a Java in questo senso; considerare il seguente programma:

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

Quando viene eseguito, il risultato è:

Base..ctor 
Derived.M 
Derived..ctor 

Cioè, il metodo Derived.M() viene richiamato prima del costruttore Derived ha eseguito.

In Mono per Android, le cose si fanno più complicate .... Il costruttore di Android Callable Wrapper (ACW) viene richiamato da Java ed è responsabile della creazione di peer C# instance and mapping the Java instance to the C# instance. Tuttavia, se un metodo virtuale viene richiamato dal costruttore Java, il metodo si spediranno prima che ci sia un'istanza di C# per richiamare il metodo su!

lasciare che affondare in un po '.

Non so quale metodo sta provocando lo scenario per il vostro codice specifico (il frammento di codice che hai fornito funziona bene), ma abbiamo un campione che colpisce questo scenario: LogTextBoxoverrides la TextView.DefaultMovementMethod proprietà, e il TextView constructor invokes the getDefaultMovementMethod() method. Il risultato è che Android tenta di richiamare LogTextBox.DefaultMovementMethod prima che esista anche un'istanza LogTextBox.

Così che cosa Mono per Android fare? Mono per Android ha creato l'ACW, e quindi sa che C# tipo il metodo getDefaultMovementMethod() dovrebbe essere delegato alla. Quello che non ha è un'istanza, perché non ne è stata creata una. Quindi, Mono per Android crea un'istanza del tipo appropriato ... tramite il costruttore (IntPtr, JniHandleOwnership) e genera un errore se questo costruttore non può essere trovato.

Una volta che la (in questo caso) TextView costruttore termina l'esecuzione, il costruttore ACW LogTextBox s' eseguiranno, a quel punto Mono per Android andrà 'aha! Abbiamo già creato un'istanza di C# per questa istanza Java', e quindi invoca il costruttore appropriato sull'istanza già creata. Significa che per una singola istanza verranno eseguiti due costruttori: lo (IntPtr, JniHandleOwnership) constructor e (successivamente) lo (Context, IAttributeSet, int) constructor.

+0

grazie mille! E 'stato molto utile. Ho bisogno di rileggere il tuo post e lasciarlo affondare un po ', e quindi dare un'occhiata al mio codice con questa nuova conoscenza e vedere dove potrebbe essere il mio problema. Pubblicherò un altro commento quando trovo qualcosa. – David

+0

Ho un codice semplice che riproduce il problema (mi dispiace per il lungo periodo di tempo trascorso dalla domanda originale). Utilizzando l'esempio di codice sopra, è sufficiente aggiungere un metodo: public override void RequestLayout(). Overriding RequestLayout riproduce il problema. Inoltre, sovrascrivendo OnLayout() lo riprodurrà anche ... ma non altrettanto attendibilmente (non riesco ancora a capire i casi esatti in cui avviene). – David

+0

Ciao Jonp, è possibile che questo problema sia stato risolto nell'ultima versione di Xamarin per Android 4.12.2? – ForceMagic

24

Il messaggio di errore dice:

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

Prova ad aggiungere un costruttore come si dice e vedere se questo aiuta:

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

+2

Intendi dire che dovrei provare la risposta logica? ;-) Quindi, sì, il tuo suggerimento risolve davvero il problema come descritto nella mia domanda originale (come in: si sbarazza del messaggio di errore, ma il comportamento dell'applicazione è ancora errato - la vista non viene visualizzata), ma in realtà non risolve il problema che è al centro del problema: perché è addirittura alla ricerca di quel costruttore in primo luogo? Il costruttore che chiama non è uno dei costruttori standard per una classe di visualizzazione personalizzata Android. I costruttori standard che si suppone creino sono quelli che ho elencato sopra. – David

+4

@David: Perché sta cercando il costruttore '(IntPtr, JniHandleOwnership)' in primo luogo? Astrazioni che perdono. Vedere la mia risposta: http://stackoverflow.com/a/10603714/83444 – jonp

+0

Ho avuto lo stesso problema con un'istanza di una classe e questo ha risolto il mio problema. – SubqueryCrunch

8

Ho avuto lo stesso problema con un ImageView personalizzato e la risposta per jpobst certamente risolto il problema completamente:

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

usavo elenco personalizzato vista renderer, ma nessuno dei arounds di lavoro ha lavorato per me. Ma il ritardo del metodo base.Dispose mi ha aiutato a risolvere il problema, probabilmente questo dà all'androide mono, la possibilità di inizializzare l'istanza del proxy.

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

Non vedo nessun arresto ora!

+0

dove hai scritto questo? –

+1

All'interno della classe ListViewRenderer, sovrascrivi il metodo Dispose e invoca il Base.Dispose (smaltimento) come indicato sopra –

+0

sembra che abbia risolto il mio problema !! Ho cercato così tanto e nulla ha potuto risolvere ... grazie !!! –

Problemi correlati