Xamarin 表单:扩展和折叠不适用于 iOS 中的列表视图
Xamarin forms: Expansion and collapse is not working for list view in iOS
我在 UI 上列出了一些项目。如果我点击一个项目,它将展开并显示其详细信息。我在 UI 上设置了所有内容,包括最初的详细信息并隐藏了详细信息部分。当我点击该项目时,我更改了细节的可见性并将其显示在 UI 上。此功能在 android 上运行良好并在 ios 上运行良好。我已经上传了一个示例项目 here 以供参考。
截图:
Xaml.cs
private void ItemTapped(object sender, ItemTappedEventArgs e)
{
var selectedItem = (Contact)e.Item;
if (selectedItem != null)
{
if (phoneId == selectedItem.phoneOne)
{
Hide(selectedItem);
phoneId = "";
}
else
{
Open(selectedItem);
}
}
addressbook_listview.SelectedItem = null;
}
private void Open(Contact selectedItem)
{
foreach (var item in contactsList)
{
item.PhoneVisibility = false;
}
selectedItem.PhoneVisibility = true;
phoneId = selectedItem.phoneOne;
}
private void Hide(Contact selectedItem)
{
selectedItem.PhoneVisibility = false;
}
Xaml
<ListView
x:Name="addressbook_listview"
Grid.Row="1"
BackgroundColor="Black"
ItemTapped="addressbook_listview_ItemTapped"
SelectionMode="None"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
Padding="10"
Orientation="Vertical">
<StackLayout
Grid.Column="1"
VerticalOptions="CenterAndExpand"
Margin="5,0,0,0">
<Label
Text="{Binding contactName}"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label
Text="{Binding phoneOne}"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<StackLayout
HorizontalOptions="CenterAndExpand"
Margin="0,10,0,0"
IsVisible="{Binding PhoneVisibility}"
Orientation="Vertical">
<StackLayout
Orientation="Horizontal">
<Label
Text="Phone 1"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label
Text="{Binding phoneOne}"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<BoxView HeightRequest="0.5"
HorizontalOptions="FillAndExpand"/>
<StackLayout
Orientation="Horizontal">
<Label
Text="Phone 2"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label
Text="{Binding phoneTwo}"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Label/>
</ListView.Footer>
</ListView>
简而言之:使用 CollectionView 而不是 ListView。
我认为这是一个已知问题,如问题 this and this,但没有得到正确回答。
所以决定迁移到 CollectionView,因为应该是官方推荐的。
我在这里更改的内容:
- 使用
CollectionView
- 将点击事件移动到
TapGestureRecognizer
- 删除 xaml
中的“网格”
- 使列表绑定到 xaml,并在 xaml.cs
中绑定到自身
- 绑定列表应该是public属性,而不是字段
- 最后一件事:更新到 Xamarin.Forms 5.0.0.2012(和 Android 设置)
没有新的 XF 包,集合视图在更新高度时有另一个问题(运行 没有在 iOS 上更新 XF,虽然解决了这个问题)。
我认为这也是一个已知问题,因此升级 XF 包修复了该问题,
但这也需要更新“目标 Android 版本”。
(一些代码仍然可以重构,但现在可以使用)
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="ExpanderDemo.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<StackLayout>
<CollectionView
x:Name="addressbook_listview"
BackgroundColor="Black"
ItemSizingStrategy="MeasureAllItems"
ItemsSource="{Binding contactsList}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" Orientation="Vertical">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="addressbook_listview_ItemTapped" />
</StackLayout.GestureRecognizers>
<StackLayout Margin="5,0,0,0">
<Label Text="{Binding contactName}" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label Text="{Binding phoneOne}" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<StackLayout
Margin="0,10,0,0"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding PhoneVisibility}"
Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Label Text="Phone 1" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label Text="{Binding phoneOne}" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<BoxView HeightRequest="0.5" HorizontalOptions="FillAndExpand" />
<StackLayout Orientation="Horizontal">
<Label Text="Phone 2" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label Text="{Binding phoneTwo}" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<Label />
</CollectionView.Footer>
</CollectionView>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace ExpanderDemo
{
public partial class MainPage : ContentPage
{
private string phoneId;
public MainPage()
{
InitializeComponent();
contactsList = new ObservableCollection<Contact>();
AddNumber();
BindingContext = this;
}
public ObservableCollection<Contact> contactsList { get; set; }
private void AddNumber()
{
contactsList.Add(new Contact() { contactName = "Smith", phoneOne = "+1 1234567952", phoneTwo = "1478523690", PhoneVisibility = false });
contactsList.Add(new Contact() { contactName = "Brent", phoneOne = "+1 1234568852", phoneTwo = "1478577690", PhoneVisibility = false });
contactsList.Add(new Contact() { contactName = "Finch", phoneOne = "+1 1234560052", phoneTwo = "1478529690", PhoneVisibility = false });
//addressbook_listview.ItemsSource = contactsList;
}
private void addressbook_listview_ItemTapped(object sender, EventArgs e)
{
var layout = sender as StackLayout;
var selectedItem = layout.BindingContext as Contact;
//var selectedItem = (Contact)e.Item;
if (selectedItem != null)
{
if (phoneId == selectedItem.phoneOne)
{
HideMessageBody(selectedItem);
phoneId = "";
}
else
{
OpenMessageBody(selectedItem);
}
}
addressbook_listview.SelectedItem = null;
}
private void HideMessageBody(Contact selectedItem)
{
selectedItem.PhoneVisibility = false;
}
private void OpenMessageBody(Contact selectedItem)
{
foreach (var item in contactsList)
{
item.PhoneVisibility = false;
}
selectedItem.PhoneVisibility = true;
phoneId = selectedItem.phoneOne;
}
}
}
Android更新XF后的两个设置
测试结果
(我在本地测试时请忽略ios上的颜色。)
这是@Shaw 指出的已知问题。
可以调用ViewCell.ForceUpdateSize
强制更新单元格的高度。
第一步是删除 ItemTapped
并使用 Tap gesture
代替。
在 ItemTemplate 的根 stackLayout 上添加一个 TapGestureRecognizer
。
xaml
<ViewCell x:Name="cell" >
<StackLayout
Padding="10"
Orientation="Vertical">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</StackLayout.GestureRecognizers>
代码隐藏。
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
var stack = sender as StackLayout;
var selectedItem = stack.BindingContext as Contact;
if (selectedItem != null)
{
if (phoneId == selectedItem.phoneOne)
{
HideMessageBody(selectedItem);
phoneId = "";
}
else
{
OpenMessageBody(selectedItem);
}
}
addressbook_listview.SelectedItem = null;
(stack.Parent as ViewCell).ForceUpdateSize(); //add this line
}
我在 UI 上列出了一些项目。如果我点击一个项目,它将展开并显示其详细信息。我在 UI 上设置了所有内容,包括最初的详细信息并隐藏了详细信息部分。当我点击该项目时,我更改了细节的可见性并将其显示在 UI 上。此功能在 android 上运行良好并在 ios 上运行良好。我已经上传了一个示例项目 here 以供参考。
截图:
Xaml.cs
private void ItemTapped(object sender, ItemTappedEventArgs e)
{
var selectedItem = (Contact)e.Item;
if (selectedItem != null)
{
if (phoneId == selectedItem.phoneOne)
{
Hide(selectedItem);
phoneId = "";
}
else
{
Open(selectedItem);
}
}
addressbook_listview.SelectedItem = null;
}
private void Open(Contact selectedItem)
{
foreach (var item in contactsList)
{
item.PhoneVisibility = false;
}
selectedItem.PhoneVisibility = true;
phoneId = selectedItem.phoneOne;
}
private void Hide(Contact selectedItem)
{
selectedItem.PhoneVisibility = false;
}
Xaml
<ListView
x:Name="addressbook_listview"
Grid.Row="1"
BackgroundColor="Black"
ItemTapped="addressbook_listview_ItemTapped"
SelectionMode="None"
HasUnevenRows="True">
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<StackLayout
Padding="10"
Orientation="Vertical">
<StackLayout
Grid.Column="1"
VerticalOptions="CenterAndExpand"
Margin="5,0,0,0">
<Label
Text="{Binding contactName}"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label
Text="{Binding phoneOne}"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<StackLayout
HorizontalOptions="CenterAndExpand"
Margin="0,10,0,0"
IsVisible="{Binding PhoneVisibility}"
Orientation="Vertical">
<StackLayout
Orientation="Horizontal">
<Label
Text="Phone 1"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label
Text="{Binding phoneOne}"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<BoxView HeightRequest="0.5"
HorizontalOptions="FillAndExpand"/>
<StackLayout
Orientation="Horizontal">
<Label
Text="Phone 2"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label
Text="{Binding phoneTwo}"
TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
</StackLayout>
</StackLayout>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
<ListView.Footer>
<Label/>
</ListView.Footer>
</ListView>
简而言之:使用 CollectionView 而不是 ListView。
我认为这是一个已知问题,如问题 this and this,但没有得到正确回答。
所以决定迁移到 CollectionView,因为应该是官方推荐的。
我在这里更改的内容:
- 使用
CollectionView
- 将点击事件移动到
TapGestureRecognizer
- 删除 xaml 中的“网格”
- 使列表绑定到 xaml,并在 xaml.cs 中绑定到自身
- 绑定列表应该是public属性,而不是字段
- 最后一件事:更新到 Xamarin.Forms 5.0.0.2012(和 Android 设置)
没有新的 XF 包,集合视图在更新高度时有另一个问题(运行 没有在 iOS 上更新 XF,虽然解决了这个问题)。
我认为这也是一个已知问题,因此升级 XF 包修复了该问题,
但这也需要更新“目标 Android 版本”。
(一些代码仍然可以重构,但现在可以使用)
MainPage.xaml
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage
x:Class="ExpanderDemo.MainPage"
xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml">
<StackLayout>
<CollectionView
x:Name="addressbook_listview"
BackgroundColor="Black"
ItemSizingStrategy="MeasureAllItems"
ItemsSource="{Binding contactsList}"
SelectionMode="None">
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Padding="10" Orientation="Vertical">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="addressbook_listview_ItemTapped" />
</StackLayout.GestureRecognizers>
<StackLayout Margin="5,0,0,0">
<Label Text="{Binding contactName}" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label Text="{Binding phoneOne}" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<StackLayout
Margin="0,10,0,0"
HorizontalOptions="CenterAndExpand"
IsVisible="{Binding PhoneVisibility}"
Orientation="Vertical">
<StackLayout Orientation="Horizontal">
<Label Text="Phone 1" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label Text="{Binding phoneOne}" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
<BoxView HeightRequest="0.5" HorizontalOptions="FillAndExpand" />
<StackLayout Orientation="Horizontal">
<Label Text="Phone 2" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
<Label Text="{Binding phoneTwo}" TextColor="White">
<Label.FontSize>
<OnIdiom x:TypeArguments="x:Double">
<OnIdiom.Phone>15</OnIdiom.Phone>
<OnIdiom.Tablet>20</OnIdiom.Tablet>
<OnIdiom.Desktop>15</OnIdiom.Desktop>
</OnIdiom>
</Label.FontSize>
</Label>
</StackLayout>
</StackLayout>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
<CollectionView.Footer>
<Label />
</CollectionView.Footer>
</CollectionView>
</StackLayout>
</ContentPage>
MainPage.xaml.cs
using System;
using System.Collections.ObjectModel;
using Xamarin.Forms;
namespace ExpanderDemo
{
public partial class MainPage : ContentPage
{
private string phoneId;
public MainPage()
{
InitializeComponent();
contactsList = new ObservableCollection<Contact>();
AddNumber();
BindingContext = this;
}
public ObservableCollection<Contact> contactsList { get; set; }
private void AddNumber()
{
contactsList.Add(new Contact() { contactName = "Smith", phoneOne = "+1 1234567952", phoneTwo = "1478523690", PhoneVisibility = false });
contactsList.Add(new Contact() { contactName = "Brent", phoneOne = "+1 1234568852", phoneTwo = "1478577690", PhoneVisibility = false });
contactsList.Add(new Contact() { contactName = "Finch", phoneOne = "+1 1234560052", phoneTwo = "1478529690", PhoneVisibility = false });
//addressbook_listview.ItemsSource = contactsList;
}
private void addressbook_listview_ItemTapped(object sender, EventArgs e)
{
var layout = sender as StackLayout;
var selectedItem = layout.BindingContext as Contact;
//var selectedItem = (Contact)e.Item;
if (selectedItem != null)
{
if (phoneId == selectedItem.phoneOne)
{
HideMessageBody(selectedItem);
phoneId = "";
}
else
{
OpenMessageBody(selectedItem);
}
}
addressbook_listview.SelectedItem = null;
}
private void HideMessageBody(Contact selectedItem)
{
selectedItem.PhoneVisibility = false;
}
private void OpenMessageBody(Contact selectedItem)
{
foreach (var item in contactsList)
{
item.PhoneVisibility = false;
}
selectedItem.PhoneVisibility = true;
phoneId = selectedItem.phoneOne;
}
}
}
Android更新XF后的两个设置
测试结果
(我在本地测试时请忽略ios上的颜色。)
这是@Shaw 指出的已知问题。
可以调用ViewCell.ForceUpdateSize
强制更新单元格的高度。
第一步是删除 ItemTapped
并使用 Tap gesture
代替。
在 ItemTemplate 的根 stackLayout 上添加一个 TapGestureRecognizer
。
xaml
<ViewCell x:Name="cell" >
<StackLayout
Padding="10"
Orientation="Vertical">
<StackLayout.GestureRecognizers>
<TapGestureRecognizer Tapped="TapGestureRecognizer_Tapped"/>
</StackLayout.GestureRecognizers>
代码隐藏。
private void TapGestureRecognizer_Tapped(object sender, EventArgs e)
{
var stack = sender as StackLayout;
var selectedItem = stack.BindingContext as Contact;
if (selectedItem != null)
{
if (phoneId == selectedItem.phoneOne)
{
HideMessageBody(selectedItem);
phoneId = "";
}
else
{
OpenMessageBody(selectedItem);
}
}
addressbook_listview.SelectedItem = null;
(stack.Parent as ViewCell).ForceUpdateSize(); //add this line
}