Make Android CollectionView smarter about recycling elements/renderers (#5980)
authorE.Z. Hart <hartez@users.noreply.github.com>
Wed, 8 May 2019 23:14:18 +0000 (17:14 -0600)
committerSamantha Houts <samhouts@users.noreply.github.com>
Wed, 8 May 2019 23:14:18 +0000 (16:14 -0700)
Xamarin.Forms.Platform.Android/CollectionView/ItemContentView.cs
Xamarin.Forms.Platform.Android/CollectionView/ItemsViewAdapter.cs
Xamarin.Forms.Platform.Android/CollectionView/TemplatedItemViewHolder.cs

index e38da87..ca2b541 100644 (file)
@@ -25,8 +25,16 @@ namespace Xamarin.Forms.Platform.Android
 
                internal void Recycle()
                {
-                       Content.Element.MeasureInvalidated -= ElementMeasureInvalidated;
-                       RemoveView(Content.View);
+                       if (Content?.Element != null)
+                       {
+                               Content.Element.MeasureInvalidated -= ElementMeasureInvalidated;
+                       }
+                       
+                       if(Content?.View != null)
+                       {
+                               RemoveView(Content.View);
+                       }
+                       
                        Content = null;
                }
 
index ece007b..c2b9884 100644 (file)
@@ -8,10 +8,11 @@ using ViewGroup = Android.Views.ViewGroup;
 
 namespace Xamarin.Forms.Platform.Android
 {
-       // TODO hartez 2018/07/25 14:43:04 Experiment with an ItemSource property change as _adapter.notifyDataSetChanged       
-
        public class ItemsViewAdapter : RecyclerView.Adapter
        {
+               const int TextView = 41;
+               const int TemplatedView = 42;
+
                protected readonly ItemsView ItemsView;
                readonly Func<View, Context, ItemContentView> _createItemContentView;
                internal readonly IItemsViewSource ItemsSource;
@@ -58,29 +59,29 @@ namespace Xamarin.Forms.Platform.Android
                {
                        var context = parent.Context;
 
-                       // Does the ItemsView have a DataTemplate?
-                       var template = ItemsView.ItemTemplate;
-                       if (template == null)
+                       if(viewType == TextView)
                        {
-                               // No template, just use the ToString view
                                var view = new TextView(context);
                                return new TextViewHolder(view);
                        }
 
                        var itemContentView = new ItemContentView(parent.Context);
-                       return new TemplatedItemViewHolder(itemContentView, template);
+                       return new TemplatedItemViewHolder(itemContentView, ItemsView.ItemTemplate);
                }
 
                public override int ItemCount => ItemsSource.Count;
 
                public override int GetItemViewType(int position)
                {
-                       // TODO hartez We might be able to turn this to our own purposes
-                       // We might be able to have the CollectionView signal the adapter if the ItemTemplate property changes
-                       // And as long as it's null, we return a value to that effect here
-                       // Then we don't have to check _itemsView.ItemTemplate == null in OnCreateViewHolder, we can just use
-                       // the viewType parameter.
-                       return 42;
+                       // Does the ItemsView have a DataTemplate?
+                       // TODO ezhart We could probably cache this instead of having to GetValue every time
+                       if (ItemsView.ItemTemplate == null)
+                       {
+                               // No template, just use the Text view
+                               return TextView;
+                       }
+
+                       return TemplatedView;
                }
 
                protected override void Dispose(bool disposing)
index f5e579b..6a36f2c 100644 (file)
@@ -7,6 +7,7 @@ namespace Xamarin.Forms.Platform.Android
        {
                readonly ItemContentView _itemContentView;
                readonly DataTemplate _template;
+               DataTemplate _selectedTemplate;
 
                public View View { get; private set; }
 
@@ -34,15 +35,19 @@ namespace Xamarin.Forms.Platform.Android
                {
                        View.BindingContext = null;
                        itemsView.RemoveLogicalChild(View);
-                       _itemContentView.Recycle();
                }
 
                public void Bind(object itemBindingContext, ItemsView itemsView)
                {
                        var template = _template.SelectDataTemplate(itemBindingContext, itemsView);
 
-                       View = (View)template.CreateContent();
-                       _itemContentView.RealizeContent(View);
+                       if(template != _selectedTemplate)
+                       {
+                               _itemContentView.Recycle();
+                               View = (View)template.CreateContent();
+                               _itemContentView.RealizeContent(View);
+                               _selectedTemplate = template;
+                       }
 
                        // Set the binding context before we add it as a child of the ItemsView; otherwise, it will
                        // inherit the ItemsView's binding context