[Android/iOS] Handle dynamically switching between item layouts in CollectionView...
authoradrianknight89 <adrianknight89@outlook.com>
Sat, 28 Sep 2019 23:50:22 +0000 (18:50 -0500)
committerRui Marinho <me@ruimarinho.net>
Sat, 28 Sep 2019 23:50:22 +0000 (00:50 +0100)
* switch between item layouts

* drop private

* added ui test

Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5354.xaml [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5354.xaml.cs [new file with mode: 0644]
Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Xamarin.Forms.Controls.Issues.Shared.projitems
Xamarin.Forms.Platform.Android/CollectionView/ItemsViewRenderer.cs
Xamarin.Forms.Platform.Android/CollectionView/StructuredItemsViewRenderer.cs
Xamarin.Forms.Platform.iOS/CollectionView/StructuredItemsViewRenderer.cs

diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5354.xaml b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5354.xaml
new file mode 100644 (file)
index 0000000..ac43195
--- /dev/null
@@ -0,0 +1,34 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<controls:TestContentPage
+    xmlns="http://xamarin.com/schemas/2014/forms"
+    xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
+    xmlns:controls="clr-namespace:Xamarin.Forms.Controls"
+    x:Class="Xamarin.Forms.Controls.Issues.Issue5354">
+    <Grid>
+        <Grid.RowDefinitions>
+            <RowDefinition Height="Auto"/>
+            <RowDefinition/>
+        </Grid.RowDefinitions>
+
+        <StackLayout Orientation="Vertical" Spacing="5" Grid.Row="0" VerticalOptions="Center">
+            <Label x:Name="Label" LineBreakMode="WordWrap" Text="Switch between linear and grid layouts. If layouts appear as expected with proper spacing between items, the test passes." HorizontalTextAlignment="Center" VerticalTextAlignment="Center"/>
+            <Button AutomationId="Button5354" Text="Switch to grid layout" HorizontalOptions="Center" VerticalOptions="Center" Clicked="ButtonClicked"/>
+        </StackLayout>
+
+        <CollectionView AutomationId="CollectionView5354" Grid.Row="1" ItemsSource="{Binding Items}">
+            <CollectionView.ItemsLayout>
+                <LinearItemsLayout Orientation="Vertical" ItemSpacing="5"/>
+            </CollectionView.ItemsLayout>
+            
+            <CollectionView.ItemTemplate>
+                <DataTemplate>
+                    <StackLayout Orientation="Vertical" Spacing="10" BackgroundColor="Beige" Padding="10">
+                        <Image Source="{Binding Source}"/>
+                        <Label Text="{Binding Text}" HorizontalTextAlignment="Center" AutomationId="{Binding AutomationId}"/>
+                    </StackLayout>
+                </DataTemplate>
+            </CollectionView.ItemTemplate>
+
+        </CollectionView>
+    </Grid>
+</controls:TestContentPage>
\ No newline at end of file
diff --git a/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5354.xaml.cs b/Xamarin.Forms.Controls.Issues/Xamarin.Forms.Controls.Issues.Shared/Issue5354.xaml.cs
new file mode 100644 (file)
index 0000000..565c690
--- /dev/null
@@ -0,0 +1,146 @@
+using System.Collections.ObjectModel;
+using Xamarin.Forms.CustomAttributes;
+using Xamarin.Forms.Internals;
+using System;
+using Xamarin.Forms.Xaml;
+using System.Collections.Generic;
+
+#if UITEST
+using Xamarin.UITest;
+using Xamarin.UITest.Queries;
+using NUnit.Framework;
+using Xamarin.Forms.Core.UITests;
+using System.Linq;
+#endif
+
+namespace Xamarin.Forms.Controls.Issues
+{
+#if UITEST
+       [Category(UITestCategories.CollectionView)]
+#endif
+#if APP
+       [XamlCompilation(XamlCompilationOptions.Compile)]
+#endif
+       [Preserve(AllMembers = true)]
+       [Issue(IssueTracker.Github, 5354, "[CollectionView] Updating the ItemsLayout type should refresh the layout", PlatformAffected.All)]
+       public partial class Issue5354 : TestContentPage
+       {
+               int count = 0;
+
+#if APP
+               public Issue5354()
+               {
+                       Device.SetFlags(new List<string> { CollectionView.CollectionViewExperimental });
+
+                       InitializeComponent();
+
+                       BindingContext = new ViewModel5354();
+               }
+#endif
+
+               protected override void Init()
+               {
+
+               }
+
+               void ButtonClicked(object sender, EventArgs e)
+               {
+                       var button = sender as Button;
+                       var stackLayout = button.Parent as StackLayout;
+                       var grid = stackLayout.Parent as Grid;
+                       var collectionView = grid.Children[1] as CollectionView;
+
+                       if (count % 2 == 0)
+                       {
+                               collectionView.ItemsLayout = new GridItemsLayout(ItemsLayoutOrientation.Vertical)
+                               {
+                                       Span = 2,
+                                       HorizontalItemSpacing = 5,
+                                       VerticalItemSpacing = 5
+                               };
+
+                               button.Text = "Switch to linear layout";
+                       }
+                       else
+                       {
+                               collectionView.ItemsLayout = new LinearItemsLayout(ItemsLayoutOrientation.Vertical)
+                               {
+                                       ItemSpacing = 5
+                               };
+
+                               button.Text = "Switch to grid layout";
+                       }
+
+                       ++count;
+               }
+
+#if UITEST
+               [Test]
+               public void CollectionViewItemsLayoutUpdate()
+               {
+                       RunningApp.WaitForElement("CollectionView5354");
+                       RunningApp.WaitForElement("Button5354");
+                       var colView = RunningApp.Query("CollectionView5354").Single();
+               
+                       for(var i=0; i<3; i++)
+                       {
+                               RunningApp.WaitForNoElement("NoElement", timeout: TimeSpan.FromSeconds(3));
+                               
+                               AppResult[] lastCellResults = null;
+
+                               RunningApp.QueryUntilPresent(() =>
+                               {
+                                        RunningApp.DragCoordinates(colView.Rect.CenterX, colView.Rect.Y + colView.Rect.Height - 50, colView.Rect.CenterX, colView.Rect.Y + 5);
+
+                                        lastCellResults = RunningApp.Query("Image49");
+
+                                        return lastCellResults;
+                               }, 100, 1);
+
+                               RunningApp.Tap("Button5354");
+                       }
+               }
+#endif
+       }
+
+       [Preserve(AllMembers = true)]
+       public class ViewModel5354
+       {
+               public ObservableCollection<Model5354> Items { get; set; }
+
+               public ViewModel5354()
+               {
+                       var collection = new ObservableCollection<Model5354>();
+                       var pageSize = 50;
+
+                       for (var i = 0; i < pageSize; i++)
+                       {
+                               collection.Add(new Model5354
+                               {
+                                       Text = "Image" + i,
+                                       Source = i % 2 == 0 ? 
+                                       "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5d/Kamchatka_Brown_Bear_near_Dvuhyurtochnoe_on_2015-07-23.jpg/320px-Kamchatka_Brown_Bear_near_Dvuhyurtochnoe_on_2015-07-23.jpg" :
+                                       "https://upload.wikimedia.org/wikipedia/commons/thumb/e/e4/Elephant_%40_kabini.jpg/180px-Elephant_%40_kabini.jpg",
+                                       AutomationId = "Image" + i
+                               });
+                       }
+
+                       Items = collection;
+               }
+       }
+
+       [Preserve(AllMembers = true)]
+       public class Model5354
+       {
+               public string Text { get; set; }
+
+               public string Source { get; set; }
+
+               public string AutomationId { get; set; }
+
+               public Model5354()
+               {
+                       
+               }
+       }
+}
\ No newline at end of file
index faaac98..c2a6312 100644 (file)
@@ -19,6 +19,9 @@
     <Compile Include="$(MSBuildThisFileDirectory)CollectionViewHeaderFooterView.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)CollectionViewItemsUpdatingScrollMode.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)Issue3475.cs" />
+    <Compile Include="$(MSBuildThisFileDirectory)Issue5354.xaml.cs">
+      <SubType>Code</SubType>
+    </Compile>
     <Compile Include="$(MSBuildThisFileDirectory)Issue7621.xaml.cs">
       <SubType>Code</SubType>
     </Compile>
     <Compile Update="C:\Users\hartez\Documents\Xamarin\Xamarin.Forms\Xamarin.Forms.Controls.Issues\Xamarin.Forms.Controls.Issues.Shared\Issue7519Xaml.xaml.cs">
       <DependentUpon>Issue7519Xaml.xaml</DependentUpon>
     </Compile>
+    <Compile Update="$(MSBuildThisFileDirectory)Issue5354.xaml.cs">
+      <DependentUpon>Issue5354.xaml</DependentUpon>
+    </Compile>
     <Compile Update="$(MSBuildThisFileDirectory)Issue7621.xaml.cs">
       <DependentUpon>Issue7621.xaml</DependentUpon>
     </Compile>
       <Generator>MSBuild:Compile</Generator>
     </EmbeddedResource>
   </ItemGroup>
-</Project>
+    <ItemGroup>
+    <EmbeddedResource Include="$(MSBuildThisFileDirectory)Issue5354.xaml">
+      <SubType>Designer</SubType>
+      <Generator>MSBuild:Compile</Generator>
+    </EmbeddedResource>
+  </ItemGroup>
+</Project>
\ No newline at end of file
index 35a91c9..ae1b22f 100644 (file)
@@ -598,6 +598,15 @@ namespace Xamarin.Forms.Platform.Android
                        }
                }
 
+               protected virtual void UpdateLayoutManager()
+               {
+                       ItemsLayout = GetItemsLayout();
+                       SetLayoutManager(SelectLayoutManager(ItemsLayout));
+
+                       UpdateFlowDirection();
+                       UpdateItemSpacing();
+               }
+
                internal void UpdateEmptyViewVisibility()
                {
                        if (ItemsViewAdapter == null)
@@ -607,18 +616,18 @@ namespace Xamarin.Forms.Platform.Android
 
                        var showEmptyView = ItemsView?.EmptyView != null && ItemsViewAdapter.ItemCount == 0;
 
-                       Adapter currAdapter = GetAdapter();
-                       if (showEmptyView && currAdapter != _emptyViewAdapter)
+                       var currentAdapter = GetAdapter();
+                       if (showEmptyView && currentAdapter != _emptyViewAdapter)
                        {
                                SwapAdapter(_emptyViewAdapter, true);
 
                                // TODO hartez 2018/10/24 17:34:36 If this works, cache this layout manager as _emptyLayoutManager      
                                SetLayoutManager(new LinearLayoutManager(Context));
                        }
-                       else if (!showEmptyView && currAdapter != ItemsViewAdapter)
+                       else if (!showEmptyView && currentAdapter != ItemsViewAdapter)
                        {
                                SwapAdapter(ItemsViewAdapter, true);
-                               SetLayoutManager(SelectLayoutManager(ItemsLayout));
+                               UpdateLayoutManager();
                        }
                }
 
index deca543..bdfdae0 100644 (file)
@@ -1,4 +1,5 @@
-using Android.Content;
+using System.ComponentModel;
+using Android.Content;
 
 namespace Xamarin.Forms.Platform.Android
 {
@@ -13,6 +14,16 @@ namespace Xamarin.Forms.Platform.Android
                {
                }
 
+               protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs changedProperty)
+               {
+                       base.OnElementPropertyChanged(sender, changedProperty);
+
+                       if (changedProperty.Is(StructuredItemsView.ItemsLayoutProperty))
+                       {
+                               UpdateLayoutManager();
+                       }
+               }
+
                protected override TAdapter CreateAdapter()
                {
                        return (TAdapter)new StructuredItemsViewAdapter<TItemsView, TItemsViewSource>(ItemsView);
index 9c73506..7233786 100644 (file)
@@ -24,6 +24,10 @@ namespace Xamarin.Forms.Platform.iOS
                        {
                                StructuredItemsViewController.UpdateFooterView();
                        }
+                       else if (changedProperty.Is(StructuredItemsView.ItemsLayoutProperty))
+                       {
+                               StructuredItemsViewController.UpdateLayout(SelectLayout());
+                       }
                }
 
                protected override void SetUpNewElement(ItemsView newElement)
@@ -42,14 +46,14 @@ namespace Xamarin.Forms.Platform.iOS
                protected override ItemsViewLayout SelectLayout()
                {
                        var itemSizingStrategy = StructuredItemsView.ItemSizingStrategy;
-                       var layoutSpecification = StructuredItemsView.ItemsLayout;
+                       var itemsLayout = StructuredItemsView.ItemsLayout;
 
-                       if (layoutSpecification is GridItemsLayout gridItemsLayout)
+                       if (itemsLayout is GridItemsLayout gridItemsLayout)
                        {
                                return new GridViewLayout(gridItemsLayout, itemSizingStrategy);
                        }
 
-                       if (layoutSpecification is LinearItemsLayout listItemsLayout)
+                       if (itemsLayout is LinearItemsLayout listItemsLayout)
                        {
                                return new ListViewLayout(listItemsLayout, itemSizingStrategy);
                        }