简体   繁体   English

为什么自定义视图的子视图不会在棉花糖中渲染?

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

I'm developing a custom keyboard using Xamarin. 我正在使用Xamarin开发自定义键盘。

My keyboard view has an overridden OnDraw() for both the view container itself and it's child key views. 我的键盘视图的视图容器本身及其子键视图都有一个重写的OnDraw()。 I'm also using SetWillNotDraw(false) appropriately for each view. 我还为每个视图适当地使用SetWillNotDraw(false)。 It currently works beautifully in 5.0.1 on my Nexus 10 tablet. 目前,在我的Nexus 10平板电脑上的5.0.1版本中,它运行良好。

In Android 6.0.1, on a Nexus 6 and a Nexus 6P, the keyboard view correctly draws itself (just a background color). 在Android 6.0.1中,在Nexus 6和Nexus 6P上,键盘视图会正确绘制自身(只是背景颜色)。 The child key views however are never drawn, even if I iterate through the view hierarchy and force an invalidate on each one. 但是,即使我遍历视图层次结构并对每个视图强制无效,也不会绘制子键视图。 This seems to be specific to Marshmallow. 这似乎是棉花糖特有的。

I don't know if there's something new I need to account for in this version of Android or if I'm encountering a bug. 我不知道在此版本的Android中是否需要解决一些新问题,或者是否遇到错误。

Any help or suggestions are welcome. 欢迎任何帮助或建议。

Code: 码:

KeyboardView KeyboardView

KeyView 的KeyView

Some extra details to shed light on the original post: 一些额外的细节可以阐明原始帖子:

The three major files we use for keyboard rendering are KeyboardView.cs , KeyboardRowView.cs , and KeyView.cs . 我们用于键盘渲染的三个主要文件是KeyboardView.csKeyboardRowView.csKeyView.cs

KeyboardView (the container for the whole keyboard) KeyboardView (整个键盘的容器)

This has no trouble rendering. 渲染没有问题。 KeyboardView extends a LinearLayout and its OnDraw method runs, calling a Build() function to create what it needs (just a basic background which will "hold" the individual keys): KeyboardView扩展了LinearLayout并运行其OnDraw方法,并调用Build()函数来创建所需的内容(只是一个基本背景,它将“保留”各个键):

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();
    }

(...and Build() below...) (...以及下面的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++;
        }
    }

(As a friendly reminder, we're doing this because we need a custom keyboard which can only display certain keys/commands to our users.) (提醒您,我们这样做是因为我们需要一个自定义键盘,该键盘只能向用户显示某些键/命令。)

KeyboardRowView (the container for each row of keys) KeyboardRowView (每行按键的容器)

This also extends a LinearLayout , and also has its OnDraw method called: 这也扩展了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 (the class which loads and renders each individual key) KeyView (加载和呈现每个单独键的类)

KeyView extends View and View.IOnTouchListener . KeyView扩展了ViewView.IOnTouchListener KeyView's constructor is called, but its OnDraw method is never called/executed: 调用了KeyView的构造函数,但从未调用/执行过其OnDraw方法:

// 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;
    }

(...and OnDraw) (...和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);
        }
    }

I am confused. 我很困惑。 Both KeyboardView and KeyboardRowView have a SetWillNotDraw(false); KeyboardViewKeyboardRowView都具有SetWillNotDraw(false); function call in their constructor/initialization methods. 在其构造函数/初始化方法中调用函数。 KeyView also has the same function call, and successfully receives each key value that needs to be rendered. KeyView也具有相同的函数调用,并成功接收需要呈现的每个键值。 What I don't get is why it just...won't...draw...the...keyboard. 我不明白的是为什么它只是...不会...绘制...键盘。 (Argh.) When I spoke with the original poster about this, he told me that all the conditions have been met in order for the keyboard keys to be rendered. (Argh。)当我与原始海报商谈此事时,他告诉我所有条件都已得到满足,以便呈现键盘键。 I tried attaching breakpoints to see what was preventing KeyView's OnDraw from being called, but got caught up in repeated OnMeasure function calls (and there are a lot of keys that get rendered, so that got old quick ). 我尝试附加断点以查看阻止调用KeyView的OnDraw的原因,但是陷入了重复的OnMeasure函数调用中(并且渲染了很多键,因此很快就变老了)。

It's worth mentioning that we've tested it on the latest Nexus 6P smartphone (running stock Android 6.0 Marshmallow) and an old Motorola Droid 4 (with Marshmallow installed via CyanogenMod 13). 值得一提的是,我们已经在最新的Nexus 6P智能手机(运行时的Android 6.0棉花糖) 旧版的Motorola Droid 4(通过CyanogenMod 13安装了棉花糖)上进行了测试。 When we tried it using a Xamarin Android Player emulator (running Marshmallow), it actually worked ... my guess is that the emulator might be rendering the keyboard with no problem because the actual phones themselves are either 当我们使用Xamarin Android Player模拟器(运行棉花糖)进行尝试时,它实际上可以工作 ……我的猜测是,模拟器可能毫无问题地渲染了键盘,因为实际的手机本身就是

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

(b) potentially holding on to old code and we just haven't completely removed their old .apks (b)可能会保留旧代码,而我们还没有完全删除它们的旧.apks

(c) some other issue I haven't thought of (c)我没有想到的其他问题

Thank you for your time. 感谢您的时间。 If anyone can think of a possible direction to go in, it would be appreciated! 如果有人能想到一个可行的方向,将不胜感激!

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 为什么我不能强制我的一个视图在软件中渲染? - Why can't I force one of my Views to render in software? 我的应用程序不会出现在android.intent.action.VIEW的选择器对话框中,只有在棉花糖上才有mime类型的视频/ * - My app won't show up on the chooser dialog for android.intent.action.VIEW with mime type video/* only on Marshmallow 自定义View动画的“我的Runnable”将不会启动。 - My Runnable for a custom View animation won't start. 尝试在自定义视图的onTouchEvent中检测子视图 - Trying to detect child views in the onTouchEvent in a custom view 自定义视图中子视图的 ID 在 Android 中是相同的 - ID of the child views in a custom view are same in Android 自定义视图并更改子视图的大小 - Custom view and change the size of the child views 自定义布局中的视图不会显示 - Views within custom layout won't show 为什么我的setVisibility View无法正确显示? - Why won't my setVisibility View display correctly? 为什么我的应用程序不会膨胀导航视图类? - Why does my app won't inflate navigation view class? 我在自定义视图中做错了什么,导致它无法在可扩展列表视图中呈现(其他视图很好呈现)-Android - What did I do wrong in my custom view to cause it to not be rendered in my expandable list view (other views render fine) - Android
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM