Xamarin.Forms - ListView在运行时更改高度

时间:2016-06-26 00:33:11

标签: listview xamarin.forms parallax

我正在使用Xamarin.Forms开发跨平台应用。在其中一个页面中,我有一个头像图片,一些信息,然后是ListView。问题是我将所有这些包裹在ScrollView中,因此我可以滚动整个内容。这个问题是我失去了ListView滚动行为。这是一些截图:

enter image description here

enter image description here

正如您所看到的,XAML视图基本上是化身Image(救火车图像),然后是"最后检查步骤"信息,然后我们ListView。我在HeightRequest中对ListView进行了硬编码,因此它的高度更高。但问题是,当我滚动到页面底部时,我无法滚动浏览ListView,因为ScrollView会混淆这种行为。我需要使用ListView,因为我在那里显示的检查报告列表是从网络服务中填充的,当我进入和退出页面时,我会更新该列表。这是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:checkReport="clr-namespace:HalliganTL.View;assembly=HalliganTL"
              xmlns:controls="clr-namespace:ImageCircle.Forms.Plugin.Abstractions;assembly=ImageCircle.Forms.Plugin.Abstractions"
             x:Class="HalliganTL.View.ApparatusDetailPage" BackgroundColor="#FFFFFFFF">
  <StackLayout Orientation="Vertical" HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
    <ScrollView x:Name="GeneralScrollView"  
                Orientation="Vertical" VerticalOptions="FillAndExpand"
                HorizontalOptions="FillAndExpand">
      <Grid HorizontalOptions="FillAndExpand" VerticalOptions="FillAndExpand">
        <Grid.RowDefinitions>
          <RowDefinition Height="180"></RowDefinition>
          <RowDefinition Height="40"></RowDefinition>
          <RowDefinition Height="80"></RowDefinition>
          <RowDefinition Height="40"></RowDefinition>
          <RowDefinition Height="360"></RowDefinition>
        </Grid.RowDefinitions>
        <Grid.ColumnDefinitions>
          <ColumnDefinition Width="*"></ColumnDefinition>
        </Grid.ColumnDefinitions>

        <!-- Top Image -->
        <ContentView Grid.Column="0" Grid.Row="0" Padding="15">
          <controls:CircleImage x:Name="ApparatusImage"
            HeightRequest="150" WidthRequest="150"
            VerticalOptions="Center"
            BorderColor="#3B5685"
            BorderThickness="2"
            HorizontalOptions="Center"
            Aspect="AspectFill">
          </controls:CircleImage>
        </ContentView>

        <!-- Last Check Separator -->
        <AbsoluteLayout Grid.Column="0" Grid.Row="1" x:Name="LastCheckContainerTitle" BackgroundColor="#FFE4E4E9" Padding="5,15,15,5" VerticalOptions="End" >
          <Label Text="LAST CHECK" Style="{StaticResource separatorLabel}" />
        </AbsoluteLayout>
        <!-- Last Check Detail -->
        <checkReport:CheckReportViewPage Grid.Column="0" Grid.Row="2" x:Name="LastCheckReportView">
          <checkReport:CheckReportViewPage.GestureRecognizers>
            <TapGestureRecognizer Tapped="OnLastCheckReportClicked" NumberOfTapsRequired="1" />
          </checkReport:CheckReportViewPage.GestureRecognizers>
        </checkReport:CheckReportViewPage>

        <AbsoluteLayout  Grid.Column="0" Grid.Row="3"  x:Name="CheckHistoryTitle" BackgroundColor="#FFE4E4E9" Padding="5,15,15,5" >
          <Label Text="CHECK HISTORY" Style="{StaticResource separatorLabel}" FontAttributes="None" VerticalOptions="End" />
        </AbsoluteLayout>

        <!-- Apparatus check history -->
        <ListView x:Name="CheckHistoryListView"
                  HeightRequest="380"
                   Grid.Column="0" Grid.Row="4"
                  HorizontalOptions="FillAndExpand"
                  HasUnevenRows="True"
                  IsPullToRefreshEnabled="False"
                  VerticalOptions="FillAndExpand" ItemTapped="OnItemTapped">
          <ListView.ItemTemplate>
            <DataTemplate>
              <ViewCell>
                <checkReport:CheckReportViewPage/>
              </ViewCell>
            </DataTemplate>
          </ListView.ItemTemplate>
        </ListView>
      </Grid>
    </ScrollView>

    <Label Text="Start Check" HeightRequest="60"  VerticalOptions="End" BackgroundColor="#3B5685" VerticalTextAlignment="Center" HorizontalTextAlignment="Center" FontSize="18" TextColor="White">
      <Label.GestureRecognizers>
        <TapGestureRecognizer
                Tapped="OnStartCheckClicked"
                NumberOfTapsRequired="1" />
      </Label.GestureRecognizers>
    </Label>

  </StackLayout>



</ContentPage>

所以基本上我要问的是如何实现类似视差效果,向下滚动然后将滚动行为委托给ListView,这样我就可以滚动浏览{{1}中的每个项目}}。目前,我只能看到适合ListView HeightRequest维度的项目。在我举例说明的示例屏幕截图中,ListView中还有其他几个项目,但我无法滚动浏览它们,因为ListView正在弄乱{{1}滚动行为。

3 个答案:

答案 0 :(得分:3)

使用ListView将内容放在页面上的推荐方法是使用ListView的页脚和页眉属性。

查看ListView Headers and Footers

<ListView x:Name="listView">
  <ListView.Footer> 
    <Label Text="Footer" />
  </ListView.Footer>
</ListView>

这样您就不需要换行另一个滚动。

答案 1 :(得分:1)

永远不要将

ListView放在滚动容器内。当您这样做时,您将失去ListView的所有好处(虚拟化,滚动等)。

如果您需要通过滚动其他内容与ListView位置同步来实现视差效果,那么您始终可以将列表覆盖在另一个滚动视图的顶部(但不能作为子项<滚动视图的/ em>。

答案 2 :(得分:0)

我相信你需要一个RepeaterView,一个重复项但不能被scrolable的控件,你可以将它绑定到一个ObservableCollection。

它只是起作用

using System;
    using System.Collections;
    using System.Collections.Specialized;
    using Xamarin.Forms;

    public delegate void RepeaterViewItemAddedEventHandler(object sender, RepeaterViewItemAddedEventArgs args);

    // in lieu of an actual Xamarin Forms ItemsControl, this is a heavily modified version of code from https://forums.xamarin.com/discussion/21635/xforms-needs-an-itemscontrol
    public class RepeaterView : StackLayout
    {
        public static readonly BindableProperty ItemsSourceProperty = BindableProperty.Create<RepeaterView, IEnumerable>(
            p => p.ItemsSource,
            null,
            BindingMode.OneWay,
            propertyChanged: ItemsChanged);

        public static readonly BindableProperty ItemTemplateProperty = BindableProperty.Create<RepeaterView, DataTemplate>(
            p => p.ItemTemplate,
            default(DataTemplate));

        public event RepeaterViewItemAddedEventHandler ItemCreated;

        public IEnumerable ItemsSource
        {
            get { return (IEnumerable)GetValue(ItemsSourceProperty); }
            set { SetValue(ItemsSourceProperty, value); }
        }

        public DataTemplate ItemTemplate
        {
            get { return (DataTemplate)GetValue(ItemTemplateProperty); }
            set { SetValue(ItemTemplateProperty, value); }
        }

        private static void ItemsChanged(BindableObject bindable, IEnumerable oldValue, IEnumerable newValue)
        {
            var control = (RepeaterView)bindable;
            var oldObservableCollection = oldValue as INotifyCollectionChanged;

            if (oldObservableCollection != null)
            {
                oldObservableCollection.CollectionChanged -= control.OnItemsSourceCollectionChanged;
            }

            var newObservableCollection = newValue as INotifyCollectionChanged;

            if (newObservableCollection != null)
            {
                newObservableCollection.CollectionChanged += control.OnItemsSourceCollectionChanged;
            }

            control.Children.Clear();

            if (newValue != null)
            {
                foreach (var item in newValue)
                {
                    var view = control.CreateChildViewFor(item);
                    control.Children.Add(view);
                    control.OnItemCreated(view);
                }
            }

            control.UpdateChildrenLayout();
            control.InvalidateLayout();
        }

        protected virtual void OnItemCreated(View view) =>
            this.ItemCreated?.Invoke(this, new RepeaterViewItemAddedEventArgs(view, view.BindingContext));

        private void OnItemsSourceCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            var invalidate = false;

            if (e.OldItems != null)
            {
                this.Children.RemoveAt(e.OldStartingIndex);
                invalidate = true;
            }

            if (e.NewItems != null)
            {
                for (var i = 0; i < e.NewItems.Count; ++i)
                {
                    var item = e.NewItems[i];
                    var view = this.CreateChildViewFor(item);

                    this.Children.Insert(i + e.NewStartingIndex, view);
                    OnItemCreated(view);
                }

                invalidate = true;
            }

            if (invalidate)
            {
                this.UpdateChildrenLayout();
                this.InvalidateLayout();
            }
        }

        private View CreateChildViewFor(object item)
        {
            this.ItemTemplate.SetValue(BindableObject.BindingContextProperty, item);
            return (View)this.ItemTemplate.CreateContent();
        }
    }

    public class RepeaterViewItemAddedEventArgs : EventArgs
    {
        private readonly View view;
        private readonly object model;

        public RepeaterViewItemAddedEventArgs(View view, object model)
        {
            this.view = view;
            this.model = model;
        }

        public View View => this.view;

        public object Model => this.model;
    }

Read this thread in Xamarin Forms Forums