Fragments in TabLayout do not bind to viewmodel

a 夏天 提交于 2019-12-23 07:03:27

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!