Xamarin表示iOS CustomRenderer不重新绘制

时间:2017-05-09 11:41:55

标签: ios xamarin.forms custom-renderer

我有一个自定义渲染器,可以在UITextView中显示HTML格式的文本。如果我将文本硬编码到包含控件的页面的构造函数中(因此它在自定义控件的OnElementChanged事件中设置),它显示正常。如果我等待api调用以获取文本然后设置它(因此它在自定义控件的OnElementPropertyChanged事件中设置)它不会重新绘制。如果我更改设备的方向,则会显示文本。我需要添加什么才能让它在设置时显示文字?

[assembly: ExportRenderer(typeof(HtmlLabel), typeof(HtmlLabelRenderer))]
namespace MyApp.iOS.Renderers
{
    class HtmlLabelRenderer : ViewRenderer<HtmlLabel, UITextView>
    {
        private UITextView _htmlTextView = new UITextView();
        protected override void OnElementChanged(ElementChangedEventArgs<HtmlLabel> e)
        {
            base.OnElementChanged(e);

            if (Element?.Text == null) return;

            SetHtmlText(Element.Text);
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            if (string.Equals(e.PropertyName, "Text", StringComparison.CurrentCultureIgnoreCase))
            {
                SetHtmlText(((HtmlLabel)sender).Text);
                _htmlTextView.SetNeedsDisplay();
            }
            base.OnElementPropertyChanged(sender, e);
        }

        private void SetHtmlText(string text)
        {
            var attr = new NSAttributedStringDocumentAttributes {DocumentType = NSDocumentType.HTML};
            var nsError = new NSError();

            _htmlTextView.Editable = false;
            _htmlTextView.AttributedText = new NSAttributedString(text, attr, ref nsError);
            _htmlTextView.DataDetectorTypes = UIDataDetectorType.All;
            SetNativeControl(_htmlTextView);
        }
    }
}

更新:通过将OnElementChanged更改为:

,我得到了进一步的发展
    protected override void OnElementChanged(ElementChangedEventArgs<HtmlLabel> e)
    {
        base.OnElementChanged(e);

        if (e.OldElement != null || Element == null) return;

        SetHtmlText(e.NewElement.Text ?? string.Empty);
        SetNativeControl(_htmlTextView);
    }

现在,如果我在页面上有多个HtmlLabel,则显示除第一个之外的所有HtmlLabel。

3 个答案:

答案 0 :(得分:0)

尝试更改:

_htmlTextView.SetNeedsDisplay();

SetNeedsDisplay();

或,

(Control ?? NativeView).SetNeedsDisplay();

答案 1 :(得分:0)

您的自定义HtmlLabel类应该能够在Android和iOS上从同一个东西派生

namespace YourNameSpace
{
    public class HtmlLabel : Label
    {
    }
}

Android的渲染器看起来应该是这样的

[assembly: ExportRenderer(typeof(HtmlLabel), typeof(HtmlLabelRenderer))]
namespace YourNameSpace.Droid
{
    public class HtmlLabelRenderer : ViewRenderer<Label, TextView>
    {
        TextView _textView;

        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            if (Element == null)
                return;

            if(Control == null)
            {
                _textView = new TextView(Context);
                SetHtmlText(Element.Text);
                SetNativeControl(_textView);
            }
        }

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

            if (Element == null || Control == null)
                return;

            if (e.PropertyName == HtmlLabel.TextProperty.PropertyName)
            {
                SetHtmlText(Element.Text);
            }
        }

        private void SetHtmlText(string text)
        {

            if (Android.OS.Build.VERSION.SdkInt >= BuildVersionCodes.N)
            {
                _textView.TextFormatted = Html.FromHtml(text, Android.Text.FromHtmlOptions.ModeCompact);
            }
            else
            {
                _textView.TextFormatted = Html.FromHtml(text);
            }
        }
    }
}

在iOS上它应该看起来非常相似

[assembly: ExportRenderer(typeof(HtmlLabel), typeof(HtmlLabelRenderer))]
namespace YourNameSpace.iOS
{
    public class HtmlLabelRenderer : ViewRenderer<Label, UITextView>
    {
        UITextView _textView;

        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            if (Element == null)
                return;

            if(Control == null)
            {
                _textView = new UITextView();
                SetHtmlText(Element.Text);
                SetNativeControl(_textView);
            }
        }

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

            if (Element == null || Control == null)
                return;

            if (e.PropertyName == HtmlLabel.TextProperty.PropertyName)
            {
                SetHtmlText(Element.Text);
            }
        }

        private void SetHtmlText(string text)
        {
            var attr = new NSAttributedStringDocumentAttributes { DocumentType = NSDocumentType.HTML };
            var nsError = new NSError();

            _textView.Editable = false;
            _textView.AttributedText = new NSAttributedString(text, attr, ref nsError);
            _textView.DataDetectorTypes = UIDataDetectorType.All;
        }
    }
}

我在Android上测试了它并且它工作正常,当文本发生变化时调用OnElementPropertyChanged。但是,我没有在家里使用Mac来试用iOS渲染器,所以我只是假设它的功能几乎相同。

答案 2 :(得分:0)

Sharada Gururaj是对的,改编自源自编辑的iOS工作..但打破了Android。虽然这看起来很蛮力,但我使用条件编译来使它工作......

namespace MyApp.Renderers
{
#if __IOS__
    public class HtmlLabel : Editor
    {
    }
#else
    public class HtmlLabel : Label
    {
    }
#endif
}

这是android

[assembly: ExportRenderer(typeof(HtmlLabel), typeof(HtmlLabelRenderer))]

namespace MyApp.Droid.Renderers
{
    public class HtmlLabelRenderer : LabelRenderer
    {
        protected override void OnElementChanged(ElementChangedEventArgs<Label> e)
        {
            base.OnElementChanged(e);

            var view = (HtmlLabel)Element;
            if (view?.Text == null) return;

            SetHtmlText(view.Text);
        }

        protected override void OnElementPropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            base.OnElementPropertyChanged(sender, e);
            if (e.PropertyName == Label.TextProperty.PropertyName)
            {
                SetHtmlText(((HtmlLabel) sender).Text);
            }
        }

        private void SetHtmlText(string text)
        {
            var encodedText = (((int)Build.VERSION.SdkInt) >= 24) ? Html.FromHtml(text, FromHtmlOptions.ModeLegacy) :
#pragma warning disable 618
   // need this for backward compatability
   Html.FromHtml(text);
#pragma warning restore 618
            Control.MovementMethod = LinkMovementMethod.Instance;
            Control.SetText(encodedText, TextView.BufferType.Spannable);
        }
    }
}