web-dev-qa-db-fra.com

MonoDroid: Erreur lors de l'appel du constructeur de la vue personnalisée - TwoDScrollView

Je crée une Android qui utilise le TwoDScrollView personnalisé trouvé ici:

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

Cette même classe peut être trouvée référencée sur plusieurs autres sites Web, et d'autres sur Stack Overflow ont posé des questions à ce sujet. Je l'utilisais dans une précédente application Android que je construisais en utilisant Java/Eclipse, et j'avais du succès.

Avec mon application actuelle, je voulais utiliser C # et MonoDroid. J'ai décidé de réécrire toute la classe TwoDScrollView en C #. Après l'avoir réécrit, puis l'avoir utilisé dans du XML de mise en page, j'obtiens les exceptions suivantes lorsque j'essaie d'exécuter mon code:

System.NotSupportedException a été levée. Impossible d'activer l'instance de type MyProject.TwoDScrollView à partir du descripteur natif 44f4d310.

System.Exception: aucun constructeur trouvé pour MyProject.TwoDScrollView ::. Ctor (System.IntPtr, Android.Runtime.JniHandleOwnership) ...... avec plus de texte qui suit ....

Ma mise en page XML est la suivante:

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

Selon les instructions du lien suivant sur l'utilisation des vues personnalisées dans la mise en page XML dans MonoDroid: http://docs.xamarin.com/Android/advanced_topics/using_custom_views_in_a_layout

Les constructeurs de la classe TwoDScrollView se présentent comme suit:

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

Les mêmes constructeurs existent dans la version C # comme dans la version Java (que vous pouvez trouver sur le lien ci-dessus). Avez-vous une idée de ce qui pourrait mal tourner? Je peux publier le code C # complet de mon TwoDScrollView si quelqu'un veut le voir. Il est essentiellement le même que le code Java code pour bit - sauf réécrit en C #.

Merci pour toute aide!

43
David

Toutes nos félicitations! Vous avez atteint une abstraction qui fuit. : - /

Le problème est le suivant: pour le meilleur ou pour le pire , les appels de méthode virtuelle des constructeurs invoquent l'implémentation de méthode la plus dérivée. C # est identique à Java à cet égard; envisager le programme suivant:

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

Lors de l'exécution, la sortie est:

Base..ctor
Derived.M
Derived..ctor

Autrement dit, la méthode Derived.M() est invoquée avant l'exécution du constructeur Derived.

Dans Mono pour Android, les choses deviennent plus ... compliquées. Le constructeur de Android Callable Wrapper (ACW) est appelé par Java et est responsable de la création de l'instance homologue C # et du mappage de Java par exemple à l'instance C # . Cependant , si une méthode virtuelle est invoquée à partir du constructeur Java, alors la méthode sera distribuée avant qu'il y ait une instance C # pour invoquer la méthode!

Laissez cela couler un peu.

Je ne sais pas quelle méthode déclenche le scénario pour votre code spécifique (le fragment de code que vous avez fourni fonctionne bien), mais nous avons un exemple qui correspond à ce scénario: LogTextBoxremplace la propriété TextView.DefaultMovementMethod , et le constructeur TextView invoque la méthode getDefaultMovementMethod() . Le résultat est que Android essaie d'invoquer LogTextBox.DefaultMovementMethod Avant même qu'une instance de LogTextBox existe.

Alors, que fait Mono pour Android? Mono pour Android a créé l'ACW, et sait donc à quel type C # la méthode getDefaultMovementMethod() doit être déléguée . Ce qu'il n'a pas, c'est une instance, car aucune n'a été créée. Donc, Mono pour Android crée une instance du type approprié ... via le constructeur (IntPtr, JniHandleOwnership), Et génère une erreur si ce constructeur est introuvable.

Une fois que le constructeur TextView (dans ce cas) a terminé son exécution, le constructeur ACW du LogTextBox s'exécutera, auquel cas Mono pour Android ira "aha! We" J'ai déjà créé une instance C # pour cette Java instance ", et puis invoquera le constructeur approprié sur l'instance déjà créée. Cela signifie que pour une seule instance, deux constructeurs seront exécutés: le constructeur (IntPtr, JniHandleOwnership) , et (plus tard) le constructeur (Context, IAttributeSet, int) .

87
jonp

Le message d'erreur dit:

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

Essayez d'ajouter un constructeur comme il est dit et voyez si cela aide:

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

27
jpobst

J'ai eu le même problème avec une image personnalisée et la réponse de jpobst a certainement résolu le problème complètement:

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)
        {
        }
11
Amir Kotb

J'utilisais un rendu d'affichage de liste personnalisé, mais aucun des contournements n'a fonctionné pour moi. Mais retarder le base.Dispose la méthode m'a aidé à corriger le plantage, cela donne probablement au mono Android, la possibilité d'initialiser l'instance de proxy.

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

Je ne vois aucun crash maintenant!

1
Anonymous