mvvmcross是否提供选项卡式布局平台? [英] Is there tabbed layout platform provided by mvvmcross?

查看:81
本文介绍了mvvmcross是否提供选项卡式布局平台?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试在xamarin应用程序中添加选项卡式布局. 我使用mvvmcross平台,但是要找到mvvmcross为android和ios提供的选项卡式布局平台并不容易.如果mvvmcross中有任何平台或示例, 请帮助我!谢谢!

I'm trying to add tabbed layout in xamarin app. I use mvvmcross platform, but it is not easy to find the tabbed layout platform provided by mvvmcross for both android and ios. If there is any platform or example in mvvmcross, pls help me! thanks!

推荐答案

TR; DR;

TR;DR;

MvvmCross的文档中,您可以在演示者中找到它( Xamarin.iOS Xamarin.Forms )

In the docs of MvvmCross you'll find it in the presenters (Xamarin.Android, Xamarin.iOS, Xamarin.Forms)

基本上,您必须使用视图的属性进行修饰以生成选项卡.

Basically, you'll have to decore with attributes your views to generate the tabs.

大量示例(这些示例使用的是Mvx 6)

Long examples (these are using Mvx 6)

从MvvmCross存储库中的游乐场项目中提取的示例.

Examples extracted from the Playground project in the MvvmCross repository.

您将拥有一个根选项卡ViewModel,它将成为所有选项卡的容器,每个选项卡均具有一个ViewModel.

You'll have a root tabs ViewModel that will be the container of all of the tabs which will have a ViewModel each.

标签根(有两个根提供了不同的方法来执行相同的操作,并显示一个选项卡可以导航到另一个选项卡,您只应使用其中一个)

Tabs root (there are two roots to provide different ways to do the same thing and to show that one tab can navigate to another one, you should only use one)

public class TabsRootViewModel : MvxNavigationViewModel
{
    public TabsRootViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
    {
        ShowInitialViewModelsCommand = new MvxAsyncCommand(ShowInitialViewModels);
        ShowTabsRootBCommand = new MvxAsyncCommand(async () => await NavigationService.Navigate<TabsRootBViewModel>());
    }

    public IMvxAsyncCommand ShowInitialViewModelsCommand { get; private set; }

    public IMvxAsyncCommand ShowTabsRootBCommand { get; private set; }

    private async Task ShowInitialViewModels()
    {
        var tasks = new List<Task>();
        tasks.Add(NavigationService.Navigate<Tab1ViewModel, string>("test"));
        tasks.Add(NavigationService.Navigate<Tab2ViewModel>());
        tasks.Add(NavigationService.Navigate<Tab3ViewModel>());
        await Task.WhenAll(tasks);
    }

    private int _itemIndex;

    public int ItemIndex
    {
        get { return _itemIndex; }
        set
        {
            if (_itemIndex == value) return;
            _itemIndex = value;
            Log.Trace("Tab item changed to {0}", _itemIndex.ToString());
            RaisePropertyChanged(() => ItemIndex);
        }
    }
}

public class TabsRootBViewModel : MvxNavigationViewModel
{
    public TabsRootBViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
    {
        ShowInitialViewModelsCommand = new MvxAsyncCommand(ShowInitialViewModels);
    }

    public IMvxAsyncCommand ShowInitialViewModelsCommand { get; private set; }

    private async Task ShowInitialViewModels()
    {
        var tasks = new List<Task>();
        tasks.Add(NavigationService.Navigate<Tab1ViewModel, string>("test"));
        tasks.Add(NavigationService.Navigate<Tab2ViewModel>());
        await Task.WhenAll(tasks);
    }

    private int _itemIndex;

    public int ItemIndex
    {
        get { return _itemIndex; }
        set
        {
            if (_itemIndex == value) return;
            _itemIndex = value;
            Log.Trace("Tab item changed to {0}", _itemIndex.ToString());
            RaisePropertyChanged(() => ItemIndex);
        }
    }
}

Tab1

public class Tab1ViewModel : MvxNavigationViewModel<string>
{
    public Tab1ViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
    {
        OpenChildCommand = new MvxAsyncCommand(async () => await NavigationService.Navigate<ChildViewModel>());

        OpenModalCommand = new MvxAsyncCommand(async () => await NavigationService.Navigate<ModalViewModel>());

        OpenNavModalCommand = new MvxAsyncCommand(async () => await NavigationService.Navigate<ModalNavViewModel>());

        CloseCommand = new MvxAsyncCommand(async () => await NavigationService.Close(this));

        OpenTab2Command = new MvxAsyncCommand(async () => await NavigationService.ChangePresentation(new MvxPagePresentationHint(typeof(Tab2ViewModel))));
    }

    public override async Task Initialize()
    {
        await Task.Delay(3000);
    }

    string para;
    public override void Prepare(string parameter)
    {
        para = parameter;
    }

    public override void ViewAppeared()
    {
        base.ViewAppeared();
    }

    public IMvxAsyncCommand OpenChildCommand { get; private set; }

    public IMvxAsyncCommand OpenModalCommand { get; private set; }

    public IMvxAsyncCommand OpenNavModalCommand { get; private set; }

    public IMvxAsyncCommand OpenTab2Command { get; private set; }

    public IMvxAsyncCommand CloseCommand { get; private set; }
}

Tab2

public class Tab2ViewModel : MvxNavigationViewModel
{
    public Tab2ViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
    {
        ShowRootViewModelCommand = new MvxAsyncCommand(async () => await NavigationService.Navigate<RootViewModel>());

        CloseViewModelCommand = new MvxAsyncCommand(async () => await NavigationService.Close(this));
    }

    public IMvxAsyncCommand ShowRootViewModelCommand { get; private set; }

    public IMvxAsyncCommand CloseViewModelCommand { get; private set; }
}

Xamarin传统

Android

此处的键是属性MvxFragmentPresentation(确定它是片段)和MvxTabLayoutPresentation(确定它将显示为选项卡).显然,选项卡的根目录具有ViewPager来托管选项卡的页面.

Xamarin Traditional

Android

The keys here are the attributes MvxFragmentPresentation that determines that it is a fragment and MvxTabLayoutPresentation that determines that it will be presented as a tab. Obviously the tabs root has a ViewPager to host the pages of the tabs.

首先,您需要一个视图来托管这些标签页(在本示例中,该视图也托管在SplitDetailView中,但您可以将其放置在任意位置:

First you'll need a view to host the tabs (in this example the view is also hosted in a SplitDetailView but you can put it whereever you want:

根"标签

我假设您希望根标签是一个片段,也可以将其作为活动(如果需要,请在Playground项目中查看).

I assume you want your root tab a fragment, you can have it as an activity as well (look in the Playground project if you want that).

[MvxFragmentPresentation(fragmentHostViewType: typeof(SplitDetailView), fragmentContentId: Resource.Id.tabs_frame, addToBackStack: true)]
[Register(nameof(TabsRootBView))]
public class TabsRootBView : MvxFragment<TabsRootBViewModel>
{
    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        base.OnCreateView(inflater, container, savedInstanceState);

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

        return view;
    }

    public override void OnViewCreated(View view, Bundle savedInstanceState)
    {
        base.OnViewCreated(view, savedInstanceState);

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

它的axml:

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:local="http://schemas.android.com/apk/res-auto"
    android:id="@+id/main_content"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">
    <android.support.design.widget.AppBarLayout android:id="@+id/appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
        <android.support.v7.widget.Toolbar android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimaryDark"
            local:popupTheme="@style/ThemeOverlay.AppCompat.Light"
            local:layout_scrollFlags="scroll|enterAlways" />
        <android.support.design.widget.TabLayout android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="?attr/colorPrimaryDark"
            android:paddingLeft="16dp"
            local:tabGravity="center"
            local:tabMode="scrollable" />
    </android.support.design.widget.AppBarLayout>
    <android.support.v4.view.ViewPager android:id="@+id/viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        local:layout_behavior="@string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>

标签1

要充分考虑在根选项卡视图中命名的TabLayoutResourceIdViewPagerResourceId.

Take into consideration well referencing the TabLayoutResourceId and ViewPagerResourceId as named in the root tabs view.

[MvxTabLayoutPresentation(TabLayoutResourceId = Resource.Id.tabs, ViewPagerResourceId = Resource.Id.viewpager, Title = "Tab 1", FragmentHostViewType = typeof(TabsRootBView))]
[Register(nameof(Tab1View))]
public class Tab1View : MvxFragment<Tab1ViewModel>
{
    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // Create your fragment here
    }

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        base.OnCreateView(inflater, container, savedInstanceState);

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

        return view;
    }
}

它的axml:

<?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:id="@+id/main_frame"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="Show Child"
        local:MvxBind="Click OpenChildCommand;" />
    <Button
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="Show Tab 2"
        local:MvxBind="Click OpenTab2Command;" />
    <FrameLayout
        android:id="@+id/content_frame"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />
</LinearLayout>

标签2

[MvxTabLayoutPresentation(TabLayoutResourceId = Resource.Id.tabs, ViewPagerResourceId = Resource.Id.viewpager, Title = "Tab 2", FragmentHostViewType = typeof(TabsRootBView))]
[Register(nameof(Tab2View))]
public class Tab2View : MvxFragment<Tab2ViewModel>
{
    public override void OnCreate(Bundle savedInstanceState)
    {
        base.OnCreate(savedInstanceState);

        // Create your fragment here
    }

    public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
    {
        base.OnCreateView(inflater, container, savedInstanceState);

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

        return view;
    }
}

它的axml:

<?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:id="@+id/main_frame"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">
    <Button android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginTop="10dp"
        android:text="Close tab"
        local:MvxBind="Click CloseViewModelCommand;" />
</LinearLayout>

iOS

这里的关键是属性MvxRootPresentation,它确定它是根,并赋予它MvxTabBarViewController它将托管制表符.除此MvxTabPresentation之外,还确定ViewController是选项卡.

iOS

The key here are the attributes MvxRootPresentation that deterines that it's the root and giving that it is an MvxTabBarViewController it will host tabs. Besides this MvxTabPresentation determines that it the ViewController is a tab.

根标签

[MvxFromStoryboard("Main")]
[MvxRootPresentation(WrapInNavigationController = true)]
public partial class TabsRootView : MvxTabBarViewController<TabsRootViewModel>
{
    private bool _isPresentedFirstTime = true;

    public TabsRootView(IntPtr handle) : base(handle)
    {
    }

    public override void ViewWillAppear(bool animated)
    {
        base.ViewWillAppear(animated);

        if (ViewModel != null && _isPresentedFirstTime)
        {
            _isPresentedFirstTime = false;
            ViewModel.ShowInitialViewModelsCommand.ExecuteAsync(null);
        }
    }

    protected override void SetTitleAndTabBarItem(UIViewController viewController, MvxTabPresentationAttribute attribute)
    {
        // you can override this method to set title or iconName
        if (string.IsNullOrEmpty(attribute.TabName))
            attribute.TabName = "Tab 2";
        if (string.IsNullOrEmpty(attribute.TabIconName))
            attribute.TabIconName = "ic_tabbar_menu";

        base.SetTitleAndTabBarItem(viewController, attribute);
    }

    public override bool ShowChildView(UIViewController viewController)
    {
        var type = viewController.GetType();

        return type == typeof(ChildView)
            ? false
            : base.ShowChildView(viewController);
    }

    public override bool CloseChildViewModel(IMvxViewModel viewModel)
    {
        var type = viewModel.GetType();

        return type == typeof(ChildViewModel)
            ? false
            : base.CloseChildViewModel(viewModel);
    }
}

标签1

[MvxFromStoryboard("Main")]
[MvxTabPresentation(WrapInNavigationController = true, TabIconName = "home", TabName = "Tab 1")]
public partial class Tab1View : MvxViewController<Tab1ViewModel>
{
    public Tab1View(IntPtr handle) : base(handle)
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        var set = this.CreateBindingSet<Tab1View, Tab1ViewModel>();

        set.Bind(btnModal).To(vm => vm.OpenModalCommand);
        set.Bind(btnNavModal).To(vm => vm.OpenNavModalCommand);
        set.Bind(btnChild).To(vm => vm.OpenChildCommand);
        set.Bind(btnTab2).To(vm => vm.OpenTab2Command);

        set.Apply();
    }
}

标签2

[MvxFromStoryboard("Main")]
[MvxTabPresentation]
public partial class Tab2View : MvxViewController<Tab2ViewModel>
{
    public Tab2View(IntPtr handle) : base(handle)
    {
    }

    public override void ViewDidLoad()
    {
        base.ViewDidLoad();

        var set = this.CreateBindingSet<Tab2View, Tab2ViewModel>();

        set.Bind(btnShowStack).To(vm => vm.ShowRootViewModelCommand);
        set.Bind(btnClose).To(vm => vm.CloseViewModelCommand);

        set.Apply();
    }
}

Xamarin形式

根"标签

这里的主要内容是views:MvxTabbedPageMvxTabbedPagePresentation,表明它将是一个托管标签的页面.

The main things here are the views:MvxTabbedPage and MvxTabbedPagePresentation to indicate that it'll be a page that will host tabs.

<?xml version="1.0" encoding="UTF-8"?>
<views:MvxTabbedPage x:TypeArguments="viewModels:TabsRootViewModel"
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
    xmlns:mvx="clr-namespace:MvvmCross.Forms.Bindings;assembly=MvvmCross.Forms"
    xmlns:viewModels="clr-namespace:Playground.Core.ViewModels;assembly=Playground.Core"
    x:Class="Playground.Forms.UI.Pages.TabsRootPage" Title="TabsRoot page">
    <ContentPage.Content>

    </ContentPage.Content>
</views:MvxTabbedPage>

[MvxTabbedPagePresentation(TabbedPosition.Root, NoHistory = true)]
public partial class TabsRootPage : MvxTabbedPage<TabsRootViewModel>
{
    public TabsRootPage()
    {
        InitializeComponent();
    }

    private bool _firstTime = true;

    protected override void OnAppearing()
    {
        base.OnAppearing();
        if (_firstTime)
        {
            //ViewModel.ShowInitialViewModelsCommand.Execute();
            ViewModel.ShowInitialViewModelsCommand.ExecuteAsync(null);
            _firstTime = false;
        }
    }

    protected override void OnViewModelSet()
    {
        base.OnViewModelSet();
    }
}

标签1

这里的主要内容是MvxTabbedPagePresentation,它指示它将是一个选项卡.

The main thing here is MvxTabbedPagePresentation that indicates that it will be a a tab.

<?xml version="1.0" encoding="UTF-8"?>
<views:MvxContentPage x:TypeArguments="viewModels:Tab1ViewModel"
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
    xmlns:mvx="clr-namespace:MvvmCross.Forms.Bindings;assembly=MvvmCross.Forms"
    xmlns:viewModels="clr-namespace:Playground.Core.ViewModels;assembly=Playground.Core"
    x:Class="Playground.Forms.UI.Pages.Tab1Page" Title="Tab1 page">
    <ContentPage.Content>
      <StackLayout Margin="10">
            <Label Text="I'm a tab page" />
            <Button Text="Show Child" mvx:Bi.nd="Command OpenChildCommand"/>
            <Button Text="Show Modal" mvx:Bi.nd="Command OpenModalCommand"/>
        </StackLayout>
    </ContentPage.Content>
</views:MvxContentPage>

[MvxTabbedPagePresentation(WrapInNavigationPage = false, Title = "Tab1")]
public partial class Tab1Page : MvxContentPage<Tab1ViewModel>
{
    public Tab1Page()
    {
        InitializeComponent();
    }
}

标签2

<?xml version="1.0" encoding="UTF-8"?>
<views:MvxContentPage x:TypeArguments="viewModels:Tab2ViewModel"
    xmlns="http://xamarin.com/schemas/2014/forms" 
    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml" 
    xmlns:views="clr-namespace:MvvmCross.Forms.Views;assembly=MvvmCross.Forms"
    xmlns:mvx="clr-namespace:MvvmCross.Forms.Bindings;assembly=MvvmCross.Forms"
    xmlns:viewModels="clr-namespace:Playground.Core.ViewModels;assembly=Playground.Core"
    x:Class="Playground.Forms.UI.Pages.Tab2Page" Title="Tab2 page">
    <ContentPage.Content>
      <StackLayout Margin="10">
            <Label Text="I'm a tab page" />
            <Button Text="Close tab" mvx:Bi.nd="Command CloseViewModelCommand"/>
        </StackLayout>
    </ContentPage.Content>
</views:MvxContentPage>

[MvxTabbedPagePresentation(WrapInNavigationPage = false)]
public partial class Tab2Page : MvxContentPage<Tab2ViewModel>
{
    public Tab2Page()
    {
        InitializeComponent();
    }
}

示例可能不够完整,因此您可以检查游乐场项目 MvvmCross存储库的a>查看它是否完整.

Maybe the example is not full enough so you can check the Playground project of the MvvmCross repository to see it complete.

HIH

这篇关于mvvmcross是否提供选项卡式布局平台?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆