如何覆盖两个Xamarin Forms布局,以便两者都可以接收触摸输入?

Owe*_*enP 5 xamarin.forms

(我最初在Xamarin论坛上发布了这个,但后来我决定在这里得到更快的答案?)TL; DR:有些布局会计算透明背景上的点击,其他布局则不会.在容器上设置InputTransparent为其所有子项设置它,我觉得孩子应该能够覆盖父项.我需要创建覆盖另一个元素的元素,并通过透明区域传递水龙头,但仍然有可点击的按钮.当我尝试使用网格时,它不起作用.我不想回到AbsoluteLayouts.我现在主要在iOS工作,我不太确定它是不是Android的问题.Windows Phone/UWP不在桌面上.

更长的版本:

我正在重写一些在较旧的Xamarin Forms(我认为1.3)中运行正常的布局.我们最近升级到2.1,它对布局代码的错误决定造成了严重破坏.我的任务是更新布局以表现自己.虽然我认识2.2已经发布,但我只是尝试升级,我输入的所有内容在该版本中也是如此,所以它不是2.1和2.2的问题,或者至少如果做了一些改进他们没有帮助我.

它是一个地图应用程序,因此所有布局的核心都是昂贵的,不稳定的OpenGL元素.这个元素非常不喜欢被重新定义,所以我采用了类似于这个虚构的XAML的布局:

<ContentPage>
    <CustomLayout>
        <OurHeaderControl /> 
        <TheMapControl /> 
        <OurFooterControl /> 
        <MapOverlay /> 
    </CustomLayout>
</ContentPage
Run Code Online (Sandbox Code Playgroud)

"MapOverlay"的目的是通过在页眉/页脚区域和/或地图上添加Xamarin元素来实现我们的工作流程.例如,一个布局会在页脚上方的底部添加一个方向列表,因此它显示的地图空间较小.自定义布局理解这一点并在叠加后布局地图,以便它可以请求正确的地图边界.

在这种布局中,我无法点击MapOverlay结束的任何内容.我可以把它变成InputTransparent并点击那些东西,但是它的所有子节点也都是不可点击的.在旧版面中不是这样.

这是我在旧布局和新布局之间看到的唯一区别:

旧的布局是AbsoluteLayouts的一个复杂的混乱.它看起来像这样,我没写它:

<ContentPage>
    <AbsoluteLayout> // "main layout"
        <AbsoluteLayout> // "map layout"
            <Map /> // An AbsoluteLayout containing the OpenGL view.
        </AbsoluteLayout>
        <AbsoluteLayout> // "child layout"
            <SubPage /> // An AbsoluteLayout
        </AbsoluteLayout>
    </AbsoluteLayout>
</ContentPage>
Run Code Online (Sandbox Code Playgroud)

主布局包含AbsoluteLayouts以约束子视图.一个子视图本身就是一个AbsoluteLayout,它包含Map和一些与之关联的其他元素.另一个子项是叠加层,它始终是一个AbsoluteLayout,其中包含与该叠加层相关的元素.这些布局都以周期相互引用,并在布局事件发生变化时相互更新.这是一个迷人的乒乓球,最终落户.通常.有时候事情就会消失.显然我有一个改写它的原因.

但我可以点击我需要在每一层点击的内容,但我没有得到.

所以,让我们谈谈我需要做些什么,也许可以弄清楚它是否是一个错误,为什么它不起作用,或者它是否与其他布局一起使用是巧合.这是一个非XAML页面布局,演示了我的项目源于你不能在共享库中使用XAML的日子:

我需要能够点击此UI中的两个按钮并让它们做出响应.

public class MyPage : ContentPage {
    public MyPage() {

        var mainLayout = new AbsoluteLayout();

        // Two buttons will be overlaid.
        var overlaidButton = new Button() {
            Text = "Overlaid",
            Command = new Command((o) => DisplayAlert("Upper!", "Overlaid button.", "Ah."))
        };
        mainLayout.Children.Add(overlaidButton, new Rectangle(0.25, 0.25, AbsoluteLayout.AutoSize, AbsoluteLayout.AutoSize), AbsoluteLayoutFlags.PositionProportional);

        // The top overlay layout will be a grid.
        var overlay = new Grid() {
            RowDefinitions = { new RowDefinition() { Height = new GridLength(1.0, GridUnitType.Star) } },
            ColumnDefinitions = {
                new ColumnDefinition() { Width = new GridLength(1.0, GridUnitType.Star) },
                new ColumnDefinition() { Width = new GridLength(1.0, GridUnitType.Star) },
            },
            BackgroundColor = Color.Transparent
        };
        var overlayingButton = new Button() {
            Text = "Overlaying",
            Command = new Command((o) => DisplayAlert("Upper!", "Overlaying button.", "Ah."))
        };
        overlay.Children.Add(overlayingButton, 0, 1);
        mainLayout.Children.Add(overlay, new Rectangle(0, 0, 1.0, 1.0), AbsoluteLayoutFlags.All);

        // This pair of property sets makes the overlaid button clickable, but not the overlaying!
//      overlay.InputTransparent = true;
//      upperOverlayButton.InputTransparent = false;


        Content = mainLayout;
    }
}
Run Code Online (Sandbox Code Playgroud)

这只允许我点击"覆盖"按钮,即使我将网格更改为AbsoluteLayout.

我很难过.我花了两周的时间来调试初始布局并提出了一个新的解决方案.我真的不想拆解所有的布局,并将所有内容放在一个大的AbsoluteLayout或自定义布局中.在WPF中,有两种透明:"透明背景"意味着背景仍然可以进行测试,"空背景"意味着背景不会受到测试.有没有办法在Xamarin中叠加这样的布局?

或者,更合适的是,为什么我们旧布局中的众多AbsoluteLayouts的复杂嵌套像我需要的那样工作,但这个更简单的布局不是?

更新

这是我记得的一些额外信息:

Pau*_*aul 7

总的来说,与其他两个平台相比,iOS 的行为似乎是如何InputTransparent处理的Grid。我不确定此时是否会将当前行为量化为错误,但我知道遇到平台行为差异令人沮丧。

不过,如果我理解正确的话,可以针对您的情况进行各种修复。似乎之前提交过类似的报告,并且通过此 SO 链接提到了有关 iOS 的行为。这个问题是在非 Forms iOS 应用程序的范围内提出的,但逻辑可以在这里应用。

通过使用自定义渲染器(让我们以 aCustomGrid为例),您可以具体实现 的 iOS 实现,Grid以遵循上述链接查找底层视图的方式:

CustomGrid.cs (PCL):

public class CustomGrid : Grid
{
    public CustomGrid() { }
}
Run Code Online (Sandbox Code Playgroud)

CustomGrid.cs (iOS):

[assembly: ExportRenderer(typeof(CustomGrid), typeof(CustomGridRenderer))]
public class CustomGridRenderer : ViewRenderer
{
    public override UIKit.UIView HitTest(CoreGraphics.CGPoint point, UIKit.UIEvent uievent)
    {
        UIView hitView = base.HitTest(point, uievent);
        if (hitView == this)
        {
            return null;
        }
        return hitView;
    }
}
Run Code Online (Sandbox Code Playgroud)

以这种方式,您不应该InputTransparent为 iOS显式设置,并且网格本身上的任何点击都会发送到下面的任何内容。InputTransparent但是,由于 Android 使用,因此在这种特殊情况下,您可以将其包装在Device.OnPlatform语句中,如果您不想,则跳过实现 Android 自定义呈现器:

Device.OnPlatform(Android: () => 
{
    overlay.InputTransparent = true 
});
Run Code Online (Sandbox Code Playgroud)

使用上面的代码修改为使用CustomGrid和 iOS 渲染器,我可以点击两个按钮。