[分享]线性布局的改进

[size=4]一、需求[/size]
很多时候,希望Layout的的部分子控件大小固定,而另一部分子控件大小则随着Layout的大小而变化。例如下图中,当整个控件宽度变化时,A,D宽度固定,而B, C则填充满剩余的宽度。

B, C的宽度和父控件的宽度的比例是不固定的,所以不能用百分比来实现。

[size=4]二、 原理[/size]
没有耐心的童鞋,可以直接跳过该部分。

在cocostudio不支持类似功能的情况下,可以用如下方法曲线解决:
[list=1][li]1. 使用线性布局,以线性横向为例,设总宽度 W[/li][li]2. 为每一个子控件,指定一个权重值[/li][/list][blockquote][list][li]a) 宽度固定的控件,权重值为0,例如上例中的A,D。设这些控件的总宽度为w1,那么剩余控件的总宽度则为w2=W-w1。[/li][li]b) 宽度变化的控件,权重值>0,表示该控件宽度在w2中所占的比例。上例中B的权重为1, C的权重为2,所以在B,C会随着父控件变化宽度,并且C始终是B的两倍[/li][/list][/blockquote]

[size=4]三、 编辑器[/size]
[size=2]因为编辑器中无法编辑该权重值,所以我们可以利用TAG来实现:
[/size][list=1][li][size=2]  1. 当TAG>=0时,表示权重值为0[/size][/li][li][size=2]  2. 当TAG<0时,表示权重值为 -TAG。上面的例子中,B为-1,C为-2(其实也可以是-100,-200,只要保持比例即可)[/size][/li][/list][size=2]这只是我目前自己所做的约定,因为正常情况下,我不会用到<0的TAG。大家也可以根据自己的需求修改。

[size=4]四、 JS代码
[/size]
[size=4][size=2][code]
cp = cp || {};
/**

  • @param name {string} widget file name
  • @param size {cc.size} new size of loaded widget
    /
    cp.loadWidget = function (name, size/
    =undefined*/) {
        var widget = ccs.GUIReader.getInstance().widgetFromJsonFile(name);

    // substitute label texts with localized strings
    //_fixLabels(widget);
   
    if ( size != undefined ) {
        var ws = widget.getSize();
        if ( size.width > 0 ) {     // use new width
            ws.width = size.width;
        }
        if ( size.height > 0 ) {    // use new height
            ws.height = size.height;
        }
        widget.setSize(ws);
    }

    cp.fixWidgetLayout(widget);
    return widget;
};

/**

  • Recalculate widget size if its tag < 0 and is inside LINEAR layout.
    */
    cp.fixWidgetLayout = function (widget) {
        var children = widget.getChildren();

    if( widget instanceof ccui.Layout ) {
        var lt = widget.getLayoutType();
        var key = null;
       
        if (lt == ccui.Layout.LINEAR_HORIZONTAL) { // calculate width
            key = "width";          
        } else if (lt == ccui.Layout.LINEAR_VERTICAL) {  // calculate height
            key = "height";
        }

        if (key != null) {
            cp._doFixWidgetLayout(widget, key);
        }
    }

    // recursively fix children
    for (var i = 0; i < children.length; i++) {       
        cp.fixWidgetLayout(children);
    }
};

/**

  • @param key{string} should be either "width" or "height"
    */
    cp._doFixWidgetLayout = function (widget, key) {
        var children = widget.getChildren();

    var fixedSize = 0;   // total size of all children whose tag>=0
    var proportions = 0; // sum of proportions all children
    for (var i = 0; i < children.length; i++) {
        var child = children;
        var tag = child.getTag();
        if (tag < 0) {
            proportions += -tag;
        } else {
            fixedSize += child.getSize()[key];
        }
    }
    //cp.log(">>>proportions:", proportions, " fixedSize:", fixedSize);
    if (proportions > 0) {
        var size = widget.getSize();
        // size per unit
        var unit = (size[key] - fixedSize) / proportions;

        for (var i = 0; i < children.length; i++) {
            var child = children;
            var tag = child.getTag();
            if (tag < 0) {
                var cs = child.getSize();
                cs[key] = -unit * tag;  // calc new size with proportion & unit               
                child.setSize(cs);
            }
        }
    }
}
[/code][/size][/size][size=4]
[/size]

[size=4]五、 改进
[size=2]完善的方案,应该还包含以下功能:
[/size][/size]
[/size][list][li][size=2][size=4][size=2]1. 可以设定每个可变子控件的最小/最大宽度(高度)[/size][/size][/size][/li][li][size=2][size=4][size=2]2. 可以设定子控件的高度(宽度)是否随着父控件的高度(宽度)而变化(其实只要cocstudio的百分比功能可以针对x,y分别设置就行)[/size][/size][/size][/li][li][size=2][size=4][size=2]3. 根据子控件的参数,来决定父控件的尺寸。如果所有子控件的最小尺寸之和大于父控件,增大父控件的尺寸至最小值。[/size][/size][/size][/li][/list][i]
[/b]

感谢分享,我们会考虑跟现有的布局整合。