Problem when rendering Android.Xamarin layout with custom text view in VS2019

57 Views Asked by At

I've created some custom elements such as a CustomTextView, so I can change at desire some visual styles such as fontsize, font family etc. This is the code for my CustomTextView:

using System;
using Android.Content;
using Android.Graphics;
using Android.Runtime;
using Android.Util;
using Android.Widget;
using myapp.Core;
using myapp.Models;

namespace myapp.Views.CustomControls
{
    public class TextViewCustom : TextView
    {
        private Color color;
        private Typeface customFont;
        private CUSTOM_FONTS_SIZE textSize;
        
        protected TextViewCustom(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer)
        {
        }

        public TextViewCustom(Context context) : base(context)
        {
            Initialize(context);
        }

        public TextViewCustom(Context context, IAttributeSet attrs) : base(context, attrs)
        {
            Initialize(context, attrs);
        }

        private void Initialize(Context context, IAttributeSet attrs = null)
        {
            color = AppearanceManager.ColorPrimary;
            customFont = AppearanceManager.Typeface;
            textSize = CUSTOM_FONTS_SIZE.SMALL;

            int textType = 0;
            int colorType = 0;
            if (attrs != null)
            {
                var array = context.ObtainStyledAttributes(attrs, Resource.Styleable.text_view_custom, 0, 0);
                colorType = array.GetInt(Resource.Styleable.text_view_custom_tvc_color, colorType);
                textType = array.GetInt(Resource.Styleable.text_view_custom_tvc_type, textType);
                array.Recycle();
            }
            
            switch (colorType)
            {
                case 1: color = AppearanceManager.ColorPrimary;
                    break;
                case 2: color = AppearanceManager.ColorSecundary;
                    break;
                default: colorType = 0;
                    break;
            }
            
            switch (textType)
            {
                case 1: textSize = CUSTOM_FONTS_SIZE.SMALL;
                    break;
                case 2: textSize = CUSTOM_FONTS_SIZE.MIDDLE;
                    break;
                case 3: textSize = CUSTOM_FONTS_SIZE.LARGE;
                    break;
                case 4: textSize = CUSTOM_FONTS_SIZE.TINY;
                    break;
            }
            
            if(colorType > 0) SetTextColor(color);
            SetTypeface(customFont, TypefaceStyle.Normal);
            SetTextSize(ComplexUnitType.Px, AppearanceManager.TypefaceSize(textSize));
        }
    }
}

, this

using System;
using Android.Graphics;
using myapp.Core;

namespace myapp.Models
{
    public enum CUSTOM_FONTS_STYLE { DEFAULT,  SEGOE_UI, BUBLE_JELLY_SHADOW, AUGUSTA,  VCR_OSD_MONO, SWEET_PURPLE}
    public enum CUSTOM_FONTS_SIZE { SMALL, MIDDLE, LARGE, TINY }

    public class AppearanceInfo
    {
        public Color? ColorPrimary { get; set; }
        public Color? ColorPrimaryDark  { get; set; }
        public Color? ColorPrimaryText  { get; set; }
        public Color? ColorSecundary { get; set; }
        public Color? ColorSecundaryDark { get; set; }
        public Color? ColorSecundaryText { get; set; }
        
        public CUSTOM_FONTS_STYLE CustomFont { get; set; }

        public void SetColorPrimaryFromHex(string hexColor)
        {
            if (hexColor.IndexOf("#", StringComparison.Ordinal) != 0) hexColor = $"#{hexColor}";
            if (!Utils.IsValidHexColor(hexColor)) return;
            ColorPrimary = Color.ParseColor(hexColor);
        }

        public void SetColorPrimaryDarkFromHex(string hexColor)
        {
            if (hexColor.IndexOf("#", StringComparison.Ordinal) != 0) hexColor = $"#{hexColor}";
            if (!Utils.IsValidHexColor(hexColor)) return;
            ColorPrimaryDark = Color.ParseColor(hexColor);
        }

        public void SetColorPrimaryTextFromHex(string hexColor)
        {
            if (hexColor.IndexOf("#", StringComparison.Ordinal) != 0) hexColor = $"#{hexColor}";
            if (!Utils.IsValidHexColor(hexColor)) return;
            ColorPrimaryText = Color.ParseColor(hexColor);
        }

        public void SetColorSecundaryFromHex(string hexColor)
        {
            if (hexColor.IndexOf("#", StringComparison.Ordinal) != 0) hexColor = $"#{hexColor}";
            if (!Utils.IsValidHexColor(hexColor)) return;
            ColorSecundary = Color.ParseColor(hexColor);
        }

        public void SetColorSecundaryDarkFromHex(string hexColor)
        {
            if (hexColor.IndexOf("#", StringComparison.Ordinal) != 0) hexColor = $"#{hexColor}";
            if (!Utils.IsValidHexColor(hexColor)) return;
            ColorSecundaryDark = Color.ParseColor(hexColor);
        }

        public void SetColorSecundaryTextFromHex(string hexColor)
        {
            if (hexColor.IndexOf("#", StringComparison.Ordinal) != 0) hexColor = $"#{hexColor}";
            if (!Utils.IsValidHexColor(hexColor)) return;
            ColorSecundaryText = Color.ParseColor(hexColor);
        }
    }
}

and finally this:

using Android.Graphics;
using myapp.Models;

namespace myapp.Core
{
    public enum APPEARANCES_TEMPORAL { DEFAULT, MEDIEVAL, GAMER, MODERN, CHILDREN, CUTE }
    
    public static class AppearanceManager
    {
        private static AppearanceInfo _customAppearance;

        public static Color ColorPrimary { get; private set; }
        public static Color ColorPrimaryDark  { get; private set; }
        public static Color ColorPrimaryText  { get; private set; }
        public static Color ColorSecundary { get; private set; }
        
        public static Color ColorSecundaryDark { get; private set; }
        
        public static Color ColorSecundaryText { get; private set; }

        public static Typeface Typeface
        {
            get
            {
                if (_customAppearance == null || _customAppearance.CustomFont == CUSTOM_FONTS_STYLE.DEFAULT) 
                    return Typeface.Default;
                
                switch (_customAppearance.CustomFont)
                {
                    case CUSTOM_FONTS_STYLE.SEGOE_UI:
                        return LocalResourceProvider.GetFont(Resource.Font.segoe_ui);
                    case CUSTOM_FONTS_STYLE.BUBLE_JELLY_SHADOW:
                        return LocalResourceProvider.GetFont(Resource.Font.buble_jelly_shadow);
                    case CUSTOM_FONTS_STYLE.VCR_OSD_MONO:
                        return LocalResourceProvider.GetFont(Resource.Font.vcr_osd_mono);
                    case CUSTOM_FONTS_STYLE.AUGUSTA:
                        return LocalResourceProvider.GetFont(Resource.Font.augusta);
                    case CUSTOM_FONTS_STYLE.SWEET_PURPLE:
                        return LocalResourceProvider.GetFont(Resource.Font.sweet_purple);
                    default:
                        return Typeface.Default;
                }
            }
        }

        public static float TypefaceSize(CUSTOM_FONTS_SIZE size)
        {
            float tinySize = LocalResourceProvider.GetDimension(Resource.Dimension.textTiny);
            float smallSize = LocalResourceProvider.GetDimension(Resource.Dimension.textSmall);
            float middleSize = LocalResourceProvider.GetDimension(Resource.Dimension.textMiddle);
            float largeSize = LocalResourceProvider.GetDimension(Resource.Dimension.textLarge);

            if (_customAppearance != null)
            {
                if (_customAppearance.CustomFont == CUSTOM_FONTS_STYLE.SWEET_PURPLE)
                {
                    tinySize = LocalResourceProvider.SpToPx(17);
                    smallSize = LocalResourceProvider.SpToPx(20);
                    middleSize = LocalResourceProvider.SpToPx(25);
                    largeSize = LocalResourceProvider.SpToPx(30);
                }
                //ADD MORE CUSTOM SIZES WHEN NECESSARY
            }
            
            if (size == CUSTOM_FONTS_SIZE.TINY) return tinySize;
            if (size == CUSTOM_FONTS_SIZE.SMALL) return smallSize;
            if (size == CUSTOM_FONTS_SIZE.MIDDLE) return middleSize;
            if (size == CUSTOM_FONTS_SIZE.LARGE) return largeSize;
            return smallSize;
        }

        public static void LoadDefaultAppearance()
        {
            ColorPrimary = LocalResourceProvider.GetColor(Resource.Color.primary);
            ColorPrimaryDark = LocalResourceProvider.GetColor(Resource.Color.primaryDark);
            ColorSecundary = LocalResourceProvider.GetColor(Resource.Color.secundary);
            ColorSecundaryDark = LocalResourceProvider.GetColor(Resource.Color.secundaryDark);
            ColorPrimaryText = Color.White;
            ColorSecundaryText = Color.White;
            _customAppearance = null;
        }
        
        public static void LoadCustomAppearance(AppearanceInfo customAppearance)
        {
            LoadDefaultAppearance();
            if (customAppearance == null) return;

            _customAppearance = customAppearance;

            ColorPrimary = _customAppearance.ColorPrimary ?? ColorPrimary;
            ColorPrimaryDark = _customAppearance.ColorPrimaryDark ?? ColorPrimaryDark;
            ColorPrimaryText = _customAppearance.ColorPrimaryText ?? ColorPrimaryText;
            ColorSecundary = _customAppearance.ColorSecundary ?? ColorSecundary;
            ColorSecundaryDark = _customAppearance.ColorSecundaryDark ?? ColorSecundaryDark;
            ColorSecundaryText = _customAppearance.ColorSecundaryText ?? ColorSecundaryText;
        }

        public static void SetAppearanceTemporal(APPEARANCES_TEMPORAL appearanceType)
        {
            switch (appearanceType)
            {
                case APPEARANCES_TEMPORAL.MEDIEVAL:
                    AppearanceInfo MedievalAppearance = new AppearanceInfo();
                    MedievalAppearance.SetColorPrimaryFromHex("5d4037");
                    MedievalAppearance.SetColorPrimaryDarkFromHex("3e2723");
                    MedievalAppearance.SetColorPrimaryTextFromHex("ffd54f");
                    MedievalAppearance.SetColorSecundaryFromHex("607d8b");
                    MedievalAppearance.SetColorSecundaryDarkFromHex("455a64");
                    MedievalAppearance.CustomFont = CUSTOM_FONTS_STYLE.AUGUSTA;
                    LoadCustomAppearance(MedievalAppearance);
                    break;
                case APPEARANCES_TEMPORAL.GAMER:
                    AppearanceInfo GamerAppearance = new AppearanceInfo();
                    GamerAppearance.SetColorPrimaryFromHex("212121");
                    GamerAppearance.SetColorPrimaryDarkFromHex("212121");
                    GamerAppearance.SetColorPrimaryTextFromHex("ffff00");
                    GamerAppearance.SetColorSecundaryFromHex("212121");
                    GamerAppearance.SetColorSecundaryDarkFromHex("212121");
                    GamerAppearance.CustomFont = CUSTOM_FONTS_STYLE.VCR_OSD_MONO;
                    LoadCustomAppearance(GamerAppearance);
                    break;
                case APPEARANCES_TEMPORAL.MODERN:
                    AppearanceInfo ModernAppearance = new AppearanceInfo();
                    ModernAppearance.SetColorPrimaryFromHex("78909c");
                    ModernAppearance.SetColorPrimaryDarkFromHex("546e7a");
                    ModernAppearance.SetColorSecundaryFromHex("00796b");
                    ModernAppearance.SetColorSecundaryDarkFromHex("004d40");
                    ModernAppearance.CustomFont = CUSTOM_FONTS_STYLE.SEGOE_UI;
                    LoadCustomAppearance(ModernAppearance);
                    break;
                case APPEARANCES_TEMPORAL.CHILDREN:
                    AppearanceInfo ChildrenAppearance = new AppearanceInfo();
                    ChildrenAppearance.SetColorPrimaryFromHex("2196f3");
                    ChildrenAppearance.SetColorPrimaryDarkFromHex("1976d2");
                    ChildrenAppearance.SetColorPrimaryTextFromHex("ffea00");
                    ChildrenAppearance.SetColorSecundaryFromHex("4caf50");
                    ChildrenAppearance.SetColorSecundaryDarkFromHex("388e3c");
                    ChildrenAppearance.CustomFont = CUSTOM_FONTS_STYLE.BUBLE_JELLY_SHADOW;
                    LoadCustomAppearance(ChildrenAppearance);
                    break;
                case APPEARANCES_TEMPORAL.CUTE:
                    AppearanceInfo CuteAppearance = new AppearanceInfo();
                    CuteAppearance.SetColorPrimaryFromHex("c2185b");
                    CuteAppearance.SetColorPrimaryDarkFromHex("880e4f");
                    CuteAppearance.SetColorSecundaryFromHex("ba68c8");
                    CuteAppearance.SetColorSecundaryDarkFromHex("9c27b0");
                    CuteAppearance.CustomFont = CUSTOM_FONTS_STYLE.SWEET_PURPLE;
                    LoadCustomAppearance(CuteAppearance);
                    break;
                default:
                    LoadDefaultAppearance();
                    break;
            }
        }
    }
}

But when I use one of this CustomTextView and want to see it rendering the layout at VS2019, I',m prompted this error:

Error when rendering layout with CustomTextView

Any ideas about it?

Regards

1

There are 1 best solutions below

2
Cheesebaron On

The error says it all. You have a NullReferenceException in your code. Set a break-point and narrow it down to where it happens.

But it looks like it happens in AppearanceManager.TypefaceSize. It is unclear to me what LocalResourceProvider is, but it likely requires a context to convert the dimensions, which throws a NullReferenceException.

You have the debugger, set some appropriate break points and debug your code. The relevant failing code is not in your question.