如何在自定义渲染器中呈现Xamarin.Forms视图

3

我有以下代码:

共享项目:

using System;
using Xamarin.Forms;

namespace XamarinForms.Framework.Controls
{
       public enum DrawerPosition
       {
             Left,
             Right
       }

       public interface ISideDrawerNativeEventProxy
       {
             void RaiseSlideChanged(float percentage);
       }

       public class SideDrawer : Grid, ISideDrawerNativeEventProxy
       {
             #region DrawerSize

             public static BindableProperty DrawerSizeProperty = BindableProperty.Create<SideDrawer, MaxedPercentSize>(d => d.DrawerSize, new MaxedPercentSize(MaxedPercentSizeType.Width, 80, 400));

             public MaxedPercentSize DrawerSize
             {
                    get { return (MaxedPercentSize) GetValue(DrawerSizeProperty); }
                    set { SetValue(DrawerSizeProperty, value); }
             }

             #endregion DrawerSize

             #region IsOpen

             public static BindableProperty IsOpenProperty = BindableProperty.Create<SideDrawer, bool>(d => d.IsOpen, default(bool));

             public bool IsOpen
             {
                    get { return (bool) GetValue(IsOpenProperty); }
                    set { SetValue(IsOpenProperty, value); }
             }

             #endregion IsOpen

             #region DrawerPosition

             public static BindableProperty DrawerPositionProperty = BindableProperty.Create<SideDrawer, DrawerPosition>(d => d.DrawerPosition, default(DrawerPosition));

             public DrawerPosition DrawerPosition
             {
                    get { return (DrawerPosition) GetValue(DrawerPositionProperty); }
                    set { SetValue(DrawerPositionProperty, value); }
             }

             #endregion DrawerPosition

             public static BindableProperty MainContentProperty = BindableProperty.Create<SideDrawer, View>(d => d.MainContent, default(View));

             public View MainContent
             {
                    get { return (View) GetValue(MainContentProperty); }
                    set { SetValue(MainContentProperty, value); }
             }

             public static BindableProperty DrawerContentProperty = BindableProperty.Create<SideDrawer, View>(d => d.DrawerContent, default(View));

             public View DrawerContent
             {
                    get { return (View) GetValue(DrawerContentProperty); }
                    set { SetValue(DrawerContentProperty, value); }
             }

             #region DrawerLength

             public static BindableProperty DrawerLengthProperty = BindableProperty.Create<SideDrawer, int>(d => d.DrawerLength, default(int), defaultValueCreator: DrawerLengthDefault);

             private static int DrawerLengthDefault(SideDrawer bindable)
             {
                    return 300;
             }

             public int DrawerLength
             {
                    get { return (int) GetValue(DrawerLengthProperty); }
                    set { SetValue(DrawerLengthProperty, value); }
             }

             #endregion DrawerLength

             #region IsContentTranslated

             public static BindableProperty IsContentTranslatedProperty = BindableProperty.Create<SideDrawer, bool>(d => d.IsContentTranslated, true);

             public bool IsContentTranslated
             {
                    get { return (bool) GetValue(IsContentTranslatedProperty); }
                    set { SetValue(IsContentTranslatedProperty, value); }
             }

             #endregion IsContentTranslated

             void ISideDrawerNativeEventProxy.RaiseSlideChanged(float percentage)
             {
             }
       }
}

安卓操作系统:

using System;
using System.ComponentModel;
using Android.Support.V4.Widget;
using Android.Views;
using Android.Widget;
using Mobile.XamarinForms.Droid.Controls.SideDrawer;
using Mobile.XamarinForms.Framework.Controls;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Android.Graphics;
using Application = Android.App.Application;
using Color = Android.Graphics.Color;
using RelativeLayout = Android.Widget.RelativeLayout;
using View = Android.Views.View;

[assembly: ExportRenderer(typeof(SideDrawer), typeof(SideDrawerRenderer))]
namespace Mobile.XamarinForms.Droid.Controls.SideDrawer
{
//     public class SideDrawerRenderer : Xamarin.Forms.Platform.Android.AppCompat.ViewRenderer<Framework.Controls.SideDrawer, DrawerLayout>, DrawerLayout.IDrawerListener
       public class SideDrawerRenderer : ViewRenderer<Framework.Controls.SideDrawer, DrawerLayout>, DrawerLayout.IDrawerListener
       {
             private DrawerLayout _nativeDrawerLayout;
             private MarginLayoutParams _contentLayoutParameters;
             private RelativeLayout _contentView;
             private TextView _drawerView;

             protected override void OnElementChanged(ElementChangedEventArgs<Framework.Controls.SideDrawer> e)
             {
                    base.OnElementChanged(e);

                    if (this.Control == null)
                    {
                           InitializeNativeControl();
                    }
                    if (e.OldElement != null)
                    {
                           _nativeDrawerLayout.SetDrawerListener(null);
            }
                    if (e.NewElement != null)
                    {
                           _nativeDrawerLayout.SetDrawerListener(this);
                    }
             }

             private void InitializeNativeControl()
             {
                    _nativeDrawerLayout = new DrawerLayout(Context.ApplicationContext);
                    _nativeDrawerLayout.SetBackgroundColor(Element.BackgroundColor.ToAndroid());


                    _contentLayoutParameters = new MarginLayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent);
                    var layoutParamsDrawer = new DrawerLayout.LayoutParams(Element.DrawerLength, LinearLayout.LayoutParams.MatchParent);
                    layoutParamsDrawer.Gravity = GetDrawerGravity();

                    _drawerView = GetDrawerView(layoutParamsDrawer);
                    _contentView = GetContentView(_contentLayoutParameters);

                    // this one works, but i need the content from my forms property
                    var contentChild = new RelativeLayout(Context.ApplicationContext);
                    var contentChildLParams = new RelativeLayout.LayoutParams(300, 300);
                    contentChild.SetBackgroundColor(Color.Yellow);
                    _contentView.AddView(contentChild, contentChildLParams);

                    // i need to figure out how to make this work
//                  var contentRenderer = RendererFactory.GetRenderer(Element.MainContent);
//                  _contentView.AddView(contentRenderer.ViewGroup, new LayoutParams(LayoutParams.MatchParent, LayoutParams.MatchParent));

                    _nativeDrawerLayout.AddView(_drawerView);
                    _nativeDrawerLayout.AddView(_contentView);

                    SetNativeControl(_nativeDrawerLayout);
             }

             private int GetDrawerGravity()
             {
                    switch (Element.DrawerPosition)
                    {
                           case DrawerPosition.Left:
                                  return (int)GravityFlags.Start;
                           case DrawerPosition.Right:
                                  return (int)GravityFlags.End;
                           default:
                                  throw new ArgumentOutOfRangeException();
                    }
             }

             private RelativeLayout GetContentView(LayoutParams layoutParameters)
             {
                    var view = new RelativeLayout(Context.ApplicationContext);
                    view.LayoutParameters = layoutParameters;
                    view.SetBackgroundColor(Color.Red);
                    return view;
             }

             private TextView GetDrawerView(LayoutParams layoutParameters)
             {
                    var view = new TextView(Context.ApplicationContext);
                    view.LayoutParameters = layoutParameters;
                    view.SetBackgroundColor(Color.Purple);
                    view.SetTextColor(Color.Blue);
                    view.SetText("just some text", TextView.BufferType.Editable);
                    return view;
             }

             protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
             {
                    base.OnElementPropertyChanged(sender, e);

                    switch (e.PropertyName)
                    {
                           case nameof(Framework.Controls.SideDrawer.Height):
                                  break;
                           case nameof(Framework.Controls.SideDrawer.Width):
                                  break;
                    }
             }

             public void OnDrawerClosed(View drawerView)
             {
                    Element.IsOpen = false;
                    System.Diagnostics.Debug.WriteLine("OnDrawerClosed");
             }

             public void OnDrawerOpened(View drawerView)
             {
                    Element.IsOpen = true;
                    System.Diagnostics.Debug.WriteLine("OnDrawerOpened");
             }

             public void OnDrawerSlide(View drawerView, float slideOffset)
             {
                    switch (Element.DrawerPosition)
                    {
                           case DrawerPosition.Left:
                                  _contentView.TranslationX = (int) Math.Abs(Element.DrawerLength*slideOffset);
                                  break;
                           case DrawerPosition.Right:
                                  _contentView.TranslationX = (int) Math.Abs(Element.DrawerLength*slideOffset)*-1;
                                  break;
                           default:
                                  throw new ArgumentOutOfRangeException();
                    }

                    _nativeDrawerLayout.BringChildToFront(_drawerView);
                    _nativeDrawerLayout.RequestLayout();

                    ((ISideDrawerNativeEventProxy) Element)?.RaiseSlideChanged(slideOffset);
             }

             public void OnDrawerStateChanged(int newState)
             {
                    // not really needed
//                  System.Diagnostics.Debug.WriteLine($"OnDrawerStateChanged {newState}");
             }
       }
}

如何输出例如MainContent的内容(共享项目属性)?

我在Xamarin文档中找不到关于这个问题的任何信息,而且支持团队对这个话题也相当沉默(我猜想他们太忙了)。

有没有人遇到过这个问题?

更新:重现解决方案


不太确定你的问题是什么。自定义渲染器只是覆盖并扩展了从XF控件传递过来的本地控件的输出。你想从XF侧获取什么以在页面上显示? - Adam
@Adam 例如,在抽屉中有一个列表和一些 StackLayout 在内容区域。我认为一定有办法做到这一点?否则就不会有任何 Xamarin Forms 的组件供应商了。 - Dbl
我无法让你的代码生成任何东西。你有一个已经设置好的小演示项目可以发送过来吗?如果是这样的话,我的联系方式在我的个人资料上。 - Pete
@Pete 我会精简我的项目并很快提供一个压缩包。 - Dbl
@Pete,关于未实现的方法,我想它们实际上是已经实现了的?(如果你生成子类,它会抱怨重复的方法签名吗?) - Dbl
显示剩余13条评论
1个回答

1
安卓容器:
internal sealed class FormsElementWrapper : FormsViewGroup
{
        public override bool OnInterceptTouchEvent(MotionEvent ev)
    {
        return false;
    }

    private readonly IVisualElementRenderer _view;

    public FormsElementWrapper(Xamarin.Forms.View content) : base(Application.Context)
    {
        _view = content != null ? Platform.CreateRenderer(content) : null;
        if (_view == null)
            return;
        AddView(_view.ViewGroup);
    }

    protected override void OnLayout(bool changed, int left, int top, int right, int bottom)
    {
        if (_view == null)
            return;
        _view.Element.Layout(new Rectangle(0.0, 0.0, ContextExtensions.FromPixels(Context, right - left), ContextExtensions.FromPixels(Context, bottom - top)));
        _view.UpdateLayout();
    }

    protected override void OnMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        MeasureSpecMode widthMode = MeasureSpec.GetMode(widthMeasureSpec);
        MeasureSpecMode heightMode = MeasureSpec.GetMode(heightMeasureSpec);
        int widthSize = MeasureSpec.GetSize(widthMeasureSpec);
        int heightSize = MeasureSpec.GetSize(heightMeasureSpec);
        int pxHeight = (int) ContextExtensions.ToPixels(Context, _view.Element.HeightRequest);
        int pxWidth = (int) ContextExtensions.ToPixels(Context, _view.Element.WidthRequest);
        var measuredWidth = widthMode != MeasureSpecMode.Exactly ? (widthMode != MeasureSpecMode.AtMost ? pxHeight : Math.Min(pxHeight, widthSize)) : widthSize;
        var measuredHeight = heightMode != MeasureSpecMode.Exactly ? (heightMode != MeasureSpecMode.AtMost ? pxWidth : Math.Min(pxWidth, heightSize)) : heightSize;
        SetMeasuredDimension(measuredWidth, measuredHeight);
    }
}

安卓抽屉:
using System;
using System.ComponentModel;
using Android.Support.V4.Widget;
using Android.Views;
using Android.Widget;
using Xamarin.Forms;
using Xamarin.Forms.Platform.Android;
using Color = Android.Graphics.Color;
using RelativeLayout = Android.Widget.RelativeLayout;
using View = Android.Views.View;

[assembly: ExportRenderer(typeof(SideDrawer), typeof(SideDrawerRenderer))]
namespace MyNamespace.SideDrawer
{
    public class SideDrawerRenderer : ViewRenderer<SideDrawer, DrawerLayout>, DrawerLayout.IDrawerListener
    {
        private DrawerLayout _nativeDrawerLayout;
        private FormsElementWrapper _contentView;
        private FormsElementWrapper _drawerView;

        protected override void OnElementChanged(ElementChangedEventArgs<Framework.Controls.SideDrawer> e)
        {
            base.OnElementChanged(e);

            if (this.Control == null)
            {
                InitializeNativeControl();
            }
            if (e.OldElement != null)
            {
                _nativeDrawerLayout.SetDrawerListener(null);
            }
            if (e.NewElement != null)
            {
                _nativeDrawerLayout.SetDrawerListener(this);
            }
        }

        private void InitializeNativeControl()
        {
            _nativeDrawerLayout = new DrawerLayout(Context.ApplicationContext);
            _nativeDrawerLayout.SetBackgroundColor(Element.BackgroundColor.ToAndroid());

            AddDrawerLayer(_nativeDrawerLayout);
            AddContentLayer(_nativeDrawerLayout);

            SetNativeControl(_nativeDrawerLayout);
        }

        private void AddContentLayer(DrawerLayout nativeDrawerLayout)
        {
            _contentView = new FormsElementWrapper(Element.MainContent);

            nativeDrawerLayout.AddView(_contentView);
        }

        private void AddDrawerLayer(DrawerLayout nativeDrawerLayout)
        {
            _drawerView = new FormsElementWrapper(Element.DrawerContent);
            UpdateDrawerLength();

            nativeDrawerLayout.AddView(_drawerView);
        }

        private int GetDrawerGravity()
        {
            switch (Element.DrawerPosition)
            {
                case DrawerPosition.Left:
                    return (int)GravityFlags.Start;
                case DrawerPosition.Right:
                    return (int)GravityFlags.End;
                default:
                    throw new ArgumentOutOfRangeException();
            }
        }
        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);

            switch (e.PropertyName)
            {
                case nameof(Framework.Controls.SideDrawer.Height):
                    break;
                case nameof(Framework.Controls.SideDrawer.Width):
                    break;
                case nameof(Framework.Controls.SideDrawer.MainContent):
                    _contentView = new FormsElementWrapper(Element.MainContent);
                    break;
                case nameof(Framework.Controls.SideDrawer.DrawerContent):
                    _drawerView = new FormsElementWrapper(Element.DrawerContent);
                    break;
                case nameof(Framework.Controls.SideDrawer.IsOpen):
                    UpdateDrawerStateProgramatically();
                    break;
                case nameof(Framework.Controls.SideDrawer.DrawerLength):
                case nameof(Framework.Controls.SideDrawer.DrawerPosition):
                    UpdateDrawerLength();
                    break;
            }
        }

        private void UpdateDrawerLength()
        {
            var layoutParamsDrawer = new DrawerLayout.LayoutParams(Element.DrawerLength, ViewGroup.LayoutParams.MatchParent);
            layoutParamsDrawer.Gravity = GetDrawerGravity();
            _drawerView.LayoutParameters = layoutParamsDrawer;
        }

        private void UpdateDrawerStateProgramatically()
        {
            if (Element.IsOpen)
            {
                Control.OpenDrawer(GetDrawerGravity());
            }
            else
            {
                Control.CloseDrawer(GetDrawerGravity());
            }
        }

        public void OnDrawerClosed(View drawerView)
        {
            Element.IsOpen = false;
            System.Diagnostics.Debug.WriteLine("OnDrawerClosed");
        }

        public void OnDrawerOpened(View drawerView)
        {
            Element.IsOpen = true;
            System.Diagnostics.Debug.WriteLine("OnDrawerOpened");
        }

        public void OnDrawerSlide(View drawerView, float slideOffset)
        {
            switch (Element.DrawerPosition)
            {
                case DrawerPosition.Left:
                    _contentView.TranslationX = (int)Math.Abs(Element.DrawerLength * slideOffset);
                    break;
                case DrawerPosition.Right:
                    _contentView.TranslationX = (int)Math.Abs(Element.DrawerLength * slideOffset) * -1;
                    break;
                default:
                    throw new ArgumentOutOfRangeException();
            }

            _nativeDrawerLayout.BringChildToFront(_drawerView);
            _nativeDrawerLayout.RequestLayout();

            ((ISideDrawerNativeEventProxy)Element)?.RaiseSlideChanged(slideOffset);
        }

        public void OnDrawerStateChanged(int newState)
        {
        }
    }
}

Xamarin.Forms 控件:

public enum DrawerPosition
{
    Left,
    Right
}

public interface ISideDrawerNativeEventProxy
{
    void RaiseSlideChanged(float percentage);
}

public class SideDrawer : Grid, ISideDrawerNativeEventProxy
{
    #region IsOpen

    public static BindableProperty IsOpenProperty = BindableProperty.Create<SideDrawer, bool>(d => d.IsOpen, default(bool), defaultBindingMode: BindingMode.TwoWay);

    public bool IsOpen
    {
        get { return (bool) GetValue(IsOpenProperty); }
        set { SetValue(IsOpenProperty, value); }
    }

    #endregion IsOpen

    #region DrawerPosition

    public static BindableProperty DrawerPositionProperty = BindableProperty.Create<SideDrawer, DrawerPosition>(d => d.DrawerPosition, default(DrawerPosition));

    public DrawerPosition DrawerPosition
    {
        get { return (DrawerPosition) GetValue(DrawerPositionProperty); }
        set { SetValue(DrawerPositionProperty, value); }
    }

    #endregion DrawerPosition

    public static BindableProperty MainContentProperty = BindableProperty.Create<SideDrawer, View>(d => d.MainContent, default(View), propertyChanging: MainContentPropertyChanging, propertyChanged: MainContentPropertyChanged);

    private static void MainContentPropertyChanged(BindableObject bindable, View oldValue, View newValue)
    {
        if (newValue == null)
            return;
        newValue.Parent = (View)bindable;
    }

    private static void MainContentPropertyChanging(BindableObject bindable, View oldValue, View newValue)
    {
        if (oldValue == null)
            return;
        oldValue.Parent = null;
    }

    public View MainContent
    {
        get { return (View) GetValue(MainContentProperty); }
        set { SetValue(MainContentProperty, value); }
    }

    public static BindableProperty DrawerContentProperty = BindableProperty.Create<SideDrawer, View>(d => d.DrawerContent, default(View), propertyChanging: DrawerContentPropertyChanging, propertyChanged: DrawerContentPropertyChanged);

    private static void DrawerContentPropertyChanged(BindableObject bindable, View oldValue, View newValue)
    {
        if (newValue == null)
            return;
        newValue.Parent = (View)bindable;
    }

    private static void DrawerContentPropertyChanging(BindableObject bindable, View oldValue, View newValue)
    {
        if (oldValue == null)
            return;
        oldValue.Parent = null;
    }

    public View DrawerContent
    {
        get { return (View) GetValue(DrawerContentProperty); }
        set { SetValue(DrawerContentProperty, value); }
    }

    #region DrawerLength

    public static BindableProperty DrawerLengthProperty = BindableProperty.Create<SideDrawer, int>(d => d.DrawerLength, default(int), defaultValueCreator: DrawerLengthDefault);

    private static int DrawerLengthDefault(SideDrawer bindable)
    {
        return 300;
    }

    public int DrawerLength
    {
        get { return (int) GetValue(DrawerLengthProperty); }
        set { SetValue(DrawerLengthProperty, value); }
    }

    #endregion DrawerLength

    #region IsContentTranslated

    public static BindableProperty IsContentTranslatedProperty = BindableProperty.Create<SideDrawer, bool>(d => d.IsContentTranslated, true);

    public bool IsContentTranslated
    {
        get { return (bool) GetValue(IsContentTranslatedProperty); }
        set { SetValue(IsContentTranslatedProperty, value); }
    }

    #endregion IsContentTranslated

    void ISideDrawerNativeEventProxy.RaiseSlideChanged(float percentage)
    {
    }

    protected override void OnBindingContextChanged()
    {
        base.OnBindingContextChanged();

        if (MainContent != null)
            SetInheritedBindingContext(MainContent, BindingContext);
        if (DrawerContent != null)
            SetInheritedBindingContext(DrawerContent, BindingContext);
    }
}

我原来实现的主要缺陷包括:

  • 没有转发测量数据
  • 未转发父层级关系
  • 未继承绑定上下文

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接