Формы Xamarin, RecyclerView, рендеринг ячеек из xaml

Я хочу создать элемент управления в Xamarin.Forms, который использует родной андроид RecyclerView в качестве средства визуализации.

Моя проблема возникает, когда я пытаюсь создать ячейку, которая содержит метки или другие элементы управления с HorizontalOptions, отличными от: Default, Fill и FillAndExpand. Эти ярлыки просто не отображаются сами по себе ( Снимок экрана). это мой код.xaml

<?xml version="1.0" encoding="utf-8" ?>
<self:FormsRecyclerCell xmlns="http://xamarin.com/schemas/2014/forms" xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
    xmlns:self="clr-namespace:FormsRecyclerViewApp;assembly:FormsRecyclerViewApp" x:Class="FormsRecyclerViewApp.FormsRecyclerCellTemplate">
  <Grid>
    <Label Grid.Row="0" Text="{Binding .}" BackgroundColor="Blue" />
    <Label Grid.Row="1" HorizontalOptions="Start" Text="Horizontal Start"  BackgroundColor="AliceBlue"/>
    <Label Grid.Row="2" HorizontalOptions="StartAndExpand" Text="Horizontal StartAndExpand" BackgroundColor="AliceBlue" />
    <Label Grid.Row="3" HorizontalOptions="Fill" Text="Horizontal Fill" BackgroundColor="Aqua" />
    <Label Grid.Row="4" HorizontalOptions="FillAndExpand" Text="Horizontal FillAndExpand" BackgroundColor="Aqua" />
    <Label Grid.Row="5" HorizontalOptions="Center" Text="Horizontal Center" BackgroundColor="Bisque" />
    <Label Grid.Row="6" HorizontalOptions="CenterAndExpand" Text="Horizontal CenterAndExpand" BackgroundColor="Bisque" />
    <Label Grid.Row="7" HorizontalOptions="End" Text="Horizontal End" BackgroundColor="BlanchedAlmond" />
    <Label Grid.Row="8" HorizontalOptions="EndAndExpand" Text="Horizontal EndAndExpand" BackgroundColor="BlanchedAlmond" />
    <Label Grid.Row="9" Text="Horizontal Default" BackgroundColor="Azure"/>
    <BoxView Grid.Row="10" BackgroundColor="Green" />
  </Grid>
</self:FormsRecyclerCell>

Для рендеринга вещей внутри RecyclerView я использую CellContainer, который создает PlatformRenderer и переопределяет методы OnLayout и OnMeasure

FormsRecyclerViewCellContainer.cs

 protected override void OnLayout(bool changed, int l, int t, int r, int b)
    {
        using (var handler = new Handler(Looper.MainLooper))
        {
            handler.Post(() =>
            {
                double width = Context.FromPixels(r - l);
                double height = Context.FromPixels(b - t);

                Xamarin.Forms.Layout.LayoutChildIntoBoundingRegion(this.view.Element, new XForms.Rectangle(0, 0, width, height));
                this.view.UpdateLayout();
            });
        }
    }

    protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        if (this.view == null || this.view.Element == null)
        {
            base.SetMeasuredDimension(widthMeasureSpec, heightMeasureSpec);
            return;
        }

        XForms.SizeRequest measure = this.view.Element.Measure(this.initialWidth, double.PositiveInfinity, XForms.MeasureFlags.IncludeMargins);
        int height = (int)Context.ToPixels(this.fastCell.Height > 0 ? this.fastCell.Height : measure.Request.Height);
        this.SetMeasuredDimension((int)Context.ToPixels(this.initialWidth), height);
    }

Мои вопросы:
Наилучший способ получить нативные объекты из форм.xaml?
- Почему некоторые ярлыки не отображаются сами?
Я должен сделать, чтобы правильно визуализировать все VisualElemts?

Не стесняйтесь скачать мой пример проекта.

2 ответа

Я нашел ответ. Моя проблема действительно была связана с HorizontalOptions, ваша заметка была полезной.

(...) Кроме того, он указывает, должен ли элемент занимать оставшееся пространство по оси X от родительского макета. (...)

Я только что добавил родителя в XF-версию моего viewHolder. Здесь вы можете увидеть, что я изменил:

public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
    {
        FormsRecyclerCell fastCell;

        fastCell = this.Element.ItemTemplate.CreateContent() as FormsRecyclerCell;

        var view = new FormsRecyclerViewCellContainer(parent.Context, fastCell, parent, this.Element.ItemWidth, this.Element);
        if (this.SelectionEnabled)
        {
            view.Click += MainView_Click;
        }

        //--------------**-----------------
        //Here i added XF parent to XF cell
        fastCell.Parent = this.Element;

        var dpW = this.ConvertDpToPixels(this.Element.ItemWidth);
        var dpH = this.ConvertDpToPixels(this.Element.ItemHeight);
        view.SetMinimumWidth(dpW);
        view.SetMinimumHeight(dpH);
        view.LayoutParameters = new GridLayoutManager.LayoutParams(dpW, GridLayoutManager.LayoutParams.WrapContent);
        return new FormsRecyclerViewCell(view);
    }

Это всего лишь одна строка кода, теперь метки отлично работают с каждым параметром HorizontalOptions. Это было очень трудно выяснить, потому что у родного (android) ViewHolder был родной родитель, поэтому я думал, что XF VievHolder мгновенно получит того же родителя, но в виде версии XF (от средства визуализации). Я ошибся, и мне пришлось установить это самостоятельно.

Теперь я знаю, что все вычисления из.xaml реализованы в ядре Xamarin.Forms, и для правильных расчетов дети должны иметь своих родителей.

Я лично думаю, что это ошибка, используя HorizontalOptions из Label внутри Grid, Не уверен, что вы можете попытаться подать эту проблему в Bugzilla.

По умолчанию для параметра HorizontalOptions установлено значение LayoutOptions.Fill, это работает, когда это свойство установлено по умолчанию или FillAndExpandостальные с разными HorizontalOptions имеет ширину 0 на моей стороне, поэтому отображаются только некоторые из ваших ярлыков.

Обходной путь для решения этой проблемы - дать WidthRequest на эти метки шириной 0, но обратите внимание, что

Присвоение HorizontalOptions изменяет расположение элемента при наличии избыточного пространства, доступного вдоль оси X из родительского макета. Кроме того, он указывает, должен ли элемент занимать оставшееся пространство по оси X от родительского макета. Если несколько дочерних элементов макета настроены на расширение, дополнительное пространство распределяется пропорционально.

Ваша ширина здесь должна быть меньше, чем родительская, чтобы увидеть результаты разных LayoutOptions,

Другие вопросы по тегам