Android9 Flexlayout 错误?
Android9 Flexlayout bug?
我在框架内有一个 flexlayout,一个网格和一个 stacklayout:
<CollectionView x:Name="collectionView"
Margin="20"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"
ItemSpacing="10" />
</CollectionView.ItemsLayout>
<!-- Define the appearance of each item in the list -->
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="0,10,0,0" Spacing="0">
<StackLayout Orientation="Horizontal" Margin="0">
<Label FontSize="Medium" FontAttributes="Bold" Text="{Binding Name, Mode=OneWay}" />
<Label Text="{Binding Bottle.ID, Mode=OneWay}" FontSize="Medium"
FontAttributes="Italic" VerticalTextAlignment="Center"
Margin="20,0,0,0"/>
</StackLayout>
<Frame CornerRadius="10" BorderColor="Black" IsVisible="{Binding IsConnected, Converter={StaticResource InverseBooleanConverter}, Mode=OneWay}" Padding="10">
<Label Text="not connected" FontSize="Small"/>
</Frame>
<Frame CornerRadius="10" BorderColor="Black" IsVisible="{Binding IsConnected, Mode=OneWay}" Padding="10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0">
<FlexLayout BindableLayout.ItemsSource="{Binding Bottle.Components, Mode=OneWay}"
Direction="Row" JustifyContent="Start" Wrap="Wrap"
AlignItems="Center" AlignContent="Start"
>
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label FontSize="Small" Text="{Binding DisplayString}" VerticalOptions="CenterAndExpand" Margin="0,0,10,0" Padding="0"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
<StackLayout Orientation="Horizontal" IsVisible="{Binding IsConnected, Mode=OneWay}" VerticalOptions="FillAndExpand">
<Label FontSize="Small" Text="in " IsVisible="{Binding Bottle.IsCarrier, Converter={StaticResource InverseBooleanConverter}, Mode=OneWay}"/>
<Label FontSize="Small" Text="{Binding Bottle.CarrierGas, Mode=OneWay}"/>
<Label FontSize="Small" Text=" 100%" IsVisible="{Binding Bottle.IsCarrier, Mode=OneWay}" />
</StackLayout>
</StackLayout>
<Label FontSize="Small" Text="(ISO)" FontAttributes="Italic" Grid.Column="1" IsVisible="False">
<Label.Triggers>
<MultiTrigger TargetType="Label">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Bottle.IsISOCertified, Mode=OneWay}" Value="True" />
<BindingCondition Binding="{Binding IsConnected, Mode=OneWay}" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="IsVisible" Value="True" />
</MultiTrigger>
</Label.Triggers>
</Label>
</Grid>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
在特定的输入数据处,flexlayout 显示不正确(见列表中的第一个元素):
如果我删除框架,文本会正确显示:
如果我保留框架并将 flexlayout 元素的边距或填充增加一个,文本将正确显示为 2 行。
<DataTemplate>
<Label FontSize="Small" Text="{Binding DisplayString}" VerticalOptions="CenterAndExpand" Margin="0,0,11,0"/>
</DataTemplate>
此外,如果我将字体大小增加到中号,它也能正确显示。另外,如果我不重新启动应用程序,而是让 visual studio 自动更新表单,在减小字体大小后,显示是正确的。但是,如果我重新启动 Android 模拟器,问题又会出现。
中号字体:
<DataTemplate>
<Label FontSize="Medium" Text="{Binding DisplayString}" VerticalOptions="CenterAndExpand" Margin="0,0,10,0"/>
</DataTemplate>
将其重置为小但未重新启动应用程序后:
我想,增加 Margin/Padding 或增加字体大小或删除框架在长 运行 上无法正常工作,因为总会有一些 data/screen 大小打破布局的组合。
在我看来,布局仅在某些极端情况下失败。但是,当元素宽度(+边距)超过某个限制时,flexlayout 认为元素必须显示在 2 行中,然后布局就可以了。
这是xamarin/android的bug,还是我的代码有问题?谁能提出可靠的解决方法和解决方案?
一些额外的信息,不确定需要什么:
- 目标 Android 版本:Android 9.0(API 28 级 - 派)
- 模拟器:Pixel 2 Pie 9.0 - API28 1080x1920 420dpi
编辑:我还测试了 1020x1920、1000x1920、800x1920、1200x1920 显示尺寸,它们都出现了同样的问题。因此,要么 Android 设备管理器中的设置未被接管 (hw.lcd.width),要么问题与显示大小无关。
Edit2:在 Settings/Display 中更改显示大小会影响布局,如果我将其设置为更低或更大,则会修复它。但我猜它只是改变了标签的 FontSize 参数。
在测试@ToolmakerSteve 的解决方案时,我发现如果删除列表中的最后一项(NO 2100ppm),children 界限会更高。Width-s 会更高。然后我为每个 children 调用了 measure,我看到,当最后一个项目丢失时,宽度与 bound.Width-s 相匹配。所以我得到了这段代码:
//
class FlexLayoutImproveMeasure : FlexLayout
{
protected override void LayoutChildren(double x, double y, double width, double height)
{
base.LayoutChildren(x, y, width, height);
foreach (var child in Children)
{
Rectangle cb = child.Bounds;
Size s = child.Measure(double.PositiveInfinity, cb.Height).Request;
double newW = s.Width;
var childBounds2 = new Rectangle(cb.X, cb.Y, newW, cb.Height);
child.Layout(childBounds2);
}
}
}
我发现 FlexLayout
有时没有给每个标签足够的空间;标签被截断。
这是 FlexLayout 的子class,它为每个 Label 添加了少量宽度。
FlexLayoutImproveMeasure.cs:
using Xamarin.Forms;
namespace XFSOAnswers
{
class FlexLayoutImproveMeasure : FlexLayout
{
protected override void LayoutChildren(double x, double y, double width, double height)
{
base.LayoutChildren(x, y, width, height);
//var childBoundss = new List<Rectangle>();
foreach (var child in Children)
{
Rectangle cb = child.Bounds;
// Make each child slightly wider: labels were being truncated.
// Less than 2 didn't always work. Increase as needed, up to right margin.
const int extraW = 2;
double newX = cb.X;
// TBD whether part of the problem is label that isn't pixel-aligned.
// (So far, seems more reliable to increase extraW instead.)
//MAYBE double newX = Math.Floor(cb.X);
double newW = cb.Width + extraW;
// TBD whether it helps to ensure width extends to next pixel.
// (So far, seems more reliable to increase extraW instead.)
//MAYBE double newW = Math.Ceiling(cb.Width) + extraW;
//double ceilingRight = Math.Ceiling(cb.X + cb.Width);
//MAYBE double newW = ceilingRight - Math.Floor(cb.X) + extraW;
var childBounds2 = new Rectangle(newX, cb.Y, newW, cb.Height);
child.Layout(childBounds2);
//childBoundss.Add(child.Bounds);
//childBoundss.Add(cb);
}
}
}
}
用这个代替 FlexLayout
。这是通过向页面添加一个 xmlns:
声明来访问项目中的名称空间来完成的。这里的命名空间是 XFSOAnswers
。更改 xmlns:local
行以引用您将上述 class 放入的任何名称空间。
XAML 个页面:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XFSOAnswers"
x:Class="XFSOAnswers.FlexLayoutInItemTemplatePage">
<ContentPage.Content>
<StackLayout>
<!-- labels truncated at width in range 188-194 -->
<CollectionView ItemsSource="{Binding Items}"
WidthRequest="190" HorizontalOptions="Center"
Margin="20" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame CornerRadius="10" BorderColor="Black"
BackgroundColor="LightBlue" Padding="10">
<local:FlexLayoutImproveMeasure
Wrap="Wrap"
BindableLayout.ItemsSource="{Binding Items2}"
BackgroundColor="LightGray" >
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label FontSize="Small" Text="{Binding .}"
VerticalOptions="Start" HorizontalOptions="Start" Margin="0,0,10,0" Padding="0"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</local:FlexLayoutImproveMeasure>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
页面后面的代码有绑定:
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace XFSOAnswers
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FlexLayoutInItemTemplatePage : ContentPage
{
public FlexLayoutInItemTemplatePage()
{
InitializeComponent();
BindingContext = this;
}
public List<ListModel> Items { get; } = new List<ListModel> {
new ListModel(),
new ListModel(),
};
}
public class ListModel
{
public List<string> Items2 { get; } = new List<string> {
"aaa", "bbbbbbbbb", "cc", "dddd", "e",
};
}
}
Items2
选择的字符串使得在 WidthRequest=190 时,“dddd”几乎不适合第一行。使用和不使用最后的“e”进行测试,以了解 FlexLayout 在这两种情况下的作用。
原始 FlexLayout,截断标签:
将 FlexLayoutImproveMeasure 与 extraW = 2 结合使用:
我在框架内有一个 flexlayout,一个网格和一个 stacklayout:
<CollectionView x:Name="collectionView"
Margin="20"
SelectionMode="Single"
SelectionChanged="OnSelectionChanged">
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical"
ItemSpacing="10" />
</CollectionView.ItemsLayout>
<!-- Define the appearance of each item in the list -->
<CollectionView.ItemTemplate>
<DataTemplate>
<StackLayout Margin="0,10,0,0" Spacing="0">
<StackLayout Orientation="Horizontal" Margin="0">
<Label FontSize="Medium" FontAttributes="Bold" Text="{Binding Name, Mode=OneWay}" />
<Label Text="{Binding Bottle.ID, Mode=OneWay}" FontSize="Medium"
FontAttributes="Italic" VerticalTextAlignment="Center"
Margin="20,0,0,0"/>
</StackLayout>
<Frame CornerRadius="10" BorderColor="Black" IsVisible="{Binding IsConnected, Converter={StaticResource InverseBooleanConverter}, Mode=OneWay}" Padding="10">
<Label Text="not connected" FontSize="Small"/>
</Frame>
<Frame CornerRadius="10" BorderColor="Black" IsVisible="{Binding IsConnected, Mode=OneWay}" Padding="10">
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="40" />
</Grid.ColumnDefinitions>
<StackLayout Grid.Column="0">
<FlexLayout BindableLayout.ItemsSource="{Binding Bottle.Components, Mode=OneWay}"
Direction="Row" JustifyContent="Start" Wrap="Wrap"
AlignItems="Center" AlignContent="Start"
>
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label FontSize="Small" Text="{Binding DisplayString}" VerticalOptions="CenterAndExpand" Margin="0,0,10,0" Padding="0"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</FlexLayout>
<StackLayout Orientation="Horizontal" IsVisible="{Binding IsConnected, Mode=OneWay}" VerticalOptions="FillAndExpand">
<Label FontSize="Small" Text="in " IsVisible="{Binding Bottle.IsCarrier, Converter={StaticResource InverseBooleanConverter}, Mode=OneWay}"/>
<Label FontSize="Small" Text="{Binding Bottle.CarrierGas, Mode=OneWay}"/>
<Label FontSize="Small" Text=" 100%" IsVisible="{Binding Bottle.IsCarrier, Mode=OneWay}" />
</StackLayout>
</StackLayout>
<Label FontSize="Small" Text="(ISO)" FontAttributes="Italic" Grid.Column="1" IsVisible="False">
<Label.Triggers>
<MultiTrigger TargetType="Label">
<MultiTrigger.Conditions>
<BindingCondition Binding="{Binding Bottle.IsISOCertified, Mode=OneWay}" Value="True" />
<BindingCondition Binding="{Binding IsConnected, Mode=OneWay}" Value="True" />
</MultiTrigger.Conditions>
<Setter Property="IsVisible" Value="True" />
</MultiTrigger>
</Label.Triggers>
</Label>
</Grid>
</Frame>
</StackLayout>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
在特定的输入数据处,flexlayout 显示不正确(见列表中的第一个元素):
如果我删除框架,文本会正确显示:
如果我保留框架并将 flexlayout 元素的边距或填充增加一个,文本将正确显示为 2 行。
<DataTemplate>
<Label FontSize="Small" Text="{Binding DisplayString}" VerticalOptions="CenterAndExpand" Margin="0,0,11,0"/>
</DataTemplate>
此外,如果我将字体大小增加到中号,它也能正确显示。另外,如果我不重新启动应用程序,而是让 visual studio 自动更新表单,在减小字体大小后,显示是正确的。但是,如果我重新启动 Android 模拟器,问题又会出现。
中号字体:
<DataTemplate>
<Label FontSize="Medium" Text="{Binding DisplayString}" VerticalOptions="CenterAndExpand" Margin="0,0,10,0"/>
</DataTemplate>
将其重置为小但未重新启动应用程序后:
我想,增加 Margin/Padding 或增加字体大小或删除框架在长 运行 上无法正常工作,因为总会有一些 data/screen 大小打破布局的组合。
在我看来,布局仅在某些极端情况下失败。但是,当元素宽度(+边距)超过某个限制时,flexlayout 认为元素必须显示在 2 行中,然后布局就可以了。
这是xamarin/android的bug,还是我的代码有问题?谁能提出可靠的解决方法和解决方案?
一些额外的信息,不确定需要什么:
- 目标 Android 版本:Android 9.0(API 28 级 - 派)
- 模拟器:Pixel 2 Pie 9.0 - API28 1080x1920 420dpi
编辑:我还测试了 1020x1920、1000x1920、800x1920、1200x1920 显示尺寸,它们都出现了同样的问题。因此,要么 Android 设备管理器中的设置未被接管 (hw.lcd.width),要么问题与显示大小无关。
Edit2:在 Settings/Display 中更改显示大小会影响布局,如果我将其设置为更低或更大,则会修复它。但我猜它只是改变了标签的 FontSize 参数。
在测试@ToolmakerSteve 的解决方案时,我发现如果删除列表中的最后一项(NO 2100ppm),children 界限会更高。Width-s 会更高。然后我为每个 children 调用了 measure,我看到,当最后一个项目丢失时,宽度与 bound.Width-s 相匹配。所以我得到了这段代码:
//
class FlexLayoutImproveMeasure : FlexLayout
{
protected override void LayoutChildren(double x, double y, double width, double height)
{
base.LayoutChildren(x, y, width, height);
foreach (var child in Children)
{
Rectangle cb = child.Bounds;
Size s = child.Measure(double.PositiveInfinity, cb.Height).Request;
double newW = s.Width;
var childBounds2 = new Rectangle(cb.X, cb.Y, newW, cb.Height);
child.Layout(childBounds2);
}
}
}
我发现 FlexLayout
有时没有给每个标签足够的空间;标签被截断。
这是 FlexLayout 的子class,它为每个 Label 添加了少量宽度。
FlexLayoutImproveMeasure.cs:
using Xamarin.Forms;
namespace XFSOAnswers
{
class FlexLayoutImproveMeasure : FlexLayout
{
protected override void LayoutChildren(double x, double y, double width, double height)
{
base.LayoutChildren(x, y, width, height);
//var childBoundss = new List<Rectangle>();
foreach (var child in Children)
{
Rectangle cb = child.Bounds;
// Make each child slightly wider: labels were being truncated.
// Less than 2 didn't always work. Increase as needed, up to right margin.
const int extraW = 2;
double newX = cb.X;
// TBD whether part of the problem is label that isn't pixel-aligned.
// (So far, seems more reliable to increase extraW instead.)
//MAYBE double newX = Math.Floor(cb.X);
double newW = cb.Width + extraW;
// TBD whether it helps to ensure width extends to next pixel.
// (So far, seems more reliable to increase extraW instead.)
//MAYBE double newW = Math.Ceiling(cb.Width) + extraW;
//double ceilingRight = Math.Ceiling(cb.X + cb.Width);
//MAYBE double newW = ceilingRight - Math.Floor(cb.X) + extraW;
var childBounds2 = new Rectangle(newX, cb.Y, newW, cb.Height);
child.Layout(childBounds2);
//childBoundss.Add(child.Bounds);
//childBoundss.Add(cb);
}
}
}
}
用这个代替 FlexLayout
。这是通过向页面添加一个 xmlns:
声明来访问项目中的名称空间来完成的。这里的命名空间是 XFSOAnswers
。更改 xmlns:local
行以引用您将上述 class 放入的任何名称空间。
XAML 个页面:
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:local="clr-namespace:XFSOAnswers"
x:Class="XFSOAnswers.FlexLayoutInItemTemplatePage">
<ContentPage.Content>
<StackLayout>
<!-- labels truncated at width in range 188-194 -->
<CollectionView ItemsSource="{Binding Items}"
WidthRequest="190" HorizontalOptions="Center"
Margin="20" >
<CollectionView.ItemsLayout>
<LinearItemsLayout Orientation="Vertical" ItemSpacing="10" />
</CollectionView.ItemsLayout>
<CollectionView.ItemTemplate>
<DataTemplate>
<Frame CornerRadius="10" BorderColor="Black"
BackgroundColor="LightBlue" Padding="10">
<local:FlexLayoutImproveMeasure
Wrap="Wrap"
BindableLayout.ItemsSource="{Binding Items2}"
BackgroundColor="LightGray" >
<BindableLayout.ItemTemplate>
<DataTemplate>
<Label FontSize="Small" Text="{Binding .}"
VerticalOptions="Start" HorizontalOptions="Start" Margin="0,0,10,0" Padding="0"/>
</DataTemplate>
</BindableLayout.ItemTemplate>
</local:FlexLayoutImproveMeasure>
</Frame>
</DataTemplate>
</CollectionView.ItemTemplate>
</CollectionView>
</StackLayout>
</ContentPage.Content>
</ContentPage>
页面后面的代码有绑定:
using System.Collections.Generic;
using Xamarin.Forms;
using Xamarin.Forms.Xaml;
namespace XFSOAnswers
{
[XamlCompilation(XamlCompilationOptions.Compile)]
public partial class FlexLayoutInItemTemplatePage : ContentPage
{
public FlexLayoutInItemTemplatePage()
{
InitializeComponent();
BindingContext = this;
}
public List<ListModel> Items { get; } = new List<ListModel> {
new ListModel(),
new ListModel(),
};
}
public class ListModel
{
public List<string> Items2 { get; } = new List<string> {
"aaa", "bbbbbbbbb", "cc", "dddd", "e",
};
}
}
Items2
选择的字符串使得在 WidthRequest=190 时,“dddd”几乎不适合第一行。使用和不使用最后的“e”进行测试,以了解 FlexLayout 在这两种情况下的作用。
原始 FlexLayout,截断标签:
将 FlexLayoutImproveMeasure 与 extraW = 2 结合使用: