4

MvvmCross で TabActivity を使用しようとしていますが、OnViewCreate に渡された viewModelLoader が null であるため、フレームワーク コードで NullReferenceException を取得しています。

namespace Cirrious.MvvmCross.ExtensionMethods
{
    public static class MvxViewExtensionMethods
    {
        public static void OnViewCreate<TViewModel>(this IMvxView<TViewModel> view, Func<TViewModel> viewModelLoader)
            where TViewModel : class, IMvxViewModel
        {
            if (view.ViewModel != null)
                return;

            var viewModel = viewModelLoader();
            viewModel.RegisterView(view);
            view.ViewModel = (TViewModel)viewModel;
        }

これは、ViewModel 経由ではなく直接ビューを読み込もうとしていることが原因であると思われます。私の TabHost アクティビティのコードは次のようになります。

[Activity(Label = "TabHost")]
    public class TabHostView : MvxBindingTabActivityView<TabHostViewModel>
    {
        protected override void OnViewModelSet()
        {
            SetContentView(Resource.Layout.Page_TabHostView);
            var tabHostWidget = this.TabHost;

            TabHost.TabSpec spec;     // Resusable TabSpec for each tab
            Intent intent;            // Reusable Intent for each tab
            // Create an Intent to launch an Activity for the tab (to be reused)
            intent = new Intent(this, typeof(HomeView));
            intent.AddFlags(ActivityFlags.NewTask);

            // Initialize a TabSpec for each tab and add it to the TabHost
            spec = tabHostWidget.NewTabSpec("home");
            spec.SetIndicator("Home", Resources.GetDrawable(Resource.Drawable.icon_home));
            spec.SetContent(intent);
            tabHostWidget.AddTab(spec);
//... more tabs

どうすればこの問題を回避できますか?

また、TabHostViewModel が各タブ ページ ViewModel のプロパティを持つように、ViewModel が設定されています。これらは、プロパティの get アクセサーが呼び出されたときにのみモデルからデータを取得するという点で怠惰です。

したがって、タブ ページの axml レイアウトにデータ バインディングがある場合、おそらくパスは TabHostViewModel がコンテキスト (ルート) であると想定する必要がありますか?

どうもありがとう、ジェイソン

4

1 に答える 1

6

些細なレベルでは、フレームワークにインテントを作成するように依頼することで、現在の問題を解決できると思います。

        TabHost.TabSpec spec;     // Resusable TabSpec for each tab
        Intent intent;            // Reusable Intent for each tab
        // Create an Intent to launch an Activity for the tab (to be reused)
        intent = base.CreateIntentFor<HomeViewModel>();
        intent.AddFlags(ActivityFlags.NewTask);

より完全なレベルで...


Android と MvvmCross の両方で、タブ付きページに取り組む方法は複数あります。

Android では、個々のタブ内のビューのすべての axml を直接含むレイアウトを使用して、TabActivity に取り組むことを選択します。この方法で作業する場合、 「通常」と同じようにデータバインディングを直接使用できると思います-個々のタブは、通常の子Androidウィジェット/ビューと同じように機能するはずです...パフォーマンスがあることを読みましたAndroid タブでこのように作業することには利点がありますが、通常、私はこのようには作業しません。

次に、私が一般的に行っている方法ですが、各タブを個別のアクティビティとして扱い、それらの各アクティビティをメイン タブ ViewModel の子 ViewModel にリンクすることで、TabActivity に取り組むことができます。(この構造の絵を描いて、今日中にアップロードしようと思います!)

これを行うことを選択した場合、従うべき良い例はカンファレンスのものです - https://github.com/slodge/MvvmCross/blob/master/Sample%20-%20CirriousConference/Cirrious.Conference.UI.Droid/Views /HomeView.cs

この会議の例では、タブの仕様はspec.SetContent(intent)、タブ アクティビティの基本クラス メソッドを使用してインテントが作成された場所を使用して初期化されCreateIntentForます。関連するコードは次のとおりです。

    protected override void OnViewModelSet()
    {
        SetContentView(Resource.Layout.Page_Home);

        TabHost.TabSpec spec;     // Resusable TabSpec for each tab
        Intent intent;            // Reusable Intent for each tab

        // Initialize a TabSpec for each tab and add it to the TabHost
        spec = TabHost.NewTabSpec("welcome");
        spec.SetIndicator(this.GetText("Welcome"), Resources.GetDrawable(Resource.Drawable.Tab_Welcome));
        spec.SetContent(CreateIntentFor(ViewModel.Welcome));
        TabHost.AddTab(spec);

        spec = TabHost.NewTabSpec("sessions");
        spec.SetIndicator(this.GetText("Sessions"), Resources.GetDrawable(Resource.Drawable.Tab_Sessions));
        spec.SetContent(CreateIntentFor(ViewModel.Sessions));
        TabHost.AddTab(spec);

        spec = TabHost.NewTabSpec("favorites");
        spec.SetIndicator(this.GetText("Favorites"), Resources.GetDrawable(Resource.Drawable.Tab_Favorites));
        spec.SetContent(CreateIntentFor(ViewModel.Favorites));
        TabHost.AddTab(spec);

        spec = TabHost.NewTabSpec("tweets");
        spec.SetIndicator(this.GetText("Tweets"), Resources.GetDrawable(Resource.Drawable.Tab_Tweets));
        spec.SetContent(CreateIntentFor(ViewModel.Twitter));
        TabHost.AddTab(spec);
    }

対応する最上位の ViewModel は次のようになります。

public class HomeViewModel
    : MvxBaseViewModel
{
    public HomeViewModel()
    {
        Welcome = new WelcomeViewModel();
        Sessions = new SessionsViewModel();            
        Twitter = new TwitterViewModel();
        Favorites = new FavoritesViewModel();
    }

    public FavoritesViewModel Favorites { get; private set; }
    public WelcomeViewModel Welcome { get; private set; }
    public SessionsViewModel Sessions { get; private set; }
    public TwitterViewModel Twitter { get; private set; }
}

...

その2番目の選択肢のバリエーションとして(そして、これがこの質問でやろうとしていることのように見えます)、個々のタブViewModelが実際には互いに関連していない場合、または「親」TabHost ViewModelに関連していない場合は、次のことができますオンデマンドで作成された新しい ViewModel に各タブをリンクします。公開されているサンプルのどれもこれを行うとは思いませんが、必要な場合、この形式は次のようになります。

        spec = TabHost.NewTabSpec("newTab");
        spec.SetIndicator("new tab text"), Resources.GetDrawable(Resource.Drawable.Tab_Icon));
        spec.SetContent(CreateIntentFor<NewViewModel>(new { constructorParameter1 = "value1", constructorParameter2 = "value2" }));
        TabHost.AddTab(spec);

ここには根底にある哲学があることに注意してください。これは MvvmCross の意見の一部です。mvx でのナビゲーション/リンクは常に ViewModel に関するものであり、View に関するものではありません。アプリケーションは ViewModel ファーストで構築されるという考え方です。各クライアント プラットフォームは、画面上でこれらの ViewModel を表現する方法を決定するだけです。これは、分割ビュー、ポップアップなどが一般的になる iPad および WinRT の開発で継続し、さらに進化する可能性があります。

もう 1 つ注意してください... ViewModel の構築が複雑な場合、または一部の ViewModel (シングルトンなど) に特別な有効期間を使用する必要がある場合は、「コア アプリケーション」内でカスタム ViewModelLocators を使用することもできます。必要に応じて動的ViewModelの作成を変更/制御します...しかし、これはこの質問には当てはまらないと思います。


質問の詳細をありがとう-高度なオプションを投入して申し訳ありません。今日の後半または今週末にもう少し説明を加えて、この回答を強化しようとします。


Mvx はまだアイデアを受け入れています...そのため、元のコードで動作するようにコードを変更できます...それを行うのはそれほど難しいことではなく、それを正当化する理由がいくつかわかります...考えてみてください...

于 2012-04-20T10:49:50.417 に答える