protected override Line CreateLine(FillDirection direction,
                                           int extentOffset,
                                           int breadthOffset,
                                           int availableBreadth,
                                           RecyclerView.Recycler recycler,
                                           RecyclerView.State state,
                                           IndexPath nextVisibleItem,
                                           bool isNewGroup
                                           )
        {
            var itemsInLine     = ResolveMaximumItemsInLine(availableBreadth);
            var firstItemInLine = nextVisibleItem;

            //Find first item in line, since the item we are passed is the last
            if (direction == FillDirection.Back)
            {
                // We are recreating the last line of the group - it may be truncated (if the total items are not an even multiple
                // of the items-per-line).
                if (isNewGroup)
                {
                    itemsInLine = XamlParent.GetItemsOnLastLine(firstItemInLine.Section, itemsInLine);
                }
                for (int i = 0; i < itemsInLine - 1; i++)
                {
                    firstItemInLine = GetNextUnmaterializedItem(FillDirection.Back, firstItemInLine).Value;
                    var isCorrectGroup = firstItemInLine.Section == nextVisibleItem.Section;
                    if (!isCorrectGroup)
                    {
                        //TODO: fix bug that makes this happen (#47229)
                    }
                    Debug.Assert(isCorrectGroup, GetAssertMessage("First item should not be from a different group"));
                }
            }
            IndexPath lastItemInLine = firstItemInLine;

            IndexPath?currentItem     = firstItemInLine;
            var       availableWidth  = ResolveAvailableWidth(availableBreadth);
            var       availableHeight = ResolveAvailableHeight(availableBreadth);

            int usedBreadth = 0;

            for (int i = 0; i < itemsInLine; i++)
            {
                var view = recycler.GetViewForPosition(GetFlatItemIndex(currentItem.Value), state);

                Debug.Assert(view is SelectorItem, "view is SelectorItem (we should never be given a group header)");

                //Add view before we measure it, this ensures that DP inheritances are correctly applied
                AddView(view, direction);

                var slotSize             = new Windows.Foundation.Size(availableWidth, availableHeight).PhysicalToLogicalPixels();
                var measuredSize         = _layouter.MeasureChild(view, slotSize);
                var physicalMeasuredSize = measuredSize.LogicalToPhysicalPixels();
                var measuredWidth        = (int)physicalMeasuredSize.Width;
                var measuredHeight       = (int)physicalMeasuredSize.Height;

                if (_implicitItemWidth == null)
                {
                    //Set these values to dimensions of first materialised item
                    _implicitItemWidth  = measuredWidth;
                    _implicitItemHeight = measuredHeight;

                    availableWidth  = ResolveAvailableWidth(availableBreadth);
                    availableHeight = ResolveAvailableHeight(availableBreadth);

                    itemsInLine = ResolveMaximumItemsInLine(availableBreadth);
                }

                LayoutChild(view,
                            FillDirection.Forward,
                            //We always lay out view 'top down' so that it is aligned correctly if its height is less than the line height
                            direction == FillDirection.Forward ? extentOffset : extentOffset - ResolveItemExtent().Value,
                            breadthOffset + usedBreadth,
                            new Foundation.Size(width: slotSize.Width, height: slotSize.Height)
                            );

                usedBreadth   += ResolveItemBreadth().Value;
                lastItemInLine = currentItem.Value;

                currentItem = GetNextUnmaterializedItem(FillDirection.Forward, currentItem);
                if (currentItem == null || currentItem.Value.Section != firstItemInLine.Section)
                {
                    itemsInLine = i + 1;
                    break;
                }
            }

            return(new Line
            {
                NumberOfViews = itemsInLine,
                Extent = ResolveItemExtent().Value,
                Breadth = usedBreadth,
                FirstItem = firstItemInLine,
                LastItem = lastItemInLine
            });
        }
예제 #2
0
        /// <summary>
        /// Remove all views not in the target range from the buffer.
        /// </summary>
        private void UnbufferViews(RecyclerView.Recycler recycler)
        {
            TrimEmpty();
            UnbufferTrailing();
            UnbufferLeading();
            TrimEmpty();             //Empty items may have been exposed by unbuffering
            CheckValidSteadyState();

            void UnbufferTrailing()
            {
                if (TrailingBufferTargetSize == 0)
                {
                    while (_trailingBuffer.Count > 0)
                    {
                        var record = _trailingBuffer.RemoveFromBack();
                        CheckValidState();
                        SendToIntermediateCache(recycler, record);
                    }
                    return;
                }

                while (TrailingBufferStart < TrailingBufferTargetStart)
                {
                    if (_trailingBuffer.Count == 0)
                    {
                        return;
                    }
                    var record = _trailingBuffer.RemoveFromFront();
                    TrimEmpty();
                    CheckValidState();
                    SendToIntermediateCache(recycler, record);
                }


                while (TrailingBufferEnd > TrailingBufferTargetEnd)
                {
                    if (_trailingBuffer.Count == 0)
                    {
                        return;
                    }
                    var record = _trailingBuffer.RemoveFromBack();
                    TrimEmpty();
                    CheckValidState();
                    SendToIntermediateCache(recycler, record);
                }
            }

            void UnbufferLeading()
            {
                if (LeadingBufferTargetSize == 0)
                {
                    while (_leadingBuffer.Count > 0)
                    {
                        var record = _leadingBuffer.RemoveFromBack();
                        CheckValidState();
                        SendToIntermediateCache(recycler, record);
                    }
                    return;
                }

                while (LeadingBufferStart < LeadingBufferTargetStart)
                {
                    if (_leadingBuffer.Count == 0)
                    {
                        return;
                    }
                    var record = _leadingBuffer.RemoveFromFront();
                    TrimEmpty();
                    CheckValidState();
                    CheckValidSteadyState();
                    SendToIntermediateCache(recycler, record);
                }

                while (LeadingBufferEnd > LeadingBufferTargetEnd)
                {
                    if (_leadingBuffer.Count == 0)
                    {
                        return;
                    }
                    var record = _leadingBuffer.RemoveFromBack();
                    TrimEmpty();
                    CheckValidState();
                    CheckValidSteadyState();
                    SendToIntermediateCache(recycler, record);
                }
            }
        }
예제 #3
0
        /// <summary>
        /// Prefetch views in the target range that aren't yet in the buffer.
        /// </summary>
        private void PrefetchViews(RecyclerView.Recycler recycler, RecyclerView.State state)
        {
            if (Layout.ItemCount == 0 || CacheHalfLength == 0)
            {
                return;
            }
            PrefetchTrailing();
            PrefetchLeading();
            if (!_isInitiallyPopulated)
            {
                PrefetchExtra();
                _isInitiallyPopulated = true;
            }

            CheckValidSteadyState();

            void PrefetchTrailing()
            {
                if (TrailingBufferTargetSize == 0)
                {
                    return;
                }

                // Seed buffer; otherwise succeeding logic fails
                if (_trailingBuffer.Count == 0)
                {
                    var record = PrefetchView(recycler, state, TrailingBufferTargetStart);
                    _trailingBuffer.AddToBack(record);
                    CheckValidState();
                }

                while (TrailingBufferStart > TrailingBufferTargetStart)
                {
                    var record = PrefetchView(recycler, state, TrailingBufferStart - 1);
                    _trailingBuffer.AddToFront(record);
                    CheckValidState();
                }

                while (TrailingBufferEnd < TrailingBufferTargetEnd)
                {
                    var record = PrefetchView(recycler, state, TrailingBufferEnd);
                    _trailingBuffer.AddToBack(record);
                    CheckValidState();
                }
            }

            void PrefetchLeading()
            {
                if (LeadingBufferTargetSize == 0)
                {
                    return;
                }

                // Seed buffer
                if (_leadingBuffer.Count == 0)
                {
                    var record = PrefetchView(recycler, state, LeadingBufferTargetStart);
                    _leadingBuffer.AddToBack(record);
                    CheckValidState();
                }

                while (LeadingBufferStart > LeadingBufferTargetStart)
                {
                    var record = PrefetchView(recycler, state, LeadingBufferStart - 1);
                    _leadingBuffer.AddToFront(record);
                    CheckValidState();
                }

                while (LeadingBufferEnd < LeadingBufferTargetEnd)
                {
                    var record = PrefetchView(recycler, state, LeadingBufferEnd);
                    _leadingBuffer.AddToBack(record);
                    CheckValidState();
                }
            }

            // Initially pre-cache a half-width of items directly to the intermediate cache. This is an optimization so that new views
            // aren't created to fill the trailing buffer when the user first scrolls.
            void PrefetchExtra()
            {
                if (TrailingBufferTargetSize > 0)
                {
                    // Only want to perform this step when scroll position is at start of list
                    return;
                }

                if (LeadingBufferTargetSize == 0)
                {
                    // No need for extra items
                    return;
                }
                var targetEnd = Math.Min(NumberOfItems, LeadingBufferEnd + CacheHalfLength + 1);

                try
                {
                    _shouldBlockIntermediateCache = true;
                    for (int i = LeadingBufferEnd; i < targetEnd; i++)
                    {
                        var record = PrefetchView(recycler, state, i);
                        SendToIntermediateCache(recycler, record);
                    }
                }
                finally
                {
                    _shouldBlockIntermediateCache = false;
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Retrieve view for <paramref name="displayPosition"/> from intermediate cache.
        /// </summary>
        private View GetViewFromIntermediateCache(RecyclerView.Recycler recycler, int displayPosition)
        {
            if (_shouldBlockIntermediateCache)
            {
                return(null);
            }
            UnoViewHolder        result = null;
            List <UnoViewHolder> views;
            var type = _owner.CurrentAdapter.GetItemViewType(displayPosition);

            if (!_intermediateCache.TryGetValue(type, out views))
            {
                return(null);
            }

            //Remove views that are animating out
            views.RemoveAll(RemoveUnrecyclable);

            foreach (var holder in views)
            {
                //Look for an exact match
                if (holder.LayoutPosition == displayPosition)
                {
                    result = holder;
                    views.Remove(result);
                    _owner.CurrentAdapter.RegisterPhaseBinding(result);                     //Restart phase binding, since this won't be rebound by adapter
                    break;
                }
            }
            if (result == null && views.Count > 0)
            {
                // Get any match of correct type except views that could be reused without rebinding
                for (int i = views.Count - 1; i >= 0; i--)
                {
                    var view = views[i];
                    if (!IsInTargetRange(view.LayoutPosition))
                    {
                        result = view;
                        views.RemoveAt(i);
                        break;
                    }
                }
            }

            if (result != null)
            {
                recycler.BindViewToPosition(result.ItemView, displayPosition);
                if (this.Log().IsEnabled(Microsoft.Extensions.Logging.LogLevel.Debug))
                {
                    this.Log().Debug($"Returning cached view for position={displayPosition} and view type={type}. {views.Count} cached views remaining.");
                }
                return(result.ItemView);
            }
            return(null);

            bool RemoveUnrecyclable(UnoViewHolder holderInner)
            {
                var isUnrecyclable = !holderInner.IsRecyclable;

                if (isUnrecyclable)
                {
                    Layout.RemoveAndRecycleView(holderInner.ItemView, recycler);
                }
                return(isUnrecyclable);
            }
        }
예제 #5
0
 private void RecycleView(RecyclerView.Recycler recycler, UnoViewHolder holder)
 {
     Layout.RemoveDetachedView(holder.ItemView);
     recycler.RecycleView(holder.ItemView);
 }
예제 #6
0
        protected override Line CreateLine(GeneratorDirection direction,
                                           int extentOffset,
                                           int breadthOffset,
                                           int availableBreadth,
                                           RecyclerView.Recycler recycler,
                                           RecyclerView.State state,
                                           Uno.UI.IndexPath nextVisibleItem,
                                           bool isNewGroup
                                           )
        {
            var itemsInLine     = ResolveMaximumItemsInLine(availableBreadth);
            var firstItemInLine = nextVisibleItem;

            //Find first item in line, since the item we are passed is the last
            if (direction == GeneratorDirection.Backward)
            {
                // We are recreating the last line of the group - it may be truncated (if the total items are not an even multiple
                // of the items-per-line).
                if (isNewGroup)
                {
                    itemsInLine = XamlParent.GetItemsOnLastLine(firstItemInLine.Section, itemsInLine);
                }
                for (int i = 0; i < itemsInLine - 1; i++)
                {
                    firstItemInLine = GetNextUnmaterializedItem(GeneratorDirection.Backward, firstItemInLine).Value;
                    var isCorrectGroup = firstItemInLine.Section == nextVisibleItem.Section;
                    if (!isCorrectGroup)
                    {
                        //TODO: fix bug that makes this happen (#47229)
                    }
                    Debug.Assert(isCorrectGroup, GetAssertMessage("First item should not be from a different group"));
                }
            }
            Uno.UI.IndexPath lastItemInLine = firstItemInLine;

            Uno.UI.IndexPath?currentItem = firstItemInLine;
            var availableWidth           = ResolveAvailableWidth(availableBreadth);
            var availableHeight          = ResolveAvailableHeight(availableBreadth);

            int usedBreadth = 0;

            for (int i = 0; i < itemsInLine; i++)
            {
                var view = recycler.GetViewForPosition(GetFlatItemIndex(currentItem.Value), state);

                if (!(view is SelectorItem))
                {
                    throw new InvalidOperationException($"Expected {nameof(SelectorItem)} but received {view?.GetType().ToString() ?? "<null>"}");
                }

                //Add view before we measure it, this ensures that DP inheritances are correctly applied
                AddView(view, direction);

                var slotSize             = new Windows.Foundation.Size(availableWidth, availableHeight).PhysicalToLogicalPixels();
                var measuredSize         = _layouter.MeasureChild(view, slotSize);
                var physicalMeasuredSize = measuredSize.LogicalToPhysicalPixels();
                var measuredWidth        = (int)physicalMeasuredSize.Width;
                var measuredHeight       = (int)physicalMeasuredSize.Height;

                if (_implicitItemWidth == null)
                {
                    //Set these values to dimensions of first materialised item
                    _implicitItemWidth  = measuredWidth;
                    _implicitItemHeight = measuredHeight;

                    // When an item dimension is not fixed, we need to arrange based on the measured size,
                    // otherwise the arrange will be passed a dimension that is too large and the first
                    // few items will not be visible
                    if (double.IsNaN(ItemWidth))
                    {
                        slotSize.Width = ViewHelper.PhysicalToLogicalPixels(_implicitItemWidth.Value);
                    }
                    if (double.IsNaN(ItemHeight))
                    {
                        slotSize.Height = ViewHelper.PhysicalToLogicalPixels(_implicitItemHeight.Value);
                    }

                    availableWidth  = ResolveAvailableWidth(availableBreadth);
                    availableHeight = ResolveAvailableHeight(availableBreadth);

                    itemsInLine = ResolveMaximumItemsInLine(availableBreadth);
                }

                LayoutChild(view,
                            GeneratorDirection.Forward,
                            //We always lay out view 'top down' so that it is aligned correctly if its height is less than the line height
                            direction == GeneratorDirection.Forward ? extentOffset : extentOffset - ResolveItemExtent().Value,
                            breadthOffset + usedBreadth,
                            slotSize
                            );

                usedBreadth   += ResolveItemBreadth().Value;
                lastItemInLine = currentItem.Value;

                currentItem = GetNextUnmaterializedItem(GeneratorDirection.Forward, currentItem);
                if (currentItem == null || currentItem.Value.Section != firstItemInLine.Section)
                {
                    itemsInLine = i + 1;
                    break;
                }
            }

            return(new Line
            {
                NumberOfViews = itemsInLine,
                Extent = ResolveItemExtent().Value,
                Breadth = usedBreadth,
                FirstItem = firstItemInLine,
                LastItem = lastItemInLine
            });
        }
예제 #7
0
 public override void OnLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
 {
     ComputeSpanCount(Width);
     base.OnLayoutChildren(recycler, state);
 }
예제 #8
0
 private void recycleAnchoredViewsFromStart(RecyclerView.Recycler recycler)
 {
     recycleViewsFromStart(recycler, false);
 }
예제 #9
0
 private void recycleAnchorsFromStart(RecyclerView.Recycler recycler)
 {
     recycleViewsFromStart(recycler, true);
 }
예제 #10
0
 private void removeAndRecycleAnchor(int position, RecyclerView.Recycler recycler)
 {
     RemoveAndRecycleView(anchors[position], recycler);
     anchors[position] = null;
 }
예제 #11
0
        //public void Logi(string msg)
        //{
        //    Log.Info(TAG, msg);
        //}

        //public void Loge(string err)
        //{
        //    Log.Error(TAG, err);
        //}
        //--- 处理布局 ----------------------------------------------------------------------------------

        /**
         * 布局子View
         *
         * @param recycler Recycler
         * @param state    State
         */

        public override void OnLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state)
        {
            Log.Info(TAG, "Item onLayoutChildren");
            Log.Info(TAG, "Item onLayoutChildren isPreLayout = " + state.IsPreLayout);
            Log.Info(TAG, "Item onLayoutChildren isMeasuring = " + state.IsMeasuring);
            Log.Info(TAG, "Item onLayoutChildren state = " + state);

            // 如果是 preLayout 则不重新布局
            if (state.IsPreLayout || !state.DidStructureChange())
            {
                return;
            }

            if (ItemCount == 0)
            {
                RemoveAndRecycleAllViews(recycler);
                // 页面变化回调
                SetPageCount(0);
                SetPageIndex(0, false);
                return;
            }
            else
            {
                SetPageCount(GetTotalPageCount());
                SetPageIndex(GetPageIndexByOffset(), false);
            }

            // 计算页面数量
            int mPageCount = ItemCount / mOnePageSize;

            if (ItemCount % mOnePageSize != 0)
            {
                mPageCount++;
            }

            // 计算可以滚动的最大数值,并对滚动距离进行修正
            if (CanScrollHorizontally())
            {
                mMaxScrollX = (mPageCount - 1) * GetUsableWidth();
                mMaxScrollY = 0;
                if (mOffsetX > mMaxScrollX)
                {
                    mOffsetX = mMaxScrollX;
                }
            }
            else
            {
                mMaxScrollX = 0;
                mMaxScrollY = (mPageCount - 1) * GetUsableHeight();
                if (mOffsetY > mMaxScrollY)
                {
                    mOffsetY = mMaxScrollY;
                }
            }

            // 接口回调
            // setPageCount(mPageCount);
            // setPageIndex(mCurrentPageIndex, false);

            Logi("count = " + ItemCount);

            if (mItemWidth <= 0)
            {
                mItemWidth = GetUsableWidth() / mColumns;
            }
            if (mItemHeight <= 0)
            {
                mItemHeight = GetUsableHeight() / mRows;
            }

            mWidthUsed  = GetUsableWidth() - mItemWidth;
            mHeightUsed = GetUsableHeight() - mItemHeight;

            // 预存储两页的View显示区域
            for (int i = 0; i < mOnePageSize * 2; i++)
            {
                GetItemFrameByPosition(i);
            }

            if (mOffsetX == 0 && mOffsetY == 0)
            {
                // 预存储View
                for (int i = 0; i < mOnePageSize; i++)
                {
                    if (i >= ItemCount)
                    {
                        break;                 // 防止数据过少时导致数组越界异常
                    }
                    View view = recycler.GetViewForPosition(i);
                    AddView(view);
                    MeasureChildWithMargins(view, mWidthUsed, mHeightUsed);
                }
            }

            // 回收和填充布局
            RecycleAndFillItems(recycler, state, true);
        }