Skip to content
This repository has been archived by the owner on May 1, 2024. It is now read-only.

Commit

Permalink
Move all the header/footer adjustment to IItemsViewSource
Browse files Browse the repository at this point in the history
Fixes #7121
Fixes #7102
  • Loading branch information
hartez committed Aug 17, 2019
1 parent bbd13a9 commit 90a282e
Show file tree
Hide file tree
Showing 14 changed files with 221 additions and 34 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;
using System.Collections.Generic;
using System.Text;
using Xamarin.Forms.CustomAttributes;
using Xamarin.Forms.Internals;

#if UITEST
using Xamarin.Forms.Core.UITests;
using Xamarin.UITest;
using NUnit.Framework;
#endif

namespace Xamarin.Forms.Controls.Issues
{
#if UITEST
[Category(UITestCategories.CollectionView)]
#endif
[Preserve(AllMembers = true)]
[Issue(IssueTracker.Github, 7102, "[Bug] CollectionView Header cause delay to adding items.",
PlatformAffected.Android)]
public class Issue7102 : TestNavigationPage
{
protected override void Init()
{
#if APP
FlagTestHelpers.SetCollectionViewTestFlag();

PushAsync(new GalleryPages.CollectionViewGalleries.ObservableCodeCollectionViewGallery(grid: false));
#endif
}

#if UITEST
[Test]
public void HeaderDoesNotBreakIndexes()
{
RunningApp.WaitForElement("entryInsert");
RunningApp.Tap("entryInsert");
RunningApp.ClearText();
RunningApp.EnterText("1");
RunningApp.Tap("Insert");

// If the bug is still present, then there will be
// two "Item: 0" items instead of the newly inserted item
// Or the header will have disappeared
RunningApp.WaitForElement("Inserted");
RunningApp.WaitForElement("This is the header");
}
#endif
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
<Compile Include="$(MSBuildThisFileDirectory)Issue7111.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellGestures.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellBackButtonBehavior.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellInsets.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue7102.cs" />
<Compile Include="$(MSBuildThisFileDirectory)ShellInsets.cs" />
<Compile Include="$(MSBuildThisFileDirectory)CollectionViewGrouping.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Issue5412.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Helpers\GarbageCollectionHelper.cs" />
Expand Down
2 changes: 1 addition & 1 deletion Xamarin.Forms.Controls/CoreGallery.cs
Original file line number Diff line number Diff line change
Expand Up @@ -285,7 +285,7 @@ public override string ToString()
new GalleryPageFactory(() => new MemoryLeakGallery(), "Memory Leak"),
new GalleryPageFactory(() => new Issues.A11yTabIndex(), "Accessibility TabIndex"),
new GalleryPageFactory(() => new FontImageSourceGallery(), "Font ImageSource"),
new GalleryPageFactory(() => new CollectionViewGallery(), "CollectionView Gallery"),
new GalleryPageFactory(() => new CollectionViewGallery(), "_CollectionView Gallery"),
new GalleryPageFactory(() => new CollectionViewCoreGalleryPage(), "CollectionView Core Gallery"),
new GalleryPageFactory(() => new Issues.PerformanceGallery(), "Performance"),
new GalleryPageFactory(() => new EntryReturnTypeGalleryPage(), "Entry ReturnType "),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ protected CollectionModifier(CollectionView cv, string buttonText)
HorizontalOptions = LayoutOptions.Fill
};

var button = new Button { Text = buttonText, AutomationId = $"btn{buttonText}" };
var label = new Label { Text = LabelText, VerticalTextAlignment = TextAlignment.Center };
var button = new Button { Text = buttonText, AutomationId = $"btn{buttonText}", HeightRequest = 20, FontSize = 10 };
var label = new Label { Text = LabelText, VerticalTextAlignment = TextAlignment.Center, FontSize = 10 };

Entry = new Entry { Keyboard = Keyboard.Numeric, Text = InitialEntryText, WidthRequest = 100, AutomationId = $"entry{buttonText}" };
Entry = new Entry { Keyboard = Keyboard.Numeric, Text = InitialEntryText, WidthRequest = 100, FontSize = 10, AutomationId = $"entry{buttonText}" };

layout.Children.Add(label);
layout.Children.Add(Entry);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?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:d="http://xamarin.com/schemas/2014/forms/design"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d"
x:Class="Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.HeaderFooterGalleries.FooterOnlyString">
<ContentPage.Content>
<CollectionView x:Name="CollectionView" Footer="This is a footer">

</CollectionView>
</ContentPage.Content>
</ContentPage>
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Xamarin.Forms;
using Xamarin.Forms.Xaml;

namespace Xamarin.Forms.Controls.GalleryPages.CollectionViewGalleries.HeaderFooterGalleries
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FooterOnlyString : ContentPage
{
readonly DemoFilteredItemSource _demoFilteredItemSource = new DemoFilteredItemSource(20);

public FooterOnlyString()
{
InitializeComponent();

CollectionView.ItemTemplate = ExampleTemplates.PhotoTemplate();
CollectionView.ItemsSource = _demoFilteredItemSource.Items;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ public HeaderFooterGallery()
GalleryBuilder.NavButton("Header/Footer (Forms View)", () => new HeaderFooterView(), Navigation),
GalleryBuilder.NavButton("Header/Footer (Template)", () => new HeaderFooterTemplate(), Navigation),
GalleryBuilder.NavButton("Header/Footer (Grid)", () => new HeaderFooterGrid(), Navigation),
GalleryBuilder.NavButton("Footer Only (String)", () => new FooterOnlyString(), Navigation),
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ public ObservableCodeCollectionViewGallery(ItemsLayoutOrientation orientation =

var itemTemplate = ExampleTemplates.PhotoTemplate();

var collectionView = new CollectionView {ItemsLayout = itemsLayout, ItemTemplate = itemTemplate, AutomationId = "collectionview" };
var collectionView = new CollectionView {ItemsLayout = itemsLayout, ItemTemplate = itemTemplate,
AutomationId = "collectionview", Header = "This is the header" };

var generator = new ItemsSourceGenerator(collectionView, initialItems, ItemsSourceType.ObservableCollection);

Expand Down
3 changes: 3 additions & 0 deletions Xamarin.Forms.Controls/Xamarin.Forms.Controls.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,9 @@
<EmbeddedResource Update="GalleryPages\BindableLayoutGalleryPage.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="GalleryPages\CollectionViewGalleries\HeaderFooterGalleries\FooterOnlyString.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
<EmbeddedResource Update="GalleryPages\MapGallery.xaml">
<Generator>MSBuild:UpdateDesignTimeXaml</Generator>
</EmbeddedResource>
Expand Down
25 changes: 24 additions & 1 deletion Xamarin.Forms.Platform.Android/CollectionView/EmptySource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,34 @@ sealed internal class EmptySource : IItemsViewSource
{
public int Count => 0;

public bool HasHeader { get; set; }
public bool HasFooter { get; set; }

public object this[int index] => throw new IndexOutOfRangeException("IItemsViewSource is empty");

public void Dispose()
{


}

public bool IsHeader(int index)
{
return HasHeader && index == 0;
}

public bool IsFooter(int index)
{
if (!HasFooter)
{
return false;
}

if (HasHeader)
{
return index == 1;
}

return index == 0;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,11 @@ internal interface IItemsViewSource : IDisposable
{
int Count { get; }
object this[int index] { get; }

bool HasHeader { get; set; }
bool HasFooter { get; set; }

bool IsHeader(int index);
bool IsFooter(int index);
}
}
31 changes: 13 additions & 18 deletions Xamarin.Forms.Platform.Android/CollectionView/ItemsViewAdapter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,6 @@ public class ItemsViewAdapter : RecyclerView.Adapter
Size? _size;

bool _usingItemTemplate = false;
int _headerOffset = 0;
bool _hasFooter;

internal ItemsViewAdapter(ItemsView itemsView, Func<View, Context, ItemContentView> createItemContentView = null)
{
Expand All @@ -27,29 +25,26 @@ internal ItemsViewAdapter(ItemsView itemsView, Func<View, Context, ItemContentVi
ItemsView = itemsView ?? throw new ArgumentNullException(nameof(itemsView));

UpdateUsingItemTemplate();
UpdateHeaderOffset();
UpdateHasFooter();

ItemsView.PropertyChanged += ItemsViewPropertyChanged;

_createItemContentView = createItemContentView;
ItemsSource = ItemsSourceFactory.Create(itemsView.ItemsSource, this);

UpdateHasHeader();
UpdateHasFooter();

if (_createItemContentView == null)
{
_createItemContentView = (view, context) => new ItemContentView(context);
}
}

private void ItemsViewPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs property)
protected virtual void ItemsViewPropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs property)
{
if (property.Is(ItemsView.HeaderProperty))
{
UpdateHeaderOffset();
}
else if (property.Is(ItemsView.ItemTemplateProperty))
{
UpdateUsingItemTemplate();
UpdateHasHeader();
}
else if (property.Is(ItemsView.ItemTemplateProperty))
{
Expand Down Expand Up @@ -93,7 +88,7 @@ public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int positi
return;
}

var itemsSourcePosition = position - _headerOffset;
var itemsSourcePosition = position;

switch (holder)
{
Expand Down Expand Up @@ -147,7 +142,7 @@ public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int
return new TemplatedItemViewHolder(itemContentView, ItemsView.ItemTemplate);
}

public override int ItemCount => ItemsSource.Count + _headerOffset + (_hasFooter ? 1 : 0);
public override int ItemCount => ItemsSource.Count;

public override int GetItemViewType(int position)
{
Expand Down Expand Up @@ -192,7 +187,7 @@ public virtual int GetPositionForItem(object item)
{
if (ItemsSource[n] == item)
{
return n + _headerOffset;
return n;
}
}

Expand All @@ -204,24 +199,24 @@ void UpdateUsingItemTemplate()
_usingItemTemplate = ItemsView.ItemTemplate != null;
}

void UpdateHeaderOffset()
void UpdateHasHeader()
{
_headerOffset = ItemsView.Header == null ? 0 : 1;
ItemsSource.HasHeader = ItemsView.Header != null;
}

void UpdateHasFooter()
{
_hasFooter = ItemsView.Footer != null;
ItemsSource.HasFooter = ItemsView.Footer != null;
}

bool IsHeader(int position)
{
return _headerOffset > 0 && position == 0;
return ItemsSource.IsHeader(position);
}

bool IsFooter(int position)
{
return _hasFooter && position > ItemsSource.Count;
return ItemsSource.IsFooter(position);
}

RecyclerView.ViewHolder CreateHeaderFooterViewHolder(object content, DataTemplate template, Context context)
Expand Down
41 changes: 37 additions & 4 deletions Xamarin.Forms.Platform.Android/CollectionView/ListSource.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,28 +3,61 @@

namespace Xamarin.Forms.Platform.Android
{
sealed class ListSource : List<object>, IItemsViewSource
sealed class ListSource : IItemsViewSource
{
private List<object> _internal;

public ListSource()
{
}

public ListSource(IEnumerable<object> enumerable) : base(enumerable)
public ListSource(IEnumerable<object> enumerable)
{

_internal = new List<object>(enumerable);
}

public ListSource(IEnumerable enumerable)
{
foreach (object item in enumerable)
{
Add(item);
_internal.Add(item);
}
}

public int Count => _internal.Count + (HasHeader ? 1 : 0) + (HasFooter ? 1 : 0);
public object this[int index] => _internal[AdjustIndexRequest(index)];

public bool HasHeader { get; set; }
public bool HasFooter { get; set; }

public void Dispose()
{

}

public bool IsFooter(int index)
{
if (!HasFooter)
{
return false;
}

if (HasHeader)
{
return index == Count + 1;
}

return index == Count;
}

public bool IsHeader(int index)
{
return HasHeader && index == 0;
}

int AdjustIndexRequest(int index)
{
return index - (HasHeader ? 1 : 0);
}
}
}
Loading

0 comments on commit 90a282e

Please sign in to comment.