4

我正在尝试将 TabActivity 与 MvvmCross 一起使用,但我在框架代码中收到 NullReferenceException,因为传入 OnViewCreate 的 viewModelLoader 为 null

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 中的代码如下所示:

[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

我将如何解决这个问题?

此外,我的 ViewModel 已设置为 TabHostViewModel 具有每个标签页 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 Widget/View 一样工作......我已经读过有性能以这种方式与 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; }
}

...

作为第二种选择的变体(看起来这是您在这个问题中尝试做的事情),如果您的各个选项卡 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 中的导航/链接总是关于 ViewModels,而不是关于 Views。这个想法是应用程序首先构建 ViewModel - 每个客户端平台简单地决定如何在屏幕上表示这些 ViewModel。这可能会在 iPad 和 WinRT 开发中继续并进一步发展,其中拆分视图、弹出窗口等将很常见。

进一步说明...如果您有复杂的 ViewModel 构造,或者您需要为某些 ViewModel(例如单例)使用特殊的生命周期,那么也可以在您的“核心应用程序”中使用自定义 ViewModelLocators - 这些将允许如果您愿意,您可以更改/控制动态 ViewModel 创建......但我怀疑这个问题并非如此。


感谢您问题中的详细信息 - 抱歉提供高级选项。我将在今天晚些时候或本周末尝试通过更好的解释来增强这个答案。


Mvx 也仍然对想法持开放态度......所以我们可以修改代码以使用您的原始代码......这不会那么难,我可以看到一些理由......我会有一个想一想……

于 2012-04-20T10:49:50.417 回答