如何通过 MvvmCross 在 TabLayout 中正确使用 Fragments 6.x
How to properly use Fragments inside TabLayout with MvvmCross 6.x
问题
我正在尝试使用 MvvmCross 6.1.2 使用 TabLayout 和 Fragments 进行非常简单的概念验证。为此,我实现了一个带有 TabLayout 和 ViewPager 的 activity,它应该有两个选项卡 - 每个选项卡包含一个不同的片段,只有一个 TextView。
但是我收到一个异常,然后在运行时崩溃,而这个 activity 应该显示:
The type MvxTabLayoutPresentationAttribute is not configured in the
presenter dictionary
代码
这就是我的代码,我在 Playground example and the 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 的 LinearLayouts)
问题
- 抛出异常的原因是什么?
- 这是用片段实现 TabLayout 的推荐方法吗?
- 按照 MvvmCross 6.x 好的做法,可以做些什么来解决这个问题?
抛出该异常是因为该属性未在 Presenter
的 AttributeTypesToActionsDictionary
中注册。
在代码中,您可以在方法 RegisterAttributeTypes it's registered but take into account that it is in the MvxAppCompatViewPresenter
. Furthermore in the docs 中看到该属性仅适用于 AppCompat
。
鉴于您收到该异常,我可以假设正在使用 non-AppCompat 演示者,因此您正在使用 MvxAndroidSetup
.
要解决此问题,请确保您使用的是 AppCompat
类,特别是如果您的自定义设置 where the MvxAppCompatViewPresenter
is set. Also make sure you are using MvxAppCompatApplication
so if forces 继承自 MvxAppCompatSetup
AppCompat
版本 Setup
.
更新关于异常的评论MvvmCross.Exceptions.MvxException: ViewPager not found
我认为问题是您导航到 Initialize
中的 children 视图模型,而不是在创建选项卡视图后执行此操作,因此当您导航时 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();
}
}
高
问题
我正在尝试使用 MvvmCross 6.1.2 使用 TabLayout 和 Fragments 进行非常简单的概念验证。为此,我实现了一个带有 TabLayout 和 ViewPager 的 activity,它应该有两个选项卡 - 每个选项卡包含一个不同的片段,只有一个 TextView。
但是我收到一个异常,然后在运行时崩溃,而这个 activity 应该显示:
The type MvxTabLayoutPresentationAttribute is not configured in the presenter dictionary
代码
这就是我的代码,我在 Playground example and the 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 的 LinearLayouts)
问题
- 抛出异常的原因是什么?
- 这是用片段实现 TabLayout 的推荐方法吗?
- 按照 MvvmCross 6.x 好的做法,可以做些什么来解决这个问题?
抛出该异常是因为该属性未在 Presenter
的 AttributeTypesToActionsDictionary
中注册。
在代码中,您可以在方法 RegisterAttributeTypes it's registered but take into account that it is in the MvxAppCompatViewPresenter
. Furthermore in the docs 中看到该属性仅适用于 AppCompat
。
鉴于您收到该异常,我可以假设正在使用 non-AppCompat 演示者,因此您正在使用 MvxAndroidSetup
.
要解决此问题,请确保您使用的是 AppCompat
类,特别是如果您的自定义设置 where the MvxAppCompatViewPresenter
is set. Also make sure you are using MvxAppCompatApplication
so if forces 继承自 MvxAppCompatSetup
AppCompat
版本 Setup
.
更新关于异常的评论MvvmCross.Exceptions.MvxException: ViewPager not found
我认为问题是您导航到 Initialize
中的 children 视图模型,而不是在创建选项卡视图后执行此操作,因此当您导航时 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();
}
}
高