MonoDroid: ошибка при вызове конструктора пользовательского представления - TwoDScrollView
Я создаю приложение для Android, которое использует специально созданный TwoDScrollView, найденный здесь:
http://blog.gorges.us/2010/06/android-two-dimensional-scrollview/
Этот же класс можно найти, ссылаясь на несколько других веб-сайтов, а другие на Qaru задали вопросы по этому поводу. Я использовал его в предыдущем приложении для Android, которое я создавал с использованием Java/Eclipse, и у меня был успех.
В моем текущем приложении я хотел использовать С# и MonoDroid. Я решил переписать весь класс TwoDScrollView на С#. После его перезаписи, а затем, используя его в XML-макете, я получаю следующие исключения при попытке запустить мой код:
Исправлено System.NotSupportedException. Не удалось активировать экземпляр типа MyProject.TwoDScrollView из встроенного дескриптора 44f4d310.
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>
В соответствии с инструкциями по следующей ссылке, используя пользовательские представления в формате XML в MonoDroid: 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();
}
В версии С# существуют те же самые конструкторы, что и в версии Java (которую вы можете найти в приведенной выше ссылке). Любая идея о том, что может пойти не так? Я могу опубликовать полный код С# моего TwoDScrollView, если кто-то захочет его увидеть. Это по сути то же, что и бит кода Java для бит, кроме перезаписываемого в С#.
Спасибо за любую помощь!
Ответы
Ответ 1
Поздравляем! Вы попали в пропущенную абстракцию.: -/
Проблема заключается в следующем: для лучшего или худшего, вызовы виртуальных методов от конструкторов вызывают реализацию самого производного метода. С# - это то же самое, что и 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 для Android все становится еще сложнее. Конструктор Android Callable Wrapper (ACW) вызывается Java и отвечает за создание peer С# и сопоставление экземпляра Java с экземпляром С#. Однако, если виртуальный метод вызывается из конструктора Java, тогда метод будет отправлен до того, как будет экземпляр С# для вызова метода на!
Пусть это немного утонет.
Я не знаю, какой метод запускает сценарий для вашего конкретного кода (фрагмент кода, который вы предоставили, отлично работает), но у нас есть образец, который использует этот сценарий: LogTextBox переопределяет TextView.DefaultMovementMethod, а конструктор TextView
вызывает метод getDefaultMovementMethod()
. В результате Android пытается вызвать LogTextBox.DefaultMovementMethod
до появления экземпляра LogTextBox
.
Итак, что делает Mono для Android? Mono для Android создал ACW и, таким образом, знает, какой тип С# должен использовать метод getDefaultMovementMethod()
. То, что у него нет, есть экземпляр, потому что он не был создан. Поэтому Mono для Android создает экземпляр соответствующего типа... через конструктор (IntPtr, JniHandleOwnership)
и генерирует ошибку, если этот конструктор не найден.
Как только завершится выполнение конструктора TextView
(в этом случае) TextView
, будет выполнен конструктор ACW LogTextBox
, после чего Mono для Android пойдет "ага! мы уже создали экземпляр С# для этого экземпляра Java" , и затем вызовет соответствующий конструктор на уже созданном экземпляре. Это означает, что для одного экземпляра будут выполняться два конструктора: конструктор (IntPtr, JniHandleOwnership)
и (позже) (Context, IAttributeSet, int)
конструктор.
Ответ 2
В сообщении об ошибке говорится:
System.Exception: No constructor found for MyProject.TwoDScrollView::.ctor(System.IntPtr, Android.Runtime.JniHandleOwnership)
Попробуйте добавить конструктор, как он говорит, и посмотрите, помогает ли это:
public TwoDScrollView (IntPtr a, JniHandleOwnership b) : base (a, b)
{
}
Ответ 3
У меня была та же проблема с пользовательским представлением изображения, и ответ для 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)
{
}