33

我建立一個使用定製TwoDScrollView在這裏找到一個Android應用程序:MonoDroid的:錯誤調用自定義視圖的構造函數時 - TwoDScrollView

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

這同一個類可以在其他幾個網站上找到所引用,以及Stack Overflow的其他人提出了有關它的問題。我在之前使用Java/Eclipse構建的Android應用程序中使用它,並且我取得了成功。

從我目前的應用程序,我想用C#和MonoDroid的。我決定用C#重寫整個TwoDScrollView類。重寫後,然後在某些佈局XML中使用它,在嘗試運行我的代碼時,我會看到以下例外情況:

System.NotSupportedException已被拋出。無法從本地句柄44f4d310激活 類型爲MyProject.TwoDScrollView的實例。

System.Exception的:沒有構造發現 MyProject.TwoDScrollView ::構造函數(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的完整C#代碼。它本質上與Java代碼的位相同 - 除了在C#中重寫。

感謝您的幫助!

回答

70

恭喜!你已經發生了漏洞抽象。 : -/

問題是這樣的:for better or worse,虛方法調用構造函數從調用最衍生的方法實現。 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構造函數執行前Derived.M()方法被調用。

在Mono for Android中,事情變得更加複雜。 Android Callable Wrapper (ACW)的構造函數由Java調用,並負責創建peer C# instance and mapping the Java instance to the C# instance但是,如果從Java構造函數調用虛擬方法,那麼在有C#實例調用該方法之前將調度方法

讓我們沉浸在一點。

我不知道哪種方法觸發場景爲您的特定代碼(您提供優秀作品的代碼片段),但我們確實有它打這種情況的例子:LogTextBoxoverridesTextView.DefaultMovementMethod屬性和TextView constructor invokes the getDefaultMovementMethod() method。其結果是,即使存在LogTextBox實例,Android也會嘗試調用LogTextBox.DefaultMovementMethod

那麼Mono for Android是做什麼的? Android的Mono創建了ACW,並且因此知道哪個C#類型getDefaultMovementMethod()方法應委託給。它沒有的是一個實例,因爲它沒有被創建。所以Android的Mono會通過構造函數(IntPtr, JniHandleOwnership)創建合適類型的實例,並且如果找不到此構造函數,則會生成一個錯誤。

一旦(在這種情況下)TextView構造函數完成執行後,LogTextBox的ACW構造函數會執行,在這一點單爲Android會去‘啊哈!我們已經創建了這個Java實例的C#實例’,並且將然後在已經創建的實例上調用適當的構造函數。這意味着對於單個實例,將執行兩個構造函數:(IntPtr, JniHandleOwnership) constructor和(後面的)(Context, IAttributeSet, int) constructor

+0

非常感謝!這非常有幫助。我需要重新閱讀你的文章,並讓它沉澱一點,然後用這些新知識來看看我的代碼,看看我的問題可能在哪裏。當我找到某些東西時,我會再發表一條評論。 – David

+0

我有簡單的代碼來重現問題(抱歉自從原始問題以來已經有很長一段時間了)。使用上面的代碼示例,只需添加一個方法:public override void RequestLayout()。覆蓋RequestLayout重現了這個問題。此外,重寫OnLayout()也會重現它......但並不十分可靠(仍不能確定它所做的確切情況)。 – David

+0

你好,Jonp,這個問題現在是否可以在最新的Xamarin Android版本4.12.2中修復? – ForceMagic

24

錯誤消息說:

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

嘗試添加構造像它說,看看是否有幫助:

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

+2

你的意思是我應該嘗試合乎邏輯的答案? ;-)所以,是的,你的建議確實解決了我原來的問題所描述的問題(例如:它擺脫了錯誤信息,但應用程序的行爲仍然不正確 - 視圖不顯示),但它並沒有真正解決問題的核心問題:爲什麼它甚至在首先尋找構造函數?它調用的構造函數不是Android自定義視圖類的標準構造函數之一。人們應該創建的標準構造函數就是我上面列出的那些構造函數。 – David

+4

@David:爲什麼它首先搜索'(IntPtr,JniHandleOwnership)'構造函數?漏洞抽象。看到我的答案:http://stackoverflow.com/a/10603714/83444 – jonp

+0

我有同樣的問題實例化一個類,這解決了我的問題。 – SubqueryCrunch

8

我有同樣的問題用一個自定義的ImageView和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) 
     { 
     } 
1

我使用自定義列表視圖渲染,但沒有變通的工作對我來說。但推遲base.Dispose方法幫助我修復了崩潰問題,這可能會讓單聲道android有機會初始化代理實例。

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

我現在沒有看到任何崩潰!

+0

你在哪裏寫這個? –

+1

在ListViewRenderer類中,重寫Dispose方法並調用base.Dispose(disposing),如上所述 –

+0

似乎它解決了我的問題!我搜索了很多,沒有什麼可以解決......謝謝! –

相關問題