2015-03-25

流式布局的原理和代码实现

Views: 26099 | 2 Comments

最简单的流式布局模型, 其实就是: 靠左, 靠右, 或者堆叠. 根据这个简单的理论, 可以用两个栈(Stack)数据结构, 一个表示靠左边的控件列表, 另一个表示靠右边的控件列表, 即可实现流式布局模型.

用伪代码表示如下:

// 视图控件
class View{
    private FlowLayouter layouter;
    
    // 当控件发生 frame 改变后, 调用此方法标记为需要重新布局
    void setNeedsLayout(){
        View view = this;
        while(view){
            view.markNeedsLayout();
            // 当控件需要重新布局时, 一般地, 它的父节点也需要重新布局
            view = view.parent;
        }
    }
    
    void layout(){
        for(View child in this.children){
            this.layouter.place(child);
        }
    }
}

// 流式布局管理器
class FlowLayouter{
    private Stack leftViews;
    private Stack rightViews;
    
    void place(View child){
        Position pos;
        child.layout(); // 子节点先进行布局
        while(!this.spaceFits(child)){
            if(child.floatLeft){
                View view = this.leftViews.pop();
                pos = view.pos;
                // 当被移除的节点比其它节点更高时, 继续移除
                while(pos.y > this.leftViews.last.y){
                    View view = this.leftViews.pop();
                    pos = view.pos;
                }
            }
            if(child.floatRight){
                View view = this.rightViews.pop();
                pos = view.pos;
                // 当被移除的节点比其它节点更高时, 继续移除
                while(pos.y > this.rightViews.last.y){
                    View view = this.rightViews.pop();
                    pos = view.pos;
                }
            }
        }
        
        // place child here
        child.pos = pos;
        
        if(child.floatLeft){
            this.leftViews.push(child.pos);
        }
        if(child.floatRight){
            this.rightViews.push(child.pos);
        }
    }
}

这段代码最重要的是两点:

1. 当某个控件发生改变时, 它需要重新布局. 同时, 它的父节点, 以及父节点的父节点, 一直到节点树的根节点, 都需要重新布局. 当然, 这是性能最差的方案, 优化的思路就是减少需要重新布局的节点的数量, 这需要发动每个人的聪明才智来想.

2. 用两个 Stack 来分别表示靠左的和靠右节点列表. 如果当前的空白空间不足以放下一个控件, 那么, 尝试从节点列表中移除一个节点, 这样, 这个布局区域就空出来了一些空间. 当然, 这个空间应该往下移, 不能和被移除的节点所占据的空间重叠. 因为流式布局的基本原理就是不重叠(除非通过特殊设定, 如负数的偏移量).

有了这个简单的流式布局模型, 你可以在所有最基本的绝对定位的 GUI 库上面实现功能强大的流式布局, 例如, iOS 的 UIKit 不支持流式布局, 你可以根据上面的代码扩展, 给 iOS 界面开发加上流式布局功能.

流式布局其实是非常有趣的一项功能, 它的模型很简洁, 但功能强大且应用广泛. GUI 界面的本质是树, 树是简洁而优美的, 而流式布局使用的数据结构是 Stack, 又是一种非常基础的数据结构.

说句题外话, 我已经实现了 iOS 系统上面的 UI 流式布局 - CocoaUI, 你可以试用下.

Related posts:

  1. JavaScript 类/函数继承最佳实践
  2. C# 版的 SimpleXML
  3. 生产者消费者模型中生产者的速度快于消费者时所产生的问题及其解决方法讨论
  4. C#封装log4net
  5. 港股实时行情系统设计
Posted by ideawu at 2015-03-25 14:33:48 Tags: ,

2 Responses to "流式布局的原理和代码实现"

Leave a Comment