web-dev-qa-db-ja.com

MonoDroid:カスタムビューのコンストラクターを呼び出すときにエラーが発生-TwoDScrollView

ここにあるカスタムビルドのTwoDScrollViewを使用するAndroidアプリケーションを作成しています:

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

この同じクラスは、他のいくつかのWebサイトで参照されており、Stack Overflowの他のWebサイトからも質問が寄せられています。 Java/Eclipseを使用して構築していた以前のAndroidアプリケーションで使用しており、成功していました。

現在のアプリケーションでは、C#とMonoDroidを使用したいと考えていました。私は、C#でTwoDScrollViewクラス全体を書き直すことにしました。書き直して、レイアウトXMLで使用した後、コードを実行しようとすると次の例外が発生します。

System.NotSupportedExceptionがスローされました。ネイティブハンドル44f4d310からMyProject.TwoDScrollView型のインスタンスをアクティブにできません。

System.Exception:MyProject.TwoDScrollView ::。ctor(System.IntPtr、Android.Runtime.JniHandleOwnership)のコンストラクターが見つかりません...続くテキストが続きます...

私のレイアウトXMLは次のとおりです。

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

MonoDroidのレイアウトXMLでのカスタムビューの使用に関する次のリンクの手順に従ってください。 http://docs.xamarin.com/Android/advanced_topics/using_custom_views_in_a_layout

TwoDScrollViewクラスのコンストラクターは次のようになります。

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

C#バージョンには、Javaバージョン(上記のリンクにあります)と同じコンストラクターが存在します。何がうまくいかないかについてのアイデアはありますか?誰かが見たいなら、私のTwoDScrollViewです。これは、基本的にJavaビットごとのコードビットと同じですが、C#で書き直されています。

助けてくれてありがとう!

43
David

おめでとう!漏れやすい抽象化に遭遇しました。 :-/

問題はこれです: 良くも悪くも 、コンストラクターからの仮想メソッド呼び出しは、最も派生したメソッド実装を呼び出します。この点で、C#はJavaと同じです。次のプログラムを検討してください。

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

実行時の出力は次のとおりです。

_Base..ctor
Derived.M
Derived..ctor
_

つまり、Derived.M()メソッドは、Derivedコンストラクターが実行される前に呼び出されます。

Mono for Androidでは、事態はさらに複雑になります。 Android Callable Wrapper(ACW) のコンストラクターはJavaによって呼び出され、 ピアC#インスタンスの作成とJavaのマッピングを担当しますinstance to C#instanceただし、、仮想メソッドがJavaコンストラクターから呼び出された場合、メソッドはディスパッチされますメソッドを呼び出すC#インスタンスが存在する前に!

それを少し沈めましょう。

どのメソッドが特定のコードのシナリオをトリガーしているのかわかりません(指定したコードフラグメントは正常に機能します)が、このシナリオにヒットするサンプルがあります: LogTextBoxオーバーライドTextView.DefaultMovementMethod プロパティ、および TextViewコンストラクターはgetDefaultMovementMethod()メソッドを呼び出します 。その結果、AndroidはLogTextBoxインスタンスが存在する前に_LogTextBox.DefaultMovementMethod_を呼び出そうとします。

では、Mono for Androidは何をしますか? AndroidのMonoはACWを作成したため、どのC#typegetDefaultMovementMethod()メソッドに委任されるべきかを知っています。 。作成されていないため、インスタンスにはありません。したがって、Mono for Androidは、_(IntPtr, JniHandleOwnership)_コンストラクターを介して適切なタイプのインスタンスを作成し、このコンストラクターが見つからない場合はエラーを生成します。

(この場合)TextViewコンストラクターの実行が完了すると、LogTextBoxのACWコンストラクターが実行され、その時点でAndroidのMonoは「あぁ!このJavaインスタンスのC#インスタンス」、およびthenは、既に作成されたインスタンスで適切なコンストラクターを呼び出します。つまり、1つのインスタンスに対して、2つのコンストラクターが実行されます: _(IntPtr, JniHandleOwnership)_ constructor 、および(後で) _(Context, IAttributeSet, int)_ constructor

87
jonp

エラーメッセージは言う:

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

次のようなコンストラクタを追加してみて、それが役立つかどうかを確認してください。

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

27
jpobst

私はカスタム画像ビューでも同じ問題を抱えていましたが、jpobstの答えは確かに問題を完全に修正しました:

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

カスタムリストビューレンダラーを使用していましたが、回避策はありませんでした。しかし、base.Disposeメソッドはクラッシュを修正するのに役立ちました。おそらく、これはモノのAndroidにプロキシインスタンスを初期化する機会を与えます。

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

現在、クラッシュは見当たりません!

1
Anonymous