问题
Problem
I'm trying to make a very simple Proof of Concept with TabLayout and Fragments using MvvmCross 6.1.2. For this, I implemented an activity with a TabLayout and a ViewPager, which should have two tabs - each one containing a different fragment with just one TextView.
But I'm receiving an exception followed by a crash on runtime, when this activity should be displayed:
The type MvxTabLayoutPresentationAttribute is not configured in the presenter dictionary
Code
This is what my code looks like, which I implemented following the 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 (and FragmentTab2ViewModel.cs likewise)
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 (and FragmentTab2ViewController.cs likewise)
[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 and FragmentTab2View.axml are just LinearLayouts with a TextView)
Questions
- What is the cause of the exception thrown?
- Is this the recommended way to implement a TabLayout with Fragments?
- What can be done to solve this issue, following MvvmCross 6.x good practices?
回答1:
That exception is thrown because that attribute is not registered in the AttributeTypesToActionsDictionary
of the Presenter
.
In the code you can see that in the method RegisterAttributeTypes it's registered but take into account that it is in the MvxAppCompatViewPresenter
. Furthermore in the docs it states that that attribute only works on AppCompat
.
Given that you are getting that exception I can assume that the non-AppCompat presenter is being used, therefore you are using MvxAndroidSetup
.
To solve this make sure you are using AppCompat
classes, in particular inherit from MvxAppCompatSetup
if you have a custom Setup that is where the MvxAppCompatViewPresenter
is set. Also make sure you are using MvxAppCompatApplication
so if forces you to use the AppCompat
version of the Setup
.
Update regarding the comment on the exception MvvmCross.Exceptions.MvxException: ViewPager not found
I think that the problem is that you are navigating to the children viewmodels in the Initialize
instead of doing this after the tabs view is created so the ViewPager may not be initialized yet when you try to navigate to the children, therefore it is not found.
So as in the Playground Viewmodel you should have a command that calls a method to do the navigation on your 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);
}
And as in the Playground View you should call the command in your 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();
}
}
HIH
来源:https://stackoverflow.com/questions/52748984/how-to-properly-use-fragments-inside-tablayout-with-mvvmcross-6-x