【问题标题】:Xamarin.Forms - How to overlay an ActivityIndicator in the middle of a StackLayout programmaticallyXamarin.Forms - 如何以编程方式在 StackLayout 中间覆盖 ActivityIndi​​cator
【发布时间】:2014-09-13 03:56:25
【问题描述】:

我想将我的 ActivityIndi​​cator 覆盖在我的表单中间,但我不完全确定我该怎么做,我假设我需要将我的堆栈布局包装在一个相对布局中?

我正在代码中执行此操作,我发现的最接近的示例是使用 XAML,它使用如下所示的网格:

 <ScrollView BackgroundColor="#d3d6db">
    <RelativeLayout
        VerticalOptions="FillAndExpand"
        HorizontalOptions="FillAndExpand">
        <StackLayout Orientation="Vertical"
                     VerticalOptions="FillAndExpand"
                     Padding="10"
                     RelativeLayout.XConstraint="{ConstraintExpression Type=Constant, Constant=0}"
                     RelativeLayout.YConstraint="{ConstraintExpression Type=Constant, Constant=0}">

            <Grid x:Name="SegmentGrid"
                  RowSpacing="10"
                  ColumnSpacing="10">
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="*" />
                    <ColumnDefinition Width="*" />
                </Grid.ColumnDefinitions>
            </Grid>
            <WebView BackgroundColor="White" VerticalOptions="FillAndExpand" Source="{Binding DescriptionHtml}" />
        </StackLayout>

        <ActivityIndicator IsVisible="{Binding IsBusy}"
                           IsRunning="{Binding IsBusy}"
                           Color="Black"
                           VerticalOptions="CenterAndExpand"
                           HorizontalOptions="CenterAndExpand"
                           RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
                                    Property=Height,
                                    Factor=0.33}"
                           RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
                                    Property=Height,
                                    Factor=0.33}" />
    </RelativeLayout>
</ScrollView>

【问题讨论】:

    标签: xamarin xamarin.forms


    【解决方案1】:

    如果您在使用 RelativeLayout 时遇到问题,您也可以使用在类似上下文中工作的 AbsoluteLayout。示例代码如下:

    var overlay = new AbsoluteLayout();
    var content = new StackLayout();
    var loadingIndicator = new ActivityIndicator();
    AbsoluteLayout.SetLayoutFlags(content, AbsoluteLayoutFlags.PositionProportional);
    AbsoluteLayout.SetLayoutBounds(content, new Rectangle(0f, 0f, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
    AbsoluteLayout.SetLayoutFlags(loadingIndicator, AbsoluteLayoutFlags.PositionProportional);
    AbsoluteLayout.SetLayoutBounds(loadingIndicator, new Rectangle(0.5, 0.5, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize));
    overlay.Children.Add(content);
    overlay.Children.Add(loadingIndicator);
    

    如果您想要一个完整的工作示例,我已经提供了一个 @https://github.com/teamtam/xamarin-forms-timesheet

    【讨论】:

    • 如果我将 stacklayout 包裹在 gridlayout 中会怎样?
    • 理论上应该没问题,尽管我之前在嵌套布局开始变得超过 2-3 深时遇到过问题。所以你的布局堆栈看起来像我想象的这样(抱歉似乎无法格式化 cmets):(1)用于加载指示器的 AbsoluteLayout(2)你的 GridLayout(3)你的 StackLayout ...你的 GridLayout 将在与加载指示器相同的深度
    • 使用 TeamTam 的解决方案而不为主要内容设置 LayoutFlags 和 Bounds 防止了我奇怪的布局。 ActivityIndi​​cator 显示在屏幕中间并正确激活。
    【解决方案2】:

    有一个可以接受的答案,但我为所有内容页面编写了一个扩展,并认为它可能会很好。

    public static void AddProgressDisplay (this ContentPage page)
    {
        var content = page.Content;
    
        var grid = new Grid();
        grid.Children.Add(content);
        var gridProgress = new Grid { BackgroundColor = Color.FromHex("#64FFE0B2"), Padding = new Thickness(50) };
        gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
        gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
        gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
        gridProgress.SetBinding(VisualElement.IsVisibleProperty, "IsWorking");
        var activity = new ActivityIndicator
        {
            IsEnabled = true,
            IsVisible = true,
            HorizontalOptions = LayoutOptions.FillAndExpand,
            IsRunning = true
        };
        gridProgress.Children.Add(activity, 0, 1);
        grid.Children.Add(gridProgress);
        page.Content = grid;
    }
    

    备注:IsWorking 必须是 ViewModel(实现 INotifyPropertyChanged)属性的成员。

    调用扩展页面ctor最后的语句。

    this.AddProgressDisplay();
    

    【讨论】:

    • 您的解决方案是最好的,因为您可以将其动态添加到任何页面。那太好了!谢谢!
    • 如何从屏幕上删除它。
    • @user6159419 请注意备注; IsWorking 必须是 ViewModel(已实现 INotifyProppertyChanged)属性的成员。
    【解决方案3】:

    您需要使用绝对布局。遵循以下布局层次结构:

    <AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
        <StackLayout AbsoluteLayout.LayoutFlags="All" AbsoluteLayout.LayoutBounds="0,0,1,1">
    
            <!--< Your control design goes here >-->
    
        </StackLayout>
    
        <StackLayout IsVisible="{Binding IsBusy}" Padding="12"
                 AbsoluteLayout.LayoutFlags="PositionProportional"
                 AbsoluteLayout.LayoutBounds="0.5,0.5,-1,-1">
    
            <ActivityIndicator IsRunning="{Binding IsBusy}" Color ="#80000000"/>
    
            <Label Text="Loading..." HorizontalOptions="Center" TextColor="White"/>
    
        </StackLayout>
    
    </AbsoluteLayout>
    

    这样,活动指示器将覆盖在堆栈布局的中间。

    【讨论】:

      【解决方案4】:

      是的,您应该将 StackLayout 包装在相对布局中。我已经完成了您给出的示例,并通过将我的 XAML 格式化为以下代码来满足我的要求

      XAML 代码

      <RelativeLayout ...> 
          <StackLayout ...>
              <ActivityIndicator  IsRunning="false"
                                  Color="Maroon"
                                  BackgroundColor="Black"
                                  VerticalOptions="CenterAndExpand"
                                  HorizontalOptions="CenterAndExpand"
                                  RelativeLayout.XConstraint="{ConstraintExpression Type=RelativeToParent,
                                      Property=Height,
                                      Factor=0.33}"
                                  RelativeLayout.YConstraint="{ConstraintExpression Type=RelativeToParent,
                                      Property=Height,
                                      Factor=0.28}" />
          </StackLayout>
      </RelativeLayout> 
      

      【讨论】:

      • 谢谢,问题是我没有使用 XAML,我的页面是用代码构建的,所以我想以编程方式实现相同的结果
      【解决方案5】:

      这里是 Nuri YILMAZ 版本的更具个性化的版本。

      public static void AddProgressDisplay(this ContentPage page, string isVisibleProperty = "IsBusy", string bgColor = "#1a1a1ab2")
      {
          var content = page.Content;
      
          var grid = new Grid();
          grid.Children.Add(content);
          var gridProgress = new Grid { BackgroundColor = Color.FromHex(bgColor), Padding = new Thickness(50) };
          gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
          gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Auto) });
          gridProgress.RowDefinitions.Add(new RowDefinition { Height = new GridLength(1, GridUnitType.Star) });
          gridProgress.SetBinding(VisualElement.IsVisibleProperty, isVisibleProperty);
          var activity = new ActivityIndicator
          {
              IsEnabled = true,
              IsVisible = true,
              HorizontalOptions = LayoutOptions.FillAndExpand,
              IsRunning = true
          };
          gridProgress.Children.Add(activity, 0, 1);
          grid.Children.Add(gridProgress);
          page.Content = grid;
      }
      

      【讨论】:

        【解决方案6】:

        您还可以在绝对布局中使用活动指示器。并将绝对布局的 isVisible 属性绑定到 isBusy。以下是我的方法

        <AbsoluteLayout HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand" AbsoluteLayout.LayoutBounds="1, 1, 1, 1"
            BackgroundColor="White" Opacity="0.5"   AbsoluteLayout.LayoutFlags="All" x:Name="indicatorFrame" IsVisible="false">
        
            <ActivityIndicator x:Name="indicator" IsVisible="true" IsRunning="true" IsEnabled="true" 
            HorizontalOptions="Center" VerticalOptions="Center" AbsoluteLayout.LayoutBounds="1, 1, 1, 1"
                Color="Black"    AbsoluteLayout.LayoutFlags="All" />
        
        </AbsoluteLayout>
        

        cs文件中的绑定是这样的

        indicator.BindingContext = this;
        indicator.SetBinding(IsVisibleProperty, "IsBusy", BindingMode.OneWay);
        indicator.SetBinding(ActivityIndicator.IsRunningProperty, "IsBusy", BindingMode.OneWay);
        indicatorFrame.BindingContext = this;
        indicatorFrame.SetBinding(IsVisibleProperty, "IsBusy", BindingMode.OneWay);
        

        【讨论】:

          【解决方案7】:

          在代码中你可以试试这个:

          RelativeLayout relativeLayout = new RelativeLayout ();
          StackLayout stack = new StackLayout ();
          ActivityIndicator indicator = new ActivityIndicator ();
          
          relativeLayout.Children.Add (stack);
          relativeLayout.Children.Add (indicator);
          
          RelativeLayout.SetBoundsConstraint (stack, () => relativeLayout.Bounds);
          
          RelativeLayout.SetBoundsConstraint (indicator,
              BoundsConstraint.FromExpression (() =>  
                  new Rectangle (relativeLayout.Width / 2 - 16, relativeLayout.Height / 2 - 16, 32, 32)));
          

          假设 ActivityIndi​​cator 的宽度/高度为 32/32,您可以选择适合您需要的尺寸或即时计算尺寸。

          RelateiveLayout 中似乎仍然存在错误,它有时会引发意外的空指针异常,即使对于可解决的约束也是如此

          【讨论】:

          • 您好,Sten,感谢您的回复。当尝试根据您上面提出的解决方案将项目添加到相对布局时,我收到一个模棱两可的调用错误。正如下面的帖子所引用的,我目前正在尝试解决这个问题,因为它似乎为了使用儿童的对象初始化程序,你需要传入你的表达式或约束,一旦我测试了一些东西,我会恢复stackoverflow.com/questions/23963658/…
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2015-02-24
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多