问题
I've been struggling with this for 2 days now and am quite simply stuck. The binding of the fragments simply will not kick in for some reason. The page is shown correctly and the tabs do work fine. I can swipe from tab 1 to 2 and vice versa. The TextView should show some text from the viewmodel. When I debug the constructors of both fragment viewmodels are executed but the properties on the LoginNotificationViewModel (LoginDescription and LastLoginRequestReceivedOn) never fire.
I've got a view that holds a TabLayout as you can see below:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<include
layout="@layout/header" />
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/main_content"
android:layout_below="@id/header">
<android.support.design.widget.TabLayout
android:id="@+id/sliding_tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
local:tabMode="fixed" />
<android.support.v4.view.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="0px"
android:layout_weight="1"
android:background="@android:color/white" />
</LinearLayout>
</RelativeLayout>
The tabview's code:
using Android.App;
using Android.OS;
using Android.Runtime;
using Android.Support.Design.Widget;
using Android.Support.V4.View;
using Android.Support.V7.Widget;
using Android.Views;
using MvvmCross.Droid.Support.V4;
using Notifier.Adapters;
using Notifier.Android.Fragments;
using Notifier.Classes;
using Notifier.ViewModels;
using System;
using static MvvmCross.Droid.Support.V4.MvxCachingFragmentStatePagerAdapter;
namespace Notifier.Android.Views
{
[Activity(Label = "HomeNotification", Theme = "@style/Theme.NatuurNetwerk.Main", NoHistory = true)]
public class HomeNotificationView : MvxFragmentActivity<HomeNotificationViewModel>
{
private HomeNotificationViewModel _HomeNotificationViewModel;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
_HomeNotificationViewModel = this.ViewModel;
SetContentView(Resource.Layout.HomeNotificationView);
var fragments = new FragmentInfo[]
{
new FragmentInfo("Login", typeof(LoginNotificationFragment), typeof(LoginNotificationViewModel)),
new FragmentInfo("Tekst", typeof(TextNotificationFragment), typeof(TextNotificationViewModel)),
};
var viewPager = FindViewById<ViewPager>(Resource.Id.viewpager);
viewPager.Adapter = new TabsFragmentPagerAdapter(this, SupportFragmentManager, fragments);
// Give the TabLayout to the ViewPager
var tabLayout = FindViewById<TabLayout>(Resource.Id.sliding_tabs);
tabLayout.SetupWithViewPager(viewPager, true);
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
SetContentView(Resource.Layout.HomeNotificationView);
menu.Add(Menu.None, (int)Parameters.NotificationMenuItems.ResetPincode, 0, Resource.String.menuResetPincode);
menu.Add(Menu.None, (int)Parameters.NotificationMenuItems.AddApplication, 0, Resource.String.menuAddApplication);
menu.Add(Menu.None, (int)Parameters.NotificationMenuItems.RemoveApplication, 0, Resource.String.menuRemoveApplication);
return base.OnCreateOptionsMenu(menu);
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
Parameters.NotificationMenuItems menuItemSelected = item.ItemId.GetEnum<Parameters.NotificationMenuItems>();
switch (menuItemSelected)
{
case Parameters.NotificationMenuItems.ResetPincode:
_HomeNotificationViewModel.ResetPincode();
break;
case Parameters.NotificationMenuItems.AddApplication:
break;
case Parameters.NotificationMenuItems.RemoveApplication:
break;
default:
throw new InvalidOperationException(string.Format("Notifier: Invalid menu {0}", menuItemSelected.ToString()));
}
return base.OnOptionsItemSelected(item);
}
protected override void OnResume()
{
base.OnResume();
Toolbar toolbar = FindViewById(Resource.Id.toolbar) as Toolbar;
if (toolbar != null)
{
toolbar.Title = _HomeNotificationViewModel.Title;
toolbar.Subtitle = _HomeNotificationViewModel.SubTitle;
}
}
}
}
.. and it's viewmodel:
namespace Notifier.ViewModels
{
public class HomeNotificationViewModel : ViewModelBase
{
public override string SubTitle => "Notificatie";
public override void InitView()
{
}
public override void InitData()
{
// TODO: Init Data
}
public override void ApplicationSelected()
{
}
public void ResetPincode()
{
DeviceRegistrationHelper.UpdatePincode(null, () =>
{
DetermineAndStartHomeView();
});
}
}
}
One of the fragments involved:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="30dp"
local:MvxBind="Text LoginDescription" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="30dp"
local:MvxBind="Text LastLoginRequestReceivedOn"/>
</LinearLayout>
The login fragment code:
using Android.OS;
using Android.Views;
using Notifier.ViewModels;
namespace Notifier.Android.Fragments
{
public class LoginNotificationFragment : BaseFragment<LoginNotificationViewModel>
{
protected override int LayoutResource => Resource.Layout.LoginNotificationFragment;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.LoginNotificationFragment, container, false);
}
}
}
.. and its base class:
using MvvmCross.Droid.Support.V4;
using Notifier.Android.Views;
using Notifier.ViewModels;
namespace Notifier.Android.Fragments
{
public abstract class BaseFragment<TViewModel> : MvxFragment<TViewModel>
where TViewModel : ViewModelBase
{
protected abstract int LayoutResource { get; }
}
}
The viewmodel:
using Notifier.Classes;
using System;
using System.Linq;
namespace Notifier.ViewModels
{
public class LoginNotificationViewModel : ViewModelBase
{
public override string SubTitle => "Inlog notificatie";
public String Status { get; set; }
public override void InitView()
{
}
public override void InitData()
{
// TODO: Init Data
}
public override void ApplicationSelected()
{
}
public string LoginDescription
{
get => Settings.HasNotification
? string.Format("Er is een inlog verzoek ontvangen voor {0}", ApplicationShortName(Settings.Notification.ApplicationId))
: "Geen openstaande inlogverzoeken";
}
/// <summary>
/// Short name for the application
/// </summary>
private string ApplicationShortName(int applicationId)
{
return Applications.Where(app => app.ApplicationID == applicationId).FirstOrDefault()?.ApplicationEntity.ApplicationShortName;
}
public string LastLoginRequestReceivedOn
{
get => string.Format("Laatste inlogverzoek: {0}",
Settings.HasLastNotificationReceivedOn
? string.Empty
: Settings.LastNotificationReceivedOn.ToString("DD-MM-YYYY HH:MM"));
}
}
}
and (part of) the base class:
namespace Notifier.ViewModels
{
/// <summary>
/// Base class for all view models.
/// </summary>
public abstract class ViewModelBase : MvxViewModel
{
private readonly IPlatformEntrance _platformEntrance;
private readonly IPlatformLog _platformLog;
public string Title => "nNotifier©";
public ViewModelBase()
{
_platformEntrance = Mvx.Resolve<IPlatformEntrance>();
_platformLog = Mvx.Resolve<IPlatformLog>();
InitData();
}
public abstract void InitData();
public abstract string SubTitle { get; }
...
}
}
Here's the PageAdapter:
using Android.Content;
using Android.Support.V4.App;
using MvvmCross.Droid.Support.V4;
namespace Notifier.Adapters
{
public class TabsFragmentPagerAdapter : MvxCachingFragmentStatePagerAdapter
{
FragmentInfo[] _fragments;
public TabsFragmentPagerAdapter(Context context, FragmentManager fragmentManager, FragmentInfo[] fragments)
: base(context, fragmentManager, fragments)
{
_fragments = fragments;
}
// public override int Count => _fragments.Length;
//public override global::Android.Support.V4.App.Fragment GetItem(int position,
// global::Android.Support.V4.App.Fragment.SavedState fragmentSavedState = null)
//{
// return _fragments[position];
//}
//public override ICharSequence GetPageTitleFormatted(int position)
//{
// return _titles[position];
//}
}
}
回答1:
You need to use BindingInflate()
instead of the default Android inflater since it doesn't know how to process the MvxBind properties.
using MvvmCross.Binding.Droid.BindingContext;
...
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignored = base.OnCreateView(inflater, container, savedInstanceState);
var view = this.BindingInflate(Resource.Layout.MyFragmentLayout, container, false);
return view;
}
来源:https://stackoverflow.com/questions/44184070/fragments-in-tablayout-do-not-bind-to-viewmodel