[英]Xamarin.Forms - ListView change height at runtime
我正在使用Xamarin.Forms開發跨平台應用程序。 在其中一個頁面中,我有一個頭像圖像,一些信息,然后是ListView
。 關鍵是我將所有這些包裝在ScrollView
,因此可以滾動瀏覽所有內容。 問題是我失去了ListView
滾動行為。 這是一些屏幕截圖:
如您所見,XAML視圖基本上是化身Image
(救火車圖像),然后是“最后檢查步驟”信息,然后是ListView
。 我在ListView
中對HeightRequest
進行了硬編碼,因此它具有更大的高度。 但是問題是,當我滾動到頁面底部時,我無法繼續滾動瀏覽ListView
因為ScrollView
干擾了該行為。 我需要使用ListView
因為從Web服務填充了我要顯示的檢查報告列表,並且在進入和退出頁面時都會更新該列表。 這是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
,這樣我就可以滾動遍歷ListView
每個項目。 目前,我只能看到適合ListView
HeightRequest維度的項目。 例如,在我所舉的示例屏幕快照中, ListView
中還有其他幾項,但是我無法滾動瀏覽它們,因為ScrollView
弄亂了ListView
滾動行為。
在帶有ListView的頁面上放置內容的推薦方法是使用ListView的Footer和Header屬性。
<ListView x:Name="listView">
<ListView.Footer>
<Label Text="Footer" />
</ListView.Footer>
</ListView>
這樣一來,您就無需包裝其他滾動條。
ListView
永遠不應放置在滾動容器內。 當您這樣做時,您將失去ListView的所有好處(虛擬化,滾動等)。
如果需要通過與ListView
位置同步滾動其他內容來實現視差效果,則可以始終將列表覆蓋在另一個滾動視圖的頂部(但不能作為該滾動視圖的子級 )。
我相信您需要RepeaterView
,該控件可重復項目但不可伸縮,並且可以將其綁定到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;
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.