新文章 网摘 文章 随笔 日记

Xamarin.Forms ListView根据内容多少显示大小

我有一个相当大的表格(主要用于平板电脑),它有一个TabbedPage嵌套的a ScrollView和一个StackPanel包含许多控件的垂直框。

ListView遇到的情况很少,其中包含一些单行项目,因此需要根据内容大小进行调整。
我想摆脱它的滚动条,但是无论如何我都不希望它占用比其项目所需更多的空间。
有没有一种方法(甚至是一个丑陋的方法)也可以无需编写渲染器x3平台来实现?

这是描述我的树的伪造物:

<ContentPage>
  <MasterDetailPage>
    <MasterDetailPage.Detail>
      <TabbedPage>
        <ContentPage>
          <ScrollView>
            <StackPanel>
              <!-- many controls-->
              <ListView>

渲染后,之后会有巨大的差距ListView我该如何避免呢?

我尝试弄乱了VerticalOptionsHeightRequest,但都没有用。

我正在寻找一种动态方式(最好是不继承)来实现这一目标,而又不涉及自定义渲染器。

 

根据Lutaaya的回答,我做出了一个使之自动化的行为,确定并设置了行高(Gist)。

行为:

namespace Xamarin.Forms
{
  using System;
  using System.Linq;
  public class AutoSizeBehavior : Behavior<ListView>
  {
    ListView _ListView;
    ITemplatedItemsView<Cell> Cells => _ListView;

    protected override void OnAttachedTo(ListView bindable)
    {
      bindable.ItemAppearing += AppearanceChanged;
      bindable.ItemDisappearing += AppearanceChanged;
      _ListView = bindable;
    }

    protected override void OnDetachingFrom(ListView bindable)
    {
      bindable.ItemAppearing -= AppearanceChanged;
      bindable.ItemDisappearing -= AppearanceChanged;
      _ListView = null;
    }

    void AppearanceChanged(object sender, ItemVisibilityEventArgs e) =>
      UpdateHeight(e.Item);

    void UpdateHeight(object item)
    {
      if (_ListView.HasUnevenRows)
      {
        double height;
        if ((height = _ListView.HeightRequest) == 
            (double)VisualElement.HeightRequestProperty.DefaultValue)
          height = 0;

        height += MeasureRowHeight(item);
        SetHeight(height);
      }
      else if (_ListView.RowHeight == (int)ListView.RowHeightProperty.DefaultValue)
      {
        var height = MeasureRowHeight(item);
        _ListView.RowHeight = height;
        SetHeight(height);
      }
    }

    int MeasureRowHeight(object item)
    {
      var template = _ListView.ItemTemplate;
      var cell = (Cell)template.CreateContent();
      cell.BindingContext = item;
      var height = cell.RenderHeight;
      var mod = height % 1;
      if (mod > 0)
        height = height - mod + 1;
      return (int)height;
    }

    void SetHeight(double height)
    {
      //TODO if header or footer is string etc.
      if (_ListView.Header is VisualElement header)
        height += header.Height;
      if (_ListView.Footer is VisualElement footer)
        height += footer.Height;
      _ListView.HeightRequest = height;
    }
  }
}

用法:

<ContentPage xmlns:xf="clr-namespace:Xamarin.Forms">
  <ListView>
    <ListView.Behaviors>
      <xf:AutoSizeBehavior />
 

好吧,假设您的ListView填充了NewsFeeds,让我们使用ObservableCollection来包含我们的数据来填充ListView,如下所示:

XAML代码:

<ListView x:Name="newslist"/>

C#代码

ObservableCollection <News> trends = new ObservableCollection<News>();

然后将趋势列表分配给ListView:

newslist.ItemSource = trends;

然后,我们对ListView和数据进行了逻辑处理,以便ListView包装数据,随着数据的增加,ListView也随之增加,反之亦然

int i = trends.Count;
int heightRowList = 90;
i = (i * heightRowList);
newslist.HeightRequest = i;

因此,完整的代码是:

ObservableCollection <News> trends = new ObservableCollection<News>();
newslist.ItemSource = trends;
int i = trends.Count;
int heightRowList = 90;
i = (i * heightRowList);
newslist.HeightRequest = i;

希望能帮助到你 。

 

我可能在这里过于简化了,但是仅添加HasUnevenRows="True"到ListView中对我有用。

 

我可以创建一个事件处理程序,该事件处理程序考虑到ListView单元格大小的不断变化。就是这样:

    <ListView ItemsSource="{Binding Items}"
         VerticalOptions="Start"
         HasUnevenRows="true"
         CachingStrategy="RecycleElement"
         SelectionMode="None" 
         SizeChanged="ListView_OnSizeChanged">
               <ListView.ItemTemplate>
                    <DataTemplate>
                        <ViewCell >
                           <Frame Padding="10,0" SizeChanged="VisualElement_OnSizeChanged">

可以通过Grid,StackLayout等更改框架。xaml.cs:

        static readonly Dictionary<ListView, Dictionary<VisualElement, int>> _listViewHeightDictionary = new Dictionary<ListView, Dictionary<VisualElement, int>>();

    private void VisualElement_OnSizeChanged(object sender, EventArgs e)
    {
        var frame = (VisualElement) sender;
        var listView = (ListView)frame.Parent.Parent;
        var height = (int) frame.Measure(1000, 1000, MeasureFlags.IncludeMargins).Minimum.Height;
        if (!_listViewHeightDictionary.ContainsKey(listView))
        {
            _listViewHeightDictionary[listView] = new Dictionary<VisualElement, int>();
        }
        if (!_listViewHeightDictionary[listView].TryGetValue(frame, out var oldHeight) || oldHeight != height)
        {
            _listViewHeightDictionary[listView][frame] = height;
            var fullHeight = _listViewHeightDictionary[listView].Values.Sum();
            if ((int) listView.HeightRequest != fullHeight 
                && listView.ItemsSource.Cast<object>().Count() == _listViewHeightDictionary[listView].Count
                )
            {
                listView.HeightRequest = fullHeight;
                listView.Layout(new Rectangle(listView.X, listView.Y, listView.Width, fullHeight));
            }
        }
    }

    private void ListView_OnSizeChanged(object sender, EventArgs e)
    {
        var listView = (ListView)sender;
        if (listView.ItemsSource == null || listView.ItemsSource.Cast<object>().Count() == 0)
        {
            listView.HeightRequest = 0;
        }
    }

当显示框架(正在应用ListView.ItemTemplate)时,框架的大小会更改。我们通过Measure()方法获取它的实际高度,并将其放入Dictionary中,后者了解当前的ListView并保存Frame的高度。当显示最后一帧时,我们将所有高度相加。如果没有项目,则ListView_OnSizeChanged()会将listView.HeightRequest设置为0。

来源:https://stackoverflow.com/questions/44655066/xamarin-forms-listview-size-to-content

posted @ 2020-08-11 10:31  岭南春  阅读(315)  评论(0)    收藏  举报