v2.1 ToggleContainer 中 toggle 调用 check() 方法不能设置选中状态的问题

onEnable () {
let container = this.node.getChildByName(‘toggleContainer’);
container.children[1].getComponent(cc.Toggle).check();
}

如上代码,在onEnable中调用check()方法,由于这个时候还没有执行toggle本身的onEnable方法,它的enabledInHierarchy值为false,所以不会执行设置整组toggle选中状态的方法。目前想到的方案是手动设置所有toggle的选中状态,或者延迟一帧调用check()方法,还有没有更好的解决方案?

有没有大佬解答下

为什么要在container的onEnable里面去设置toggle的check?为什么不放在start里面?如果你是想这个container被激活时就还原选项为默认值的话,你可以写一个方法来控制啊,而不是重写onEnable方法,或者是在onEnable中加上schdule。
你可以说下你的需求,可能会有更好的办法

不是container的onEnable,是一个界面节点的脚本的onEnable。我要打开这个界面的时候(active = true),根据参数设置默认选中哪个toggle。不放在start里面是因为它只会执行一次(界面不销毁,只设置隐藏显示)。如果有个方法是在组件全部激活之后触发的就好了。

那就是时序逻辑以及组件拆分的问题啊,你的逻辑应该是在这个container的onEnable当中去,判断数据的值为多少,比如为1就是第一个toggle被check了,为2就是第二个toggle。这样你的container不用管数据从哪来的,只需要根据数据在active的时候自动选择设定的默认选项就行。至于这个数据在你打开这个界面container的地方,也就是让这个container的active = true前,对这个container的数据进行设置即可

这样我还需要在container上挂一个脚本,然后重写onEnable,写我的逻辑代码。 打开界面时我不止设置toggle选中状态,还要设置其他信息(字体、图片等),如果我这个界面的其他控件也有类似的问题,岂不是每个控件上都要挂一个脚本去处理? 这样不合理吧。

额,那你这样整个onEnale不就会随着界面的复杂度变得越来越大,越来越难维护?我前面说的就是通过数据去降低两个模块之间的耦合,这样的话如果你的设置信息有变化,只需要改动传入的数据即可,如果规则改变,就改动container那边的代码即可,这样就很清晰啊。这样就可以隔离变化,控制代码的复杂度。

就是我在界面的onEnable里只设置数据,不主动调用子控件的方法,在子控件的onEnable里根据数据设置自己的属性。这样是很清晰,但是代价是要挂很多的脚本。我有几十个界面,每个界面有几十个控件,每个控件都挂自己的脚本要疯掉了。如果只是个别控件(比如这个container)这么处理倒是可以。 如果所有的组件在它的enabledInHierarchy值为false的时候都不能设置其属性(目前只发现toggle不能,label等组件依然可以设置),那就不能在onEnable里设置子节点的属性了,是不是官方本身就不建议在onEnable里去设置界面属性?

惨了, 我跟楼主是差不多的需求, 差不多的做法. 不过我没有需要针对inactive的控件指定状态.
在同一个UI脚本在OnEnable设置UI内的控件状态(管它有没有active)哪边不对啦?
为什么代码怎么写还需要一个外人来规定? 在控件上挂脚本就好维护? 我可不这么想.

老话一句, 写引擎的归写引擎, 写游戏的归写游戏. 你不干我的事怎么知道我怎么做会更好呢?

不好意思,之前写过7年的端/手游,写游戏的干哪些事情我还是知道一些。

一、这个并不是官方强制要求或者倡导这样做,这只是我个人的建议。
楼主的方法也不是不能解决问题,只是我凭着个人的习惯以及之前工作的游戏开发经验让我觉得这样做不太好。你完全可以按照自己的想法去实现。
如果你觉得这样做你方便维护,对你来说有好处,那就这样做。毕竟好游戏也不是好代码决定的(:joy:,看看太吾绘卷)

我也是看这个帖子一直没人回复,所以分享一下自己的思路以及做法。可能会有其他大佬有不同的做法,更好的做法,但是不分享出来,对楼主没有任何帮助。

二、对于好不好维护,我的看法是,虽然都写在一个脚本里面也可以,但有可能回带来整个脚本变得复杂,复杂的代码在后期的功能删减修改上,经常会无意识的或者不经意间一点点的增加代码的耦合度。导致后期bug难查,功能难以复用,扩展性也变差,如果有相似功能的需求,就容易变成代码的Ctrl+C和Ctrl+V。
将代码进行拆分,一个是遵循面向对象思想中的单一职责原则,另一方面也是想尽量使用组合去完成功能。从而代码的复用性增强,清晰干净,为后期扩展留有余地。比如后期如果有新的活动,要做新的界面和功能,很多东西都可以复用,而且这些事情交给策划就可以,如果界面的拼接,选择什么样的弹出效果,使用哪套字体和数字等等,程序只需要提供一个规则的算法就可以。

我并不是一定要你每个ui都挂一个脚本,你也完全可以将这些设置字体、大小、位置等等的代码放在界面父节点的里面。我个人推荐是复杂的字节点抽成模块,单一简单的字节点没必要挂脚本,父节点脚本中处理就行。
你完全可以在父节点中写一个方法或者利用notify来做这些事情,然后使用时通过类似这样的代码去让ui界面显示,同时更换选中状态

    properties: {
        selectedIndex : {
            default : 0,
            notify () {
                this.node.children[this.selectedIndex].check();
            }
        }
    },

//使用时
parentNode.active = true;
parentNode. selectedIndex = 1;

或者

    init (style) {
        if (style.fnt !== undefined) {
            this._mylabel.font = style.fnt;
        }
        if (style.selectedIndex !== undefined) {
            this._myToggleContainer.children[style.selectedIndex].check();
        }
        ...
    }
    //使用时
    parentNode.active = true;
    parentNode.init({selectedIndex : 1});

另外并不是不建议去修改onEnable,只是目前你的这个方案,如果放在onEnable里面,很容易写死,而且如果某两个component的onEnable中还有交互,还要去注意各个component组件的enable时序。越到后面头越疼

2赞

首先感谢大佬的耐心解答。 并不是界面所有代码都在onEnable里写,onEnable只是作为界面的打开后的入口,然后调用封装好的各个方法,有些复杂的或者共用的节点封装成了模块,有自己的脚本,所以维护起来也还好。将onEnable作为入口是因为界面显示时自动触发,对于多界面的管理比较方便。但是这次ToggleContainer遇到的问题让我感觉到将onEnable作为界面处理的入口并不好,因为这个时候子节点的组件还不是激活状态。[quote=“BigBear, post:10, topic:72932”]
//使用时
parentNode.active = true;
parentNode.init({selectedIndex : 1});
[/quote]
就像你给出的建议,也是在active后,再赋值或调用一个方法,我现在的做法也差不多,是每个界面里都写一个open方法,将原本在onEnable里处理的代码都挪到open里,并继承同一个父类BaseLayer:
layerNode.active = true;
layerNode.getComponent(BaseLayer).open();
最后也提醒同样在onEnable里处理界面信息的亲们注意下类似的问题。

如果不用场景而是用节点或预制来管理界面,在onEnable里设置UI属性应该很常见,但保不齐哪个控件对enabledInHierarchy属性有依赖,从而导致不生效的问题,所以保险起见建议再写个接口,在active后调用。

我遇到的问题可能不一样,是在子物体已经active之后 我去设置子物体的Toggle组件里的isChecked 为true 它是不会选中的,但是直接调用check( ) 方法的话 是可以的 这两者有什么区别吗