【问题标题】:Xamarin forms Add button in TabbedPageXamarin 在 TabbedPage 中形成添加按钮
【发布时间】:2021-01-16 11:27:06
【问题描述】:

我有一个问题。我创建了以下 TabbedPage:

<?xml version="1.0" encoding="utf-8" ?>
<TabbedPage xmlns="http://xamarin.com/schemas/2014/forms"
            xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
            xmlns:d="http://xamarin.com/schemas/2014/forms/design"
            xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
            xmlns:views="clr-namespace:MyApp.Views"
            mc:Ignorable="d"
            x:Class="MyApp.Views.MainPage"
            xmlns:android="clr-namespace:Xamarin.Forms.PlatformConfiguration.AndroidSpecific;assembly=Xamarin.Forms.Core"
            android:TabbedPage.ToolbarPlacement="Bottom"
            BarBackgroundColor="White"
            BarTextColor="Black"
            android:TabbedPage.BarItemColor="#B2B2B2"
            android:TabbedPage.BarSelectedItemColor="#56D7A5"
            android:TabbedPage.IsSwipePagingEnabled="False">    

    <TabbedPage.Children>
        <NavigationPage Title="page1" IconImageSource="navbar_page1">
            <x:Arguments>
                <views:page1 NavigationPage.HasNavigationBar="False" />
            </x:Arguments>
        </NavigationPage>

        <NavigationPage Title="page2" IconImageSource="navbar_page2">
            <x:Arguments>
                <views:page2 NavigationPage.HasNavigationBar="False" />
            </x:Arguments>
        </NavigationPage>

        <NavigationPage Title="page3" IconImageSource="navbar_page3">
            <x:Arguments>
                <views:page3 NavigationPage.HasNavigationBar="False" />
            </x:Arguments>
        </NavigationPage>
    
</TabbedPage>

现在我在每个页面上都添加了这样的自定义 FabMenu:

<c:FloatingMenu Margin="0, 0, 10, 10" BGColor="#56D7A5" OpenIcon="openFab_icon" CloseIcon="closeFab_icon"
                AbsoluteLayout.LayoutBounds=".95,.95" AbsoluteLayout.LayoutFlags="PositionProportional">
    <c:FloatingButton x:Name="btnAddHomework" BGColor="#59E1FF" IconSrc="add_homework_icon" OnClickCommand="{Binding btnAddHomeworkCommand}" />
    <c:FloatingButton x:Name="btnAddDeadline" BGColor="#0FF1A0" IconSrc="add_deadline_icon"/>
    <c:FloatingButton x:Name="btnAddTest" BGColor="#5988FF" IconSrc="add_test_icon"/>
</c:FloatingMenu>

问题是每个页面都有自己的 FabMenu,所以您会看到它在每个页面上消失并重新出现,所以我的问题是:是否有某种根视图覆盖了 TabbedPage 中的所有选项卡?

请告诉我我是怎么做到的!

【问题讨论】:

  • 我只想说这些回复中的大多数都不是真的。使用自定义渲染器将按钮添加到选项卡页面是可能的(尽管您可以说并不容易)。参考这个问题:link
  • 我的意思是当你切换标签时不会消失的按钮,所以是标签的覆盖。该链接适用于 gabbar 内的图像,但这不是我的意思
  • 我认为在你的情况下 ToolBarItem 是一个很好的解决方法
  • 这当然是可能的。我建议使用 FloatingMenu 的静态实例。 @LucasZhang-MSFT - 作为一名微软员工,我强烈建议您避免说某事是“不可能的”,除非您 100% 确定这是不可能的。更好的回应是“我不知道如何实现这一点”或根本没有回应。我们都是 Microsoft 的代表,我们给其他开发人员的建议很重要,并且会被视为真理。
  • 标签页只是一个 ContentPage 的容器,您需要在每个 ContentPage 上添加浮动按钮。

标签: xaml xamarin xamarin.forms xamarin.android xamarin.ios


【解决方案1】:

免责声明

我想出了一种仅使用纯 Xamarin.Forms 来创建所需效果的方法。继续阅读并注意解决方案的棘手部分。

摘要

此解决方案是通过实现AbsoluteLayoutCarouselViewIndicatorViewDataTemplateSelector 实现的。 Xamarin.Forms 4.8 应该在以下内容中。如果使用较低版本,请注意 CarouselViewIndicatorView 等功能可能处于预览状态。

DataTemplateSelectorCarouselViewIndicatorView 用于模拟TabbedPageAbsoluteLayout 用于提供叠加层。 p>

所以,现在有了解决方案:

创建您的视图

您可以在此处为所需的每个页面创建一个视图。在此示例中,我希望我的应用程序包含两个页面,因此我创建了两个视图(后面的代码保持不变):

View1.xaml

<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="overlayTest.View1"
             BackgroundColor="Black">
  <ContentView.Content>
      <StackLayout>
            <Label Text="Welcome to Xamarin.Forms 1!"
                   TextColor="White"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="CenterAndExpand" />
        </StackLayout>
  </ContentView.Content>
</ContentView>

View2.xaml

<?xml version="1.0" encoding="UTF-8"?>
<ContentView xmlns="http://xamarin.com/schemas/2014/forms" 
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             x:Class="overlayTest.View2">
  <ContentView.Content>
      <StackLayout>
            <Label Text="Welcome to Xamarin.Forms 2!"
                VerticalOptions="CenterAndExpand" 
                HorizontalOptions="CenterAndExpand" />
        </StackLayout>
  </ContentView.Content>
</ContentView>

创建一个DataTemplateSelector

CarouselView 将使用它来根据当前的Position 选择一个视图或另一个视图。

using System;
using Xamarin.Forms;

namespace overlayTest
{
    class MyTemplateSelector : DataTemplateSelector
    {

        readonly DataTemplate view1, view2;

        public MyTemplateSelector()
        {
            view1 = new DataTemplate(typeof(View1));
            view2 = new DataTemplate(typeof(View2));
        }

        protected override DataTemplate OnSelectTemplate(object item, BindableObject container)
        {
            String s = item.ToString();
            if(s == "1")
            {
                return view1;
            }
            
            return view2;
        }
    }
}

创建您的主页

Page1.xaml

<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
             xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
             xmlns:t="clr-namespace:overlayTest"
             x:Class="overlayTest.Page1">
    <ContentPage.Resources>
        <ResourceDictionary>
            <t:MyTemplateSelector x:Key="templateSelector"/>
        </ResourceDictionary>
    </ContentPage.Resources>
    <ContentPage.Content>
        <AbsoluteLayout>
            <StackLayout AbsoluteLayout.LayoutBounds="0,0,1,1"
                     AbsoluteLayout.LayoutFlags="All"
                     Padding="0"
                     Spacing="0">
                <CarouselView ItemTemplate="{StaticResource templateSelector}"
                          IndicatorView="indicatorView">
                    <CarouselView.ItemsSource>
                        <x:Array Type="{x:Type x:String}">
                            <x:String>1</x:String>
                            <x:String>2</x:String>
                        </x:Array>
                    </CarouselView.ItemsSource>
                </CarouselView>

                <IndicatorView x:Name="indicatorView">
                    <IndicatorView.IndicatorTemplate>
                        <DataTemplate>
                            <StackLayout HorizontalOptions="FillAndExpand">
                                <Frame Margin="10">
                                    <Label/>
                                </Frame>
                            </StackLayout>
                        </DataTemplate>
                    </IndicatorView.IndicatorTemplate>
                </IndicatorView>

            </StackLayout>

            <ContentView 
                     IsVisible="True" VerticalOptions="Start"
                     AbsoluteLayout.LayoutBounds="0,0,1,1"
                     AbsoluteLayout.LayoutFlags="All"
                     BackgroundColor="Transparent">
                <Frame CornerRadius="10"
                   Margin="20"
                   VerticalOptions="StartAndExpand"
                   HorizontalOptions="CenterAndExpand" InputTransparent="False">
                    <StackLayout Padding="0">
                        <Label 
                           FontSize="Medium"
                           TextColor="Black"/>

                        <StackLayout Orientation="Horizontal"
                                 HorizontalOptions="CenterAndExpand">
                            <Label Text="I am floating here"/>
                            <Switch IsToggled="True" />
                        </StackLayout>


                        <Button Text="Save"
                               BackgroundColor="Accent"/>
                    </StackLayout>
                </Frame>
            </ContentView>
        </AbsoluteLayout>
    </ContentPage.Content>
</ContentPage>

在后面的代码中我们设置了选项卡的名称。在这里请注意我假设StackLayout -> Frame -> Label 的元素树。如果您更改IndicatorTemplate,您还必须修改这部分代码!

Page1.xaml.cs

using System.Linq;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace overlayTest
{
    [XamlCompilation(XamlCompilationOptions.Compile)]
    public partial class Page1 : ContentPage
    {
        public Page1()
        {
            
            InitializeComponent();

            indicatorView.PropertyChanged += (s, a) =>
            {
                if (a.PropertyName == IndicatorView.HeightProperty.PropertyName)
                {
                    var indicators = indicatorView.IndicatorLayout.Children.ToList();

                    int counter = 0;

                    foreach(var indicator in indicators)
                    {
                        var indicatorBaseStack = (StackLayout)indicator;
                        var indicatorFrame = (Frame)indicatorBaseStack.Children[0];
                        var indicatorFrameLabel = (Label)indicatorFrame.Content;

                        indicatorFrameLabel.Text = counter == 0 ? "View1" : "View2";
                        counter++;
                    }
                }
            };

        }
    }

}

最后将该Page设置为App的MainPage属性:

public App()
{
    InitializeComponent();

    MainPage = new Page1();
}

最终结果如下所示:

【讨论】:

    【解决方案2】:

    作为一种解决方法,您可以为每个 ContentPage 设置 ToolbarItem(或者您可以定义一个基本 ContentPage)。

    <ContentPage.ToolbarItems>
        <ToolbarItem Text="Example Item"
                 IconImageSource="xxx.png"
                 Order="Secondary"
                 Clicked="{Binding xx}"
                 Priority="0" />
    
        <ToolbarItem Text="Example Item"
                 IconImageSource="xxx.png"
                 Order="Secondary"
                 Priority="1" />
    
        <ToolbarItem Text="Example Item"
                 IconImageSource="xxx.png"
                 Order="Secondary"
                 Priority="2" />
    
    </ContentPage.ToolbarItems>
    

    【讨论】:

      【解决方案3】:

      我建议创建一个包含static FloatingButtonBaseContentPage。这允许每个页面都继承自 BaseContentPage 并使用相同的 FloatingButton

      代码

      基本内容页面

      abstract class BaseContentPage : ContentPage
      {
          protected static Button Button { get; } = new Button { Text = $"This button was created at {DateTimeOffset.UtcNow}" }.Invoke(button => button.Clicked += HandleButtonClicked);
      
          static async void HandleButtonClicked(object sender, EventArgs e) =>
              await Application.Current.MainPage.DisplayAlert("Button Clicked", "This is the same button on both pages", "OK");
      }
      

      标签页示例

      class LabelPage : BaseButtonPage
      {
          public LabelPage()
          {
              Title = "LabelPage";
      
              Content = new StackLayout
              {
                  Children =
                  {
                      new Label { Text = "Label Page" }.TextCenter().Center(),
      
                      Button
                  }
              }
          }
      }
      

      按钮页面示例

      class ButtonPage : BaseButtonPage
      {
          public ButtonPage()
          {
              Title = "ButtonPage";
      
              Content = Button;
          }
      }
      

      示例应用

      public class App : Application
      {
          public App()
          {
              Device.SetFlags(new[] { "Markup_Experimental" });
      
              MainPage = new TabbedPage
              {
                  Children =
                  {
                      new ButtonPage(),
                      new LabelPage()
                  }
              };
          }
      }
      

      示例应用

      以下是用于创建附加 GIF 的示例应用: https://github.com/brminnick/TabbedPageButton/

      【讨论】:

      • 它不会浮在标签上方,不。该按钮显示在每个 ContentPage 内
      • 一切皆有可能?。澄清一下,您正在寻找一个悬浮在 TabbedPage 上方且不会单独添加到每个 ContentPage 的浮动操作按钮?
      • 嗨,布兰登。我还在寻找一种方法来显示将悬停在 TabbedPage 上方的视图。我需要它来显示全屏 Material Drawer,因为我无法使用 MasterDetailPage 实现相同的行为☹️
      猜你喜欢
      • 1970-01-01
      • 2017-08-29
      • 1970-01-01
      • 1970-01-01
      • 2020-04-02
      • 1970-01-01
      • 2018-01-07
      • 2020-08-31
      • 2020-12-14
      相关资源
      最近更新 更多