Reputation: 2626
I want to create control in Xamarin.Forms that uses native android RecyclerView as renderer.
My problem occurs when i try create cell that contains labels or other controls with HorizontalOptions different than: Default, Fill and FillAndExpand. That labels just don't render themselves (Screenshot). thisis my .xaml cell code
<?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>
For rendering things inside RecyclerView i'm using CellContainer that creates PlatformRenderer and overrides methods OnLayout and 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);
}
My questions:
- What is the best way to get native objects from forms .xaml?
- Why some labels don't render themselves?
- What should i do to render all VisualElemts properly?
Feel free to download my sample project.
Upvotes: 0
Views: 1140
Reputation: 2626
I found answer. My problem was indeed connected with HorizontalOptions, your note was useful.
(...) Additionally it specifies if the element should consume leftover space in the X axis from the parent layout. (...)
I just added parent to XF version of my viewHolder. Here you can see what i changed:
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);
}
It's just 1 line of code, now the labels are working perfectly with every setting of HorizontalOptions. This was very hard to find out, because native (android) ViewHolder had native parent, so i thought XF VievHolder instantly get the same parent but as XF version (from renderer). I was mistaken, and i had to set it on my own.
I know now, that all calculations from .xaml are implemented in core of Xamarin.Forms and to proper calculations children must have their parents.
Upvotes: 1
Reputation: 16652
I personally think this is a bug using HorizontalOptions
of Label
inside a Grid
. Not sure, you can try to file this issue in Bugzilla.
By default HorizontalOptions is set to LayoutOptions.Fill
, it works when this property is set by default or to FillAndExpand
, the others with different HorizontalOptions
has a 0 width by my side, that's why only some of your labels are shown.
A workaround to solve this issue is to give a WidthRequest
to these labels with 0 width, but please notice that
Assigning the HorizontalOptions modifies how the element is laid out when there is excess space available along the X axis from the parent layout. Additionally it specifies if the element should consume leftover space in the X axis from the parent layout. If multiple children of a layout are set to expand, the extra space is distributed proportionally.
Your width here should be smaller here than the parent to see the results of different LayoutOptions
.
Upvotes: 0