c# - 如何通过 MvvmCross 6.x 正确使用 TabLayout 内的 Fragment

标签 c# android xamarin mvvmcross android-tablayout

问题

我正在尝试使用 MvvmCross 6.1.2 使用 TabLayout 和 Fragments 进行非常简单的概念验证。为此,我使用 TabLayout 和 ViewPager 实现了一项 Activity ,该 Activity 应该有两个选项卡 - 每个选项卡都包含一个不同的 fragment ,其中只有一个 TextView。

但是当应该显示此 Activity 时,我收到一个异常,然后在运行时崩溃:

The type MvxTabLayoutPresentationAttribute is not configured in the presenter dictionary

代码

这就是我的代码,我按照 Playground example 实现了它和 Documentation :

AppStart.cs:

public class AppStart : MvxAppStart
{
    private readonly IMvxNavigationService _mvxNavigationService;

    public AppStart(IMvxApplication app, IMvxNavigationService mvxNavigationService)
        : base(app, mvxNavigationService)
    {
        _mvxNavigationService = mvxNavigationService;
    }

    protected override void NavigateToFirstViewModel(object hint = null)
    {
        Mvx.Resolve<IMvxNavigationService>().Navigate<TabLayoutViewModel>();
    }
}

TabLayoutViewModel.cs

public class TabLayoutViewModel: MvxViewModel
{
    public override async Task Initialize()
    {
        await base.Initialize();

        var tasks = new List<Task>();
        tasks.Add(Mvx.Resolve<IMvxNavigationService>().Navigate<FragmentTab1ViewModel>());
        tasks.Add(Mvx.Resolve<IMvxNavigationService>().Navigate<FragmentTab2ViewModel>());
        await Task.WhenAll(tasks);
    }
}

FragmentTab1ViewModel.cs(同样还有 FragmentTab2ViewModel.cs)

public class FragmentTab1ViewModel : MvxViewModel
{
    public override Task Initialize()
    {
        return base.Initialize();
    }
}

TabLayoutViewController.cs

[MvxActivityPresentation]
[Activity(Label = "", ScreenOrientation = ScreenOrientation.Portrait, LaunchMode = LaunchMode.SingleTask, Theme = "@style/LoginTheme")]
public class TabLayoutViewController: MvxAppCompatActivity<TabLayoutViewModel>
{
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        SetContentView(Resource.Layout.TabLayoutView);

        var set = this.CreateBindingSet<TabLayoutViewController, TabLayoutViewModel>();

        set.Apply();
    }
}

TabLayoutView.axml

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:alwaysDrawnWithCache="false"
    android:background="#f4f4f4"
    android:minWidth="25px"
    android:minHeight="25px">
  <android.support.design.widget.TabLayout
      android:id="@+id/tabsTeste"
      android:layout_width="match_parent"
      android:layout_height="wrap_content"
      android:paddingLeft="16dp"
      app:tabGravity="center"
      app:tabMode="scrollable" />
  <android.support.v4.view.ViewPager
      android:id="@+id/viewpagerTeste"
      android:layout_width="match_parent"
      android:layout_height="match_parent" />
</android.support.design.widget.CoordinatorLayout>

FragmentTab1ViewController.cs(同样还有 FragmentTab2ViewController.cs)

[MvxTabLayoutPresentation(ActivityHostViewModelType = typeof(TabLayoutViewModel), ViewPagerResourceId = Resource.Id.viewpagerTest, TabLayoutResourceId = Resource.Id.tabsTest, Title = "Tab A")]
[Register("smartSolution.coleta.droid.view.FragmentTab1ViewController")]
public class FragmentTab1ViewController : MvxFragment<FragmentTab1ViewModel>
{
    public override Android.Views.View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        base.OnCreateView(inflater, container, savedInstanceState);

        var view = this.BindingInflate(Resource.Layout.FragmentTab1View, null);

        inflater.Inflate(Resource.Layout.FragmentTab1View, container, true);

        var set = this.CreateBindingSet<FragmentTab1ViewController, FragmentTab1ViewModel>();

        set.Apply();

        return view;
    }
}

(FragmentTab1View.axml 和 FragmentTab2View.axml 只是带有 TextView 的 LinearLayout)

问题

  1. 抛出异常的原因是什么?
  2. 这是使用 Fragments 实现 TabLayout 的推荐方法吗?
  3. 遵循 MvvmCross 6.x 良好实践,可以采取哪些措施来解决此问题?

最佳答案

抛出该异常是因为该属性未在 PresenterAttributeTypesToActionsDictionary 中注册。

在代码中你可以看到在方法 RegisterAttributeTypes 中它已注册,但请考虑到它位于 MvxAppCompatViewPresenter 中。此外在 docs它指出该属性仅适用于 AppCompat

鉴于您遇到了该异常,我可以假设正在使用非 AppCompat 演示器,因此您正在使用 MvxAndroidSetup

要解决此问题,请确保您使用的是 AppCompat 类,特别是如果您有自定义安装程序 where 则从 MvxAppCompatSetup 继承。 MvxAppCompatViewPresenter 已设置。还要确保您使用的是 MvxAppCompatApplication 所以如果 forces您可以使用 SetupAppCompat 版本。


更新有关异常 MvvmCross.Exceptions.MvxException: ViewPager not found

的评论

我认为问题在于您正在 Initialize 中导航到 subview 模型,而不是在创建选项卡 View 后执行此操作,因此当您尝试导航时 ViewPager 可能尚未初始化给 children ,因此找不到。

就像 Playground Viewmodel 中那样您应该有一个命令来调用方法来在 ViewModel 上进行导航:

...
ShowInitialViewModelsCommand = new MvxAsyncCommand(ShowInitialViewModels);
...

public IMvxAsyncCommand ShowInitialViewModelsCommand { get; private set; }

...

private async Task ShowInitialViewModels()
{
    var tasks = new List<Task>();
    tasks.Add(Mvx.Resolve<IMvxNavigationService>().Navigate<FragmentTab1ViewModel>());
    tasks.Add(Mvx.Resolve<IMvxNavigationService>().Navigate<FragmentTab2ViewModel>());
    await Task.WhenAll(tasks);
}

Playground View 中所示您应该在 TabLayoutViewController 中调用该命令:

protected override void OnCreate(Bundle bundle)
{
    base.OnCreate(bundle);

    SetContentView(Resource.Layout.TabLayoutView);

    var set = this.CreateBindingSet<TabLayoutViewController, TabLayoutViewModel>();

    set.Apply();

    if (bundle == null)
    {
        ViewModel.ShowInitialViewModelsCommand.Execute();
    }
}

关于c# - 如何通过 MvvmCross 6.x 正确使用 TabLayout 内的 Fragment,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52748984/

相关文章:

c# - 在Treeview MVVM中获取父项

java - 我需要帮助错误 E/AndroidRuntime : FATAL EXCEPTION: main

c# - 跨应用钥匙串(keychain)访问,在哪里配置?

ios - Xamarin iOS 与 VS 2012 中的错误 "Failed to retrieve SDK status from server"

c# - configSource 在 system.serviceModel *或其子部分中不起作用

c# - exe 需要 .NET 桌面运行时

c# - 构建 C# EntityFramework IQueryable 表达式时遇到问题

android - 在添加内容之前必须调用 requestFeature()

android - 上传到服务器时如何减小图像大小

c# - 如何从现有列表中获取与关键字匹配的项目列表并忽略大小写(不区分大小写)?