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 item = GetFlatItemIndex(nextVisibleItem);
            var view = recycler.GetViewForPosition(item, state);

            if (!(view is SelectorItem))
            {
                throw new InvalidOperationException($"Expected {nameof(SelectorItem)} but received {view?.GetType().ToString() ?? "<null>"}");
            }
            var size         = AddViewAtOffset(view, direction, extentOffset, breadthOffset, availableBreadth);
            var physicalSize = size.LogicalToPhysicalPixels();

            var breadth = (int)(ScrollOrientation == Orientation.Vertical ? physicalSize.Width : physicalSize.Height);

            return(new Line
            {
                NumberOfViews = 1,
                Extent = (int)(ScrollOrientation == Orientation.Vertical ? physicalSize.Height : physicalSize.Width),
                FirstItem = nextVisibleItem,
                LastItem = nextVisibleItem,
                Breadth = breadth
            });
        }
Esempio n. 2
0
        /// <summary>
        /// Prefetch a view for given <paramref name="displayPosition"/>.
        /// </summary>
        private ElementViewRecord PrefetchView(RecyclerView.Recycler recycler, int displayPosition)
        {
            if (displayPosition < 0)
            {
                throw new ArgumentException($"{nameof(displayPosition)} must be greater than 0.");
            }
            var view = GetViewFromIntermediateCache(recycler, displayPosition);

            if (view == null)
            {
                view = recycler.GetViewForPosition(displayPosition);

                // Add->Detach allows view to be efficiently re-displayed
                Layout.AddView(view);
                Layout.TryDetachView(view);
            }
            var viewHolder = _owner.GetChildViewHolder(view) as UnoViewHolder;

            if (!(view is SelectorItem))
            {
                throw new InvalidOperationException($"{nameof(PrefetchView)} received {view?.GetType()} in place of {nameof(SelectorItem)}.");
            }

            return(new ElementViewRecord(displayPosition, viewHolder));
        }
        protected override Line CreateLine(FillDirection direction,
                                           int extentOffset,
                                           int breadthOffset,
                                           int availableBreadth,
                                           RecyclerView.Recycler recycler,
                                           RecyclerView.State state,
                                           IndexPath nextVisibleItem,
                                           bool isNewGroup
                                           )
        {
            var item = GetFlatItemIndex(nextVisibleItem);
            var view = recycler.GetViewForPosition(item, state);

            Debug.Assert(view is SelectorItem, "view is SelectorItem (we should never be given a group header)");
            var size         = AddViewAtOffset(view, direction, extentOffset, breadthOffset, availableBreadth);
            var physicalSize = size.LogicalToPhysicalPixels();

            var breadth = (int)(ScrollOrientation == Orientation.Vertical ? physicalSize.Width : physicalSize.Height);

            return(new Line
            {
                NumberOfViews = 1,
                Extent = (int)(ScrollOrientation == Orientation.Vertical ? physicalSize.Height : physicalSize.Width),
                FirstItem = nextVisibleItem,
                LastItem = nextVisibleItem,
                Breadth = breadth
            });
        }
Esempio n. 4
0
        /// <summary>
        /// Wrapper of <see cref="RecyclerView.Recycler.GetViewForPosition(int)"/> that duplicates bounds checking in managed code, for easier error handling.
        /// </summary>
        public static View GetViewForPosition(this RecyclerView.Recycler recycler, int position, RecyclerView.State state)
        {
            if (position < 0 || position >= state.ItemCount)
            {
                throw new IndexOutOfRangeException($"Invalid item position ({position}). Item count:{state.ItemCount}");
            }

            return(recycler.GetViewForPosition(position));
        }
Esempio n. 5
0
            public View Next(RecyclerView.Recycler recycler)
            {
                if (ScrapList != null)
                {
                    //todo: check in the scrapList when laying out for predictive animations
                }

                var view = recycler.GetViewForPosition(CurrentAnchorPosition);

                CurrentAnchorPosition += ItemDirection;
                return(view);
            }
Esempio n. 6
0
        private void measureScrapChild(RecyclerView.Recycler recycler, int position, int widthSpec,
                                       int heightSpec, int[] measuredDimension)
        {
            View view = recycler.GetViewForPosition(position);

            if (view != null)
            {
                RecyclerView.LayoutParams p = (RecyclerView.LayoutParams)view.LayoutParameters;
                int childWidthSpec          = ViewGroup.GetChildMeasureSpec(widthSpec,
                                                                            PaddingLeft + PaddingRight, p.Width);
                int childHeightSpec = ViewGroup.GetChildMeasureSpec(heightSpec,
                                                                    PaddingTop + PaddingBottom, p.Height);
                view.Measure(childWidthSpec, childHeightSpec);
                measuredDimension[0] = view.MeasuredWidth + p.LeftMargin + p.RightMargin;
                measuredDimension[1] = view.MeasuredHeight + p.BottomMargin + p.TopMargin;
                recycler.RecycleView(view);
            }
        }
Esempio n. 7
0
        /**
         * 添加或者移除条目
         *
         * @param recycler    RecyclerView
         * @param displayRect 显示区域
         * @param i           条目下标
         */
        private void AddOrRemove(RecyclerView.Recycler recycler, Rect displayRect, int i)
        {
            View child = recycler.GetViewForPosition(i);
            Rect rect  = GetItemFrameByPosition(i);

            if (!Rect.Intersects(displayRect, rect))
            {
                RemoveAndRecycleView(child, recycler);   // 回收入暂存区
            }
            else
            {
                AddView(child);
                MeasureChildWithMargins(child, mWidthUsed, mHeightUsed);
                RecyclerView.LayoutParams lp = (RecyclerView.LayoutParams)child.LayoutParameters;// getLayoutParams();
                LayoutDecorated(child,
                                rect.Left - mOffsetX + lp.LeftMargin + PaddingLeft,
                                rect.Top - mOffsetY + lp.TopMargin + PaddingTop,
                                rect.Right - mOffsetX - lp.RightMargin + PaddingLeft,
                                rect.Bottom - mOffsetY - lp.BottomMargin + PaddingTop);
            }
        }
Esempio n. 8
0
        protected override Line CreateLine(GeneratorDirection 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 == 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"));
                }
            }
            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);

                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
            });
        }
        private void layoutChunk(RecyclerView.Recycler recycler, RecyclerView.State state)
        {
            var view = layoutState.Next(recycler);

            if (view == null || !(view.LayoutParameters is RecyclerView.LayoutParams layoutParams))
            {
                layoutChunkResult.IsFinished = true;
                return;
            }

            if (!(view.Tag is Anchor anchor))
            {
                return;
            }

            //todo: check for scrap list if we do predictive animations
            if (layoutState.LayoutDirection == TowardsTheEnd)
            {
                addAnchor(view);
            }
            else
            {
                addAnchor(view, 0);
            }

            MeasureChildWithMargins(view, 0, anchor.Height);
            layoutChunkResult.Consumed = anchor.Height;

            var anchorLeft  = PaddingLeft;
            var anchorRight = PaddingLeft;

            int anchorTop;
            int anchorBottom;

            if (layoutState.LayoutDirection == TowardsTheStart)
            {
                anchorBottom = layoutState.Offset;
                anchorTop    = layoutState.Offset - layoutChunkResult.Consumed;
            }
            else
            {
                anchorTop    = layoutState.Offset;
                anchorBottom = layoutState.Offset + layoutChunkResult.Consumed;
            }

            LayoutDecoratedWithMargins(view, anchorLeft, anchorTop, anchorRight, anchorBottom);

            if (layoutParams.IsItemRemoved || layoutParams.IsItemChanged)
            {
                layoutChunkResult.IgnoreConsumed = true;
            }

            foreach (var anchorData in anchor.AnchoredData)
            {
                if (anchoredViewsPositions.Get(anchorData.AdapterPosition) != null)
                {
                    continue;
                }

                var anchoredView     = recycler.GetViewForPosition(anchorData.AdapterPosition);
                var anchoredViewLeft = anchorLeft + anchorData.LeftOffset;
                var anchoredViewTop  = anchorTop + anchorData.TopOffset;

                if (layoutState.LayoutDirection == TowardsTheEnd)
                {
                    addAnchoredView(anchoredView, anchorData.AdapterPosition);
                }
                else
                {
                    addAnchoredView(anchoredView, anchorData.AdapterPosition, 0);
                }

                MeasureChildWithMargins(anchoredView, anchorData.Width, anchorData.Height);
                LayoutDecoratedWithMargins(anchoredView,
                                           anchoredViewLeft,
                                           anchoredViewTop,
                                           anchoredViewLeft + anchorData.Width,
                                           anchoredViewTop + anchorData.Height);
            }
        }
        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
            });
        }
Esempio n. 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);
        }