bykovme/nswallet

View on GitHub
src/NSWallet/NSWallet/Controls/FlowListView/FlowListViewInternalCell.cs

Summary

Maintainability
F
4 days
Test Coverage
using System;
using Xamarin.Forms;
using System.Collections.Generic;
using System.Collections;
using System.Threading.Tasks;

namespace DLToolkit.Forms.Controls
{
    /// <summary>
    /// Flow list view internal cell.
    /// </summary>
    public class FlowListViewInternalCell : ViewCell
    {
        readonly WeakReference<FlowListView> _flowListViewRef;
        readonly AbsoluteLayout _rootLayout;
        readonly Grid _rootLayoutAuto;
        readonly bool _useGridAsMainRoot;
        int _desiredColumnCount;
        DataTemplate _flowColumnTemplate;
        FlowColumnExpand _flowColumnExpand;
        IList<DataTemplate> _currentColumnTemplates = null;

        /// <summary>
        /// Initializes a new instance of the <see cref="DLToolkit.Forms.Controls.FlowListViewInternalCell"/> class.
        /// </summary>
        /// <param name="flowListViewRef">Flow list view reference.</param>
        public FlowListViewInternalCell(WeakReference<FlowListView> flowListViewRef)
        {
            _flowListViewRef = flowListViewRef;
            FlowListView flowListView = null;
            flowListViewRef.TryGetTarget(out flowListView);
            _useGridAsMainRoot = !flowListView.FlowUseAbsoluteLayoutInternally;

            if (!_useGridAsMainRoot)
            {
                _rootLayout = new AbsoluteLayout()
                {
                    Padding = 0d,
                    BackgroundColor = flowListView.FlowRowBackgroundColor,
                };
                View = _rootLayout;
            }
            else
            {
                _rootLayoutAuto = new Grid()
                {
                    RowSpacing = 0d,
                    ColumnSpacing = 0d,
                    Padding = 0d,
                    BackgroundColor = flowListView.FlowRowBackgroundColor,
                };
                View = _rootLayoutAuto;
            }

            _flowColumnTemplate = flowListView.FlowColumnTemplate;
            _desiredColumnCount = flowListView.DesiredColumnCount;
            _flowColumnExpand = flowListView.FlowColumnExpand;
        }

        private IList<DataTemplate> GetDataTemplates(IList container)
        {
            List<DataTemplate> templates = new List<DataTemplate>();

            var flowTemplateSelector = _flowColumnTemplate as FlowTemplateSelector;
            if (flowTemplateSelector != null)
            {
                for (int i = 0; i < container.Count; i++)
                {
                    var template = flowTemplateSelector.SelectTemplate(container[i], i, null);
                    templates.Add(template);
                }

                return templates;
            }

            var templateSelector = _flowColumnTemplate as DataTemplateSelector;
            if (templateSelector != null)
            {
                for (int i = 0; i < container.Count; i++)
                {
                    var template = templateSelector.SelectTemplate(container[i], null);
                    templates.Add(template);
                }

                return templates;
            }

            for (int i = 0; i < container.Count; i++)
            {
                templates.Add(_flowColumnTemplate);
            }

            return templates;
        }

        private bool RowLayoutChanged(int containerCount, IList<DataTemplate> templates)
        {
            // Check if desired number of columns is equal to current number of columns
            if (_currentColumnTemplates == null || containerCount != _currentColumnTemplates.Count)
            {
                return true;
            }

            // Check if desired column view types are equal to current columns view types
            for (int i = 0; i < containerCount; i++)
            {
                if (_currentColumnTemplates[i].GetType() != templates[i].GetType())
                {
                    return true;
                }
            }

            return false;
        }

        private void SetBindingContextForView(View view, object bindingContext)
        {
            if (view != null && view.BindingContext != bindingContext)
                view.BindingContext = bindingContext;
        }

        void AddViewToLayoutAutoHeightDisabled(View view, int containerCount, int colNumber)
        {
            double desiredColumnWidth = 1d / _desiredColumnCount;
            Rectangle bounds = Rectangle.Zero;

            if (_flowColumnExpand != FlowColumnExpand.None && _desiredColumnCount > containerCount)
            {
                int diff = _desiredColumnCount - containerCount;
                bool isLastColumn = colNumber == containerCount - 1;

                switch (_flowColumnExpand)
                {
                    case FlowColumnExpand.First:

                        if (colNumber == 0)
                        {
                            bounds = new Rectangle(0d, 0d, desiredColumnWidth + (desiredColumnWidth * diff), 1d);
                        }
                        else if (isLastColumn)
                        {
                            bounds = new Rectangle(1d, 0d, desiredColumnWidth, 1d);
                        }
                        else
                        {
                            bounds = new Rectangle(desiredColumnWidth * (colNumber + diff) / (1d - desiredColumnWidth), 0d, desiredColumnWidth, 1d);
                        }

                        break;

                    case FlowColumnExpand.Last:

                        if (colNumber == 0)
                        {
                            bounds = new Rectangle(0d, 0d, desiredColumnWidth + (desiredColumnWidth * diff), 1d);
                        }
                        else if (isLastColumn)
                        {
                            bounds = new Rectangle(1d, 0d, desiredColumnWidth + (desiredColumnWidth * diff), 1d);
                        }
                        else
                        {
                            bounds = new Rectangle(desiredColumnWidth * colNumber / (1d - desiredColumnWidth), 0d, desiredColumnWidth, 1d);
                        }

                        break;

                    case FlowColumnExpand.Proportional:

                        double propColumnsWidth = 1d / containerCount;
                        if (colNumber == 0)
                        {
                            bounds = new Rectangle(0d, 0d, propColumnsWidth, 1d);
                        }
                        else if (isLastColumn)
                        {
                            bounds = new Rectangle(1d, 0d, propColumnsWidth, 1d);
                        }
                        else
                        {
                            bounds = new Rectangle(propColumnsWidth * colNumber / (1d - propColumnsWidth), 0d, propColumnsWidth, 1d);
                        }

                        break;

                    case FlowColumnExpand.ProportionalFirst:

                        int propFMod = _desiredColumnCount % containerCount;
                        double propFSize = desiredColumnWidth * Math.Floor((double)_desiredColumnCount / containerCount);
                        double propFSizeFirst = propFSize + desiredColumnWidth * propFMod;

                        if (colNumber == 0)
                        {
                            bounds = new Rectangle(0d, 0d, propFSizeFirst, 1d);
                        }
                        else if (isLastColumn)
                        {
                            bounds = new Rectangle(1d, 0d, propFSize, 1d);
                        }
                        else
                        {
                            bounds = new Rectangle(((propFSize * colNumber) + (propFSizeFirst - propFSize)) / (1d - propFSize), 0d, propFSize, 1d);
                        }

                        break;

                    case FlowColumnExpand.ProportionalLast:

                        int propLMod = _desiredColumnCount % containerCount;
                        double propLSize = desiredColumnWidth * Math.Floor((double)_desiredColumnCount / containerCount);
                        double propLSizeLast = propLSize + desiredColumnWidth * propLMod;

                        if (colNumber == 0)
                        {
                            bounds = new Rectangle(0d, 0d, propLSize, 1d);
                        }
                        else if (isLastColumn)
                        {
                            bounds = new Rectangle(1d, 0d, propLSizeLast, 1d);
                        }
                        else
                        {
                            bounds = new Rectangle((propLSize * colNumber) / (1d - propLSize), 0d, propLSize, 1d);
                        }

                        break;
                }
            }
            else
            {
                if (Math.Abs(1d - desiredColumnWidth) < Epsilon.DoubleValue)
                {
                    bounds = new Rectangle(1d, 0d, desiredColumnWidth, 1d);
                }
                else
                {
                    bounds = new Rectangle(desiredColumnWidth * colNumber / (1d - desiredColumnWidth), 0d, desiredColumnWidth, 1d);
                }
            }

            _rootLayout.Children.Add(view, bounds, AbsoluteLayoutFlags.All);
        }

        void AddViewToLayoutAutoHeightEnabled(View view, int containerCount, int colNumber)
        {
            if (_desiredColumnCount > containerCount)
            {
                int diff = _desiredColumnCount - containerCount;
                bool isLastColumn = colNumber == containerCount - 1;

                switch (_flowColumnExpand)
                {
                    case FlowColumnExpand.None:

                        _rootLayoutAuto.Children.Add(view, colNumber, 0);

                        break;

                    case FlowColumnExpand.First:

                        if (colNumber == 0)
                        {
                            _rootLayoutAuto.Children.Add(view, colNumber, colNumber + diff + 1, 0, 1);
                        }
                        else
                        {
                            _rootLayoutAuto.Children.Add(view, colNumber + diff, colNumber + diff + 1, 0, 1);
                        }

                        break;

                    case FlowColumnExpand.Last:

                        if (isLastColumn)
                        {
                            _rootLayoutAuto.Children.Add(view, colNumber, colNumber + diff + 1, 0, 1);
                        }
                        else
                        {
                            _rootLayoutAuto.Children.Add(view, colNumber, 0);
                        }

                        break;

                    case FlowColumnExpand.Proportional:

                        int howManyP = _desiredColumnCount / containerCount - 1;
                        _rootLayoutAuto.Children.Add(view, colNumber + colNumber * howManyP, colNumber + colNumber * howManyP + howManyP + 1, 0, 1);

                        break;

                    case FlowColumnExpand.ProportionalFirst:

                        int firstSizeAdd = (int)((double)_desiredColumnCount) % containerCount; //1
                        int otherSize = (int)Math.Floor((double)_desiredColumnCount / containerCount); //2

                        if (colNumber == 0)
                            _rootLayoutAuto.Children.Add(view, 0, otherSize + firstSizeAdd, 0, 1);
                        else
                            _rootLayoutAuto.Children.Add(view, (colNumber * otherSize) + firstSizeAdd, ((colNumber + 1) * otherSize) + firstSizeAdd, 0, 1);

                        break;

                    case FlowColumnExpand.ProportionalLast:

                        int lastSizeAdd = (int)((double)_desiredColumnCount) % containerCount; //1
                        int otherSize1 = (int)Math.Floor((double)_desiredColumnCount / containerCount); //2

                        if (isLastColumn)
                        {
                            _rootLayoutAuto.Children.Add(view, (colNumber * otherSize1), ((colNumber + 1) * otherSize1) + lastSizeAdd, 0, 1);
                        }
                        else
                        {
                            _rootLayoutAuto.Children.Add(view, (colNumber * otherSize1), ((colNumber + 1) * otherSize1), 0, 1);
                        }

                        break;
                }
            }
            else
            {
                _rootLayoutAuto.Children.Add(view, colNumber, 0);
            }
        }

        /// <summary>
        /// Override this method to execute an action when the BindingContext changes.
        /// </summary>
        /// <remarks></remarks>
        protected override void OnBindingContextChanged()
        {
            base.OnBindingContextChanged();

            var container = BindingContext as IList;

            if (container == null)
                return;

            FlowListView flowListView = null;
            if (_flowListViewRef.TryGetTarget(out flowListView) && flowListView != null)
            {
                _flowColumnTemplate = flowListView.FlowColumnTemplate;
                _desiredColumnCount = flowListView.DesiredColumnCount;
                _flowColumnExpand = flowListView.FlowColumnExpand;
            }
                
            // Getting view types from templates
            var containerCount = container.Count;
            IList<DataTemplate> templates = GetDataTemplates(container);
            bool layoutChanged = RowLayoutChanged(containerCount, templates);

            if (!layoutChanged) // REUSE VIEWS
            {
                if (_useGridAsMainRoot)
                {
                    for (int i = 0; i < containerCount; i++)
                    {
                        SetBindingContextForView(_rootLayoutAuto.Children[i], container[i]);
                    }
                }
                else
                {
                    for (int i = 0; i < containerCount; i++)
                    {
                        SetBindingContextForView(_rootLayout.Children[i], container[i]);
                    }
                }
            }
            else // RECREATE COLUMNS
            {
                if (_useGridAsMainRoot)
                {
                    if (_rootLayoutAuto.Children.Count > 0)
                        _rootLayoutAuto.Children.Clear();
                }
                else
                {
                    if (_rootLayout.Children.Count > 0)
                        _rootLayout.Children.Clear();
                }

                _currentColumnTemplates = new List<DataTemplate>(templates);

                if (_useGridAsMainRoot)
                {
                    if (_rootLayoutAuto.Children.Count > 0)
                        _rootLayoutAuto.Children.Clear();

                    var colDefs = new ColumnDefinitionCollection();
                    for (int i = 0; i < _desiredColumnCount; i++)
                    {
                        colDefs.Add(new ColumnDefinition() { Width = new GridLength(1d, GridUnitType.Star) });
                    }
                    _rootLayoutAuto.ColumnDefinitions = colDefs;

                    for (int i = 0; i < containerCount; i++)
                    {
                        var view = (View)templates[i].CreateContent();

                        view.GestureRecognizers.Add(new TapGestureRecognizer()
                        {
                            Command = new Command(async (obj) =>
                            {
                                await ExecuteTapGestureRecognizer(view);
                            })
                        });

                        SetBindingContextForView(view, container[i]);
                        if (containerCount == 0 || _desiredColumnCount == 0)
                            return;

                        AddViewToLayoutAutoHeightEnabled(view, containerCount, i);
                    }
                }
                else
                {
                    if (_rootLayout.Children.Count > 0)
                        _rootLayout.Children.Clear();

                    for (int i = 0; i < containerCount; i++)
                    {
                        var view = (View)templates[i].CreateContent();

                        view.GestureRecognizers.Add(new TapGestureRecognizer()
                        {
                            Command = new Command(async (obj) =>
                            {
                                await ExecuteTapGestureRecognizer(view);
                            })
                        });

                        SetBindingContextForView(view, container[i]);
                        if (containerCount == 0 || _desiredColumnCount == 0)
                            return;

                        AddViewToLayoutAutoHeightDisabled(view, containerCount, i);
                    }
                }
            }
        }

        async Task ExecuteTapGestureRecognizer(View view)
        {
            var flowCell = view as IFlowViewCell;
            if (flowCell != null)
            {
                flowCell.OnTapped();
            }

            FlowListView flowListView = null;
            _flowListViewRef.TryGetTarget(out flowListView);

            if (flowListView != null)
            {
                int tapBackgroundEffectDelay = flowListView.FlowTappedBackgroundDelay;

                try
                {
                    if (tapBackgroundEffectDelay != 0)
                    {
                        view.BackgroundColor = flowListView.FlowTappedBackgroundColor;
                    }

                    flowListView.FlowPerformTap(view.BindingContext);
                }
                finally
                {
                    if (tapBackgroundEffectDelay != 0)
                    {
                        await Task.Delay(tapBackgroundEffectDelay);
                        view.BackgroundColor = flowListView.FlowRowBackgroundColor;
                    }
                }
            }
        }
}
}