IT技术博客大学习 共学习 共进步
全部 移动开发 后端 数据库 AI 算法 安全 DevOps 前端 设计 开发者

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

idea's blog 2015-03-26 13:32:57 累计浏览 2,231 次
本机暂存

   最简单的流式布局模型, 其实就是: 靠左, 靠右, 或者堆叠. 根据这个简单的理论, 可以用两个栈(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, 你可以试用下.

同分类推荐文章

  1. 对基本有序的序列排序算法 (2026-06-11 17:46:49)
  2. Four Levels Of Customer Understanding (2026-05-22 21:00:00)
  3. 除法的意义 (2026-04-12 20:52:17)

查看更多 算法 文章 →

建议继续学习

  1. 红黑树并没有我们想象的那么难(上) (累计阅读 21,494)
  2. 为什么算法这么难? (累计阅读 12,397)
  3. 浅谈MySQL索引背后的数据结构及算法 (累计阅读 11,902)
  4. 加州求职记 (累计阅读 11,561)
  5. 海量数据面试题举例 (累计阅读 11,114)
  6. 基于Redis构建系统的经验和教训 (累计阅读 10,521)
  7. 谷歌(Google)2011年校园招聘笔试题 (累计阅读 9,572)
  8. 浅谈redis数据库的键值设计 (累计阅读 9,353)
  9. 关于使用STL的红黑树map还是hashmap的问题 (累计阅读 8,873)
  10. 再谈“我是怎么招聘程序员的” (累计阅读 8,791)