【问题标题】:Customizing Xamarin.Forms layouts自定义 Xamarin.Forms 布局
【发布时间】:2014-08-13 15:58:13
【问题描述】:

我正在使用 Xamarin Forms 构建 Android 和 iOS 应用程序。

我只是想在我的 Android 应用程序上为我的ListView 项目设置一个可绘制的背景。我的ListView 项目的根视图是StackLayout 的:

var listView = new ListView
{
    ItemsSource = items,
    ItemTemplate = new DataTemplate(() =>
    {
        return new ViewCell
        {
            View = new StackLayout(...)
        };
    }
};

我知道我可以使用自定义渲染器访问本机元素:

public class MyEntryRenderer : EntryRenderer
{
    protected override void OnElementChanged(ElementChangedEventArgs<Entry> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement == null) {
            var nativeEditText = (EditText)Control;
            ...
        }
    }
}

但我不确定这对于StackLayout(或任何其他布局)如何工作。

我先扩展StackLayout

public class ListViewItem : StackLayout
{
}

我在某处读到布局使用VisualElementRenderer,所以我尝试了以下方法:

public class ListViewItemRenderer : VisualElementRenderer<StackLayout>
{
    protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
    {
        base.OnElementChanged(e);

        // any way to access the native element?
    }
}

VisualElementRenderer 似乎无法让我访问本机元素。

那么有什么方法可以访问 Layout 元素的原生元素吗?或者,也许有一种不同的方法可以在我的 Android 应用中的布局上简单地设置背景可绘制对象?

【问题讨论】:

    标签: xamarin.ios xamarin xamarin.forms


    【解决方案1】:

    即使我仍然不知道如何访问布局的本机元素,VisualElementRenderer 有一种方法可以在 Android 上设置背景可绘制对象(这正是我所需要的)。所以我最终得到了以下结果:

    protected override void OnElementChanged(ElementChangedEventArgs<StackLayout> e)
    {
        base.OnElementChanged(e);
        SetBackgroundDrawable(Resources.GetDrawable(Resource.Drawable.listViewItem));
    }
    

    【讨论】:

    • VisualElementRenderer 有一个名为 Element 的属性,它返回本机元素。
    • 如何在 iOS 中实现同样的功能??
    • 嗨,我知道这是一篇旧帖子。但是请向我提供您最终使用的整个 android 源代码。包括 listview 数据模板和 stacklayout 渲染器。请!
    【解决方案2】:

    我了解到您希望连接到现有的 布局渲染器 并扩展它以访问具有额外功能(如 背景图像)的本机元素。

    最终将支持 background-image 就像 background-colour 一样,我想,跨 Layout 控件。等待这个可能是值得的,因为我不明白他们为什么不在以后的版本中实现这些。

    与此同时,您需要一些可以工作并且很容易实现的东西?

    通过从 Layout 继承渲染器来创建可绘制的背景可能不是最简单的解决方案,尽管它确实有其优势,因为您可以轻松地在所有 应用程序的strong>布局

    但是,在 ListViewItemRenderer 的代码中,它继承自 Xamarin.Forms 控件(您指定了 StackLayout)并且没有指定一个本地的、平台相关的控件,作为布局控件的基础,该控件必须与使用的 Xamarin.Forms 平台相关控件相匹配。

    每个 Renderer 都与一个 native 元素相关联。 布局控件与其他自定义原生控件渲染器没有什么不同。

    对于自定义控件,您将编写一个类似于以下内容的渲染器(注意我没有指定 layout 渲染器,因为我还没有需要这样做,我只是去根据过去的经验 - 但类似的规则应该适用于为 layout 实现 renderer 而不是自定义控件):-

    // System.Windows.Controls.Grid in this case is the root native control for a WindowsPhone renderer of MyControl
    public class MyControlRenderer : ViewRenderer<MyControlView, System.Windows.Controls.Grid>
    

    有一种更简单的方法,但是可以实现您想要做的事情:-

    更简单的方法是从 Stack Layout 控件继承而不是继承自 Grid 作为控件的根。

    然后,您可以将 Image 控件添加到 Grid 并为同一 Grid 添加 Stack Layout

    通过执行上述操作,您将能够在整个列表视图项行中实现背景图像。

    【讨论】:

    • 感谢您的回复。除非VisualElementRenderer 不是正确的布局渲染器,否则我认为布局渲染器与自定义控件渲染器不同。 VisualElementRenderer 没有为我提供访问本机控件的属性 (Control)。
    • GridStackLayout 派生自 Layout。我的直觉是您需要从 ViewRenderer 创建一个派生,因为这是 ViewRenderer 的快捷方式,然后应用 Export 在此之上,以便调用并执行您的自定义类实现。然后,您将能够在自己的 ViewRenderer 实现中添加对背景图像的额外支持,以访问适用于每个平台的本机根控件。
    • 我的猜测是StackLayout 在Android 上会呈现为LinearLayout。所以我尝试了public class ListViewItemRenderer : ViewRenderer&lt;StackLayout, LinearLayout&gt;,但Control 保持为空。
    • 会有意义,但是他们将 Android 的基础指定为仅 View,因为它们也允许 renderer涵盖不同的布局。我刚刚看到您管理了一个解决方法,但是如果您确实遵循上面的建议 确实 工作并允许您通过获得 root 访问 native 元素。