为什么我的自定义视图的子视图不会在 Marshmallow 中呈现?

Why won't the child views of my custom view render in Marshmallow?

我正在使用 Xamarin 开发自定义键盘。

我的键盘视图为视图容器本身及其子键视图覆盖了 OnDraw()。我还为每个视图适当地使用了 SetWillNotDraw(false)。它目前在我的 Nexus 10 平板电脑上的 5.0.1 中运行良好。

在 Android 6.0.1 中,在 Nexus 6 和 Nexus 6P 上,键盘视图会正确绘制自身(只是一种背景颜色)。然而,子键视图永远不会被绘制,即使我遍历视图层次结构并强制使每个视图无效。这似乎是 Marshmallow 特有的。

我不知道在这个版本 Android 中是否有我需要考虑的新内容,或者我是否遇到了错误。

欢迎任何帮助或建议。

代码:

KeyboardView

KeyView

一些额外的细节来阐明原作 post:

我们用于键盘渲染的三个主要文件是KeyboardView.csKeyboardRowView.csKeyView.cs.

KeyboardView(整个键盘的容器)

渲染没有问题。 KeyboardView 扩展了一个 LinearLayout 并且它的 OnDraw 方法运行,调用一个 Build() 函数来创建它需要的东西(只是一个基本的背景,它将 "hold" 各个键):

protected override void OnDraw(Canvas canvas)
    {
        Build(); 

        base.OnDraw(canvas);

        // background
        Paint bg = new Paint(PaintFlags.AntiAlias);
        bg.Color = BG; // light blue
        canvas.DrawRect(0, 0, MeasuredWidth, Height, bg);

        InvalidateKeys();
    }

(...和下​​面的 Build()...)

public void Build()
    {
        // only build once
        if (keyLayout != null)
            return;

        // clear out children
        RemoveAllViews();

        // define sizes of stuff
        if (isPortrait)
        {
            keyMargin = (int)(MeasuredWidth * .01f);
        }
        else
        {
            keyMargin = (int)(MeasuredHeight * .01f);
        }

        keyWidth = (MeasuredWidth - (keyMargin * 2)) / keyboard.MaxCols;
        keyHeight = (MeasuredHeight - (keyMargin * 2)) / keyboard.Rows.Count;

        // set general padding around keyboardview
        SetPadding(keyMargin, keyMargin, keyMargin, keyMargin);

        // build KeyLayout from the keyboard object
        keyLayout = new List<List<KeyView>>();
        int idx = 0;
        foreach (List<Key> row in keyboard.Rows)
        {
            keyLayout.Add(new List<KeyView>());

            // create and add new KeyboardRowView
            KeyboardRowView krv = new KeyboardRowView(Context, this, idx);
            AddView(krv);

            // figure out if we need a margin offset for this row
            int extraMargin = 0;
            int numCols = CountRowCols(row);
            if (numCols < keyboard.MaxCols)
            {
                // measure full width of the button container and the total row margin
                int rowWidth = (int)(numCols * keyWidth);
                int rowMargin = MeasuredWidth - (keyMargin * 2) - rowWidth;

                // add the offset
                extraMargin = rowMargin / 2;
            }

            // build keys and add them to keyLayout and KeyboardRowView
            int idx2 = 0;
            foreach (Key key in row)
            {
                int leftMargin = idx2 == 0 ? extraMargin : 0;
                KeyView kv = new KeyView(Context, this, key, leftMargin);
                keyLayout[idx].Add(kv);
                krv.AddView(kv);

                idx2++;
            }

            idx++;
        }
    }

(友情提醒,我们这样做是因为我们需要一个只能向我们的用户显示某些 keys/commands 的自定义键盘。)

KeyboardRowView(每行键的容器)

这也扩展了 LinearLayout,并且还调用了其 OnDraw 方法:

protected override void OnDraw(Canvas canvas)
    {
        base.OnDraw(canvas);
        Paint paint = new Paint();
        paint.SetARGB(255, 0, 0, 0);
        paint.SetStyle(Paint.Style.Stroke);
        paint.StrokeWidth = 3;
        canvas.DrawRGB(255, 255, 255);
        canvas.DrawRect(0, 0, 100, 100, paint);
    }

KeyView(加载和呈现每个单独键的 class)

KeyView 扩展了 ViewView.IOnTouchListener。调用了 KeyView 的构造函数,但它的 OnDraw 方法永远不会 called/executed:

// key views are always dynamically created
    public KeyView(Context ctx, KeyboardView parent, Key k, int leftMargin)
        : base(ctx)
    {
        // make sure the key will draw
        SetWillNotDraw(false);

        keyboard = parent;
        key = k;
        isDown = false;

        // check for an overridden span to adjust width, if needed
        int span = string.IsNullOrEmpty(key.Span) ? 1 : Convert.ToInt32(key.Span);
        int keyWidth = keyboard.keyWidth + ((span - 1) * keyboard.keyWidth);

        width = keyWidth;
        height = keyboard.keyHeight;

        // set margin
        var parameters = new LinearLayout.LayoutParams(
            LinearLayout.LayoutParams.WrapContent,
            LinearLayout.LayoutParams.MatchParent
        );
        parameters.LeftMargin = leftMargin;
        LayoutParameters = parameters;

        // set touch listener
        SetOnTouchListener(this);

        // enable haptic feedback for button presses
        HapticFeedbackEnabled = true;
    }

(...和 ​​OnDraw)

protected override void OnDraw(Canvas canvas)
    {
        base.OnDraw(canvas);

        KeyState primary = key.Primary;
        KeyState secondary = key.Secondary;

        if (keyboard.swapped)
        {
            primary = key.Secondary != null ? key.Secondary : key.Primary;
            secondary = key.Secondary != null ? key.Primary : null;
        }

        if (keyboard.shifted)
        {
            if (primary.Shift != null)
                primary = primary.Shift;

            if (secondary != null && secondary.Shift != null)
                secondary = secondary.Shift;
        }

        // figure out what color the key is supposed to be
        Paint bg = new Paint(PaintFlags.AntiAlias);
        bg.Color = GetKeyBgColor(key.Style);
        if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
            canvas.DrawRoundRect(keyboard.keyMargin, keyboard.keyMargin, width - keyboard.keyMargin, height - keyboard.keyMargin, keyboard.keyMargin, keyboard.keyMargin, bg);
        else
            canvas.DrawRoundRect(new RectF(keyboard.keyMargin, keyboard.keyMargin, width - keyboard.keyMargin, height - keyboard.keyMargin), keyboard.keyMargin, keyboard.keyMargin, bg);

        // draw primary key state
        Paint fg = new Paint(PaintFlags.AntiAlias);
        fg.TextSize = height * .5f;
        fg.Color = GetKeyFgColor(key.Style);
        string character = string.IsNullOrEmpty(primary.Character) ? "#" : primary.Character;
        int charWidth = Convert.ToInt32(fg.MeasureText(character));
        int charX = (width - charWidth) / 2;
        canvas.DrawText(character, charX, (height * .7f), fg);

        // draw secondary key state
        if (secondary != null)
        {
            fg.TextSize = height * .25f;
            fg.Color = GetKeyFgColor(key.Style, true);
            character = string.IsNullOrEmpty(secondary.Character) ? "#" : secondary.Character;
            charWidth = Convert.ToInt32(fg.MeasureText(character));
            charX = width - charWidth - (keyboard.keyMargin * 2);
            canvas.DrawText(character, charX, (height * .35f), fg);
        }
    }

我很困惑。 KeyboardViewKeyboardRowView 在它们的 constructor/initialization 方法中都有一个 SetWillNotDraw(false); 函数调用。 KeyView也有同样的函数调用,成功接收到每一个需要渲染的键值。我不明白的是为什么它只是……不会……画……键盘。 (啊。)当我与最初的 post er 谈论此事时,他告诉我所有条件都已满足才能呈现键盘按键。我尝试附加断点以查看是什么阻止了 KeyView 的 OnDraw 被调用,但在重复的 OnMeasure 函数调用中陷入困境(并且有很多键被渲染,所以它变旧了 快速).

值得一提的是,我们已经在最新的 Nexus 6P 智能手机(运行 stock Android 6.0 Marshmallow) 旧的摩托罗拉 Droid 上对其进行了测试4(通过 CyanogenMod 13 安装 Marshmallow)。当我们使用 Xamarin Android 播放器模拟器 (运行 Marshmallow) 进行尝试时,它实际上 有效 ...我猜模拟器可能正在渲染键盘没问题,因为实际手机本身是

(a) 以某种方式限制访问

(b) 可能会保留旧代码,我们只是还没有完全删除他们的旧 .apks

(c) 其他一些我没有想到的问题

感谢您的宝贵时间。如果有人能想到一个可能的方向,将不胜感激!

已通过 Android 文档建议的自上而下方法为每个自定义视图正确实施 OnMeasure 和 OnLayout 进行修复。