【问题标题】:building small GUI engine: visible vs. addChild/removeChild构建小型 GUI 引擎:可见与 addChild/removeChild
【发布时间】:2012-04-19 16:54:04
【问题描述】:

目前,我正在试验一个非常简单的 GUI 绘图……“引擎”(我猜你可以这么称呼它)。要点:

  1. 有一个 FrontController 被用户请求击中;每个请求都有一个uid
  2. 每个 uid(读作“页面”)都有其上存在的组件(“模块”)的声明
  3. 组件是 Sprite 子类,本质上是独一无二的

当然,我需要一种隐藏/显示这些精灵的方法。目前,我拥有它就像 Flex 默认拥有它一样 - 方式是“如果我们在一个 comp 可见的地方,创建它、缓存它并在每次它再次可见时重用它”。

问题是 - 通过addChild/removeChild 或切换visible,哪种方式更合适、更有效地隐藏和显示。

我的看法是这样的:

  • visible 又快又脏(在第一次测试中)
  • visible 不会创建像 Event.ADDEDEvent.REMOVED 这样的冒泡事件链
  • 不可见的组件不会获得鼠标事件

所以removeChild 将是我确定时调用的东西,该组件将不再需要在屏幕上(或者缓存太大,例如)

stackoverflow'ers / AS3-crazy 的人是怎么想的?

更新: 这是good read(忘了谷歌)。

我会坚持visible;它似乎更适合我的任务; Adobe 的手册“优化 FLASH 平台的性能”,第 15 页。 69给了我更多的信心。

这是我为感兴趣的人测试的代码 sn-p:

package 
{
import flash.display.Sprite;
import flash.display.Stage;
import flash.events.Event;
import flash.events.KeyboardEvent;
import flash.ui.Keyboard;
import flash.utils.getTimer;

/**
 * Simple benchmark to test alternatives for hiding and showing
 * DisplayObject.
 * 
 * Use:
 * <code>
 * new DisplayBM(stage);
 * </code>
 * 
 * Hit:
 * - "1" to addChild (note that hitting it 2 times is expensive; i think
 * this is because the player has to check whether or not the comp is
 * used elsewhere)
 * - "q" to removeChild (2 times in a row will throw an exception) 
 * - "2" to set visible to true
 * - "w" to set visible to false
 * 
 * @author Vasi Grigorash
 */    
public class DisplayBM{
    public function DisplayBM(stage:Stage){
        super();

        var insts:uint = 5000;
        var v:Vector.<Sprite> = new Vector.<Sprite>(insts);
        var i:Number = v.length, s:Sprite
        while (i--){
            s = new Sprite;
            s.graphics.beginFill(Math.random() * 0xFFFFFF);
            s.graphics.drawRect(
                Math.random() * stage.stageWidth, 
                Math.random() * stage.stageHeight,
                10, 
                10
            );
            s.graphics.endFill();
            v[i] = s;
        }

        var store:Object = {};
        store[Event.ADDED] = null;
        store[Event.REMOVED] = null;
        var count:Function = function(e:Event):void{
            store[e.type]++;
        }
        var keydown:Function = function (e:KeyboardEvent):void{
            var key:String
            //clear event counts from last run
            for (key in store){
                store[key] = 0;
            }

            stage.addEventListener(Event.ADDED, count);
            stage.addEventListener(Event.REMOVED, count);

            var s0:uint = getTimer(), op:String;
            var i:Number = v.length;
            if (e.keyCode === Keyboard.NUMBER_1){
                op = 'addChild';
                while (i--){
                    stage.addChild(v[i]);
                }
            }
            if (e.keyCode === Keyboard.Q){
                op = 'removeChild';
                while (i--){
                    stage.removeChild(v[i]);
                }
            }
            if (e.keyCode === Keyboard.NUMBER_2){
                op = 'visibile';
                while (i--){
                    v[i].visible = true;
                }
            }
            if (e.keyCode === Keyboard.W){
                op = 'invisibile';
                while (i--){
                    v[i].visible = false;
                }
            }
            if (op){
                //format events
                var events:Array = [];
                for (key in store){
                    events.push(key + ' : ' + store[key])
                }

                trace(op + ' took ' + (getTimer() - s0) + ' ' + events.join(','));
            }

            stage.removeEventListener(Event.ADDED, count);
            stage.removeEventListener(Event.REMOVED, count);
        }

        //autodispatch
        stage.addEventListener(KeyboardEvent.KEY_DOWN, keydown);
    }
}
}

【问题讨论】:

  • 如果我需要隐藏/显示的内容会占用大量资源,通常我会在设置其可见性时删除Child/addChild。如果它是较小的或不太复杂的图形或文本字段,我将设置它的可见性或 alpha。这完全取决于您需要隐藏/显示什么
  • @Ronnie removeChild 仅从显示列表中删除项目 - 仅此而已。
  • 对不起,我也将它设置为 null 并在需要时重新创建
  • 我之前从未真正考虑过 removeChild 与可见的区别,但我想到了一件事。如果它仍然在舞台上 IE visible=false 那么它可能仍然在获取 events/enterFrame 不确定它并且没有经过测试。可能值得创建一个测试来查看 enterframe 是否仍然被触发(我认为不会)并查看是否触发了任何阶段事件。
  • @Ronnie 如果有需要大量资源的东西 - 不要触摸它的显示列表并重新创建它是有意义的 O-)。目前,我发现创建可视化组件是所有操作中最繁重的操作(尽管组合仍然非常简单)。 @The_asMan visible 不影响 stage。因为,stage 仍然存在,我假设所有与舞台相关的听众都会被解雇。这就是为什么除了设置visible 之外,我还在考虑一个(取消)激活特定组件的功能。

标签: actionscript-3 flash user-interface


【解决方案1】:

以下是 Moock 关于该主题的一些硬数据: http://www.developria.com/2008/11/visible-false-versus-removechi.html

                  Children on the                     Single-frame 
                  Display List    .visible   .alpha   Elapsed Time (ms)
No Children       0               --         --       4
Non-visible       1000            false      1        4
Zero Alpha        1000            true       0        85
Fully Visible     1000            true       1        1498
90% Transparent   1000            true       .1       1997

【讨论】:

    【解决方案2】:

    Visible 对我来说更有意义(因为移除一个孩子就意味着一个终结),并且是我在自己的项目中显示/隐藏时倾向于使用的。

    我还假设 addChild 的性能稍差,但我没有做过任何测试。

    编辑:我刚刚看到这篇 Adob​​e 文章 http://help.adobe.com/en_US/as3/mobile/WS5d37564e2b3bb78e5247b9e212ea639b4d7-8000.html,它指定在使用 GPU 渲染模式时,仅设置 visible = false 会影响性能,因为绘制重叠对象是有成本的(即使它们不可见) .相反,建议完全移除孩子:

    尽可能避免过度绘制。过度绘制是分层多个 图形元素,使它们彼此模糊。使用软件 渲染器,每个像素只绘制一次。因此,对于软件 渲染,应用程序不会招致性能损失,无论 有多少图形元素在该像素处相互覆盖 地点。相比之下,硬件渲染器为每个像素绘制每个像素 元素是否其他元素遮盖了该区域。如果两个 矩形相互重叠,硬件渲染器绘制 软件渲染器绘制区域时重叠区域两次 只有一次。

    因此,在使用软件渲染器的桌面上,您 通常不会注意到透支对性能的影响。然而, 许多重叠的形状会对设备的性能产生不利影响 使用 GPU 渲染。最佳做法是从 显示列表而不是隐藏它们。

    【讨论】:

    • 这也是我的想法。玩家在“绘制/不绘制”方面的工作比“添加到显示列表、更新显示列表、生成和气泡事件、绘制”以及返回的方式更容易。第一个简单的测试(不是单元测试)表明,通过visible 操作 UI 时响应更快;不过,我必须更深入地研究它。
    【解决方案3】:

    删除孩子最好减少实例,事件并从您的Flash电影中释放内存,您可能会发现精灵可能会在一段时间后相互影响。从它们的绘制方式或那里的listneres来看,垃圾收集通常会在以下情况下发挥作用此方法已实现,最终可以与您的应用程序混在一起

    Visible 仍然在内存中有精灵,它只是当前没有绘制。你也可以保存精灵然后删除它,然后在需要时重新加载它,这将是所有解决方案的理想选择。 使用数组存储数据是另一种解决方案,也取决于您的应用程序是如何实现的,很难说,因为我们不知道,lol

    我想说添加孩子的表演压力更小,因为它仍然是唯一添加的项目与隐藏的倍数。此外,在这些隐藏的孩子中,属性与听众一起存储在内存中。

    【讨论】:

    • 正如@Pixel Elephant 所述-removeChild 不会减少您的实例,它会删除引用。如果碰巧这些是指向该对象的唯一引用 - 它们将被 gc'ed 并因此释放内存。我还没有彻底测试它,但从用户的角度来看 - visibleaddChildremoveChild 工作得更快。也不会产生任何事件。监听器可以与visibility 一起取消设置(就像恢复一样)。
    • 所以你告诉我如果我要创建一个循环添加 500 个对象到舞台与监听器,当它们不可见时,监听器就会消失?它们仍然在内存中被引用,直到它们再次可见,但是删除项目将从内存中销毁其所有资产
    • 不完全是; removeChildvisible = false 在 500 个对象上 - 听众仍然会在那里。 visible = falseremoveChilded comp 的唯一区别是第一个获得舞台事件;第二个 - 没有。我所说的“没有产生任何事件”的意思是invisible = false 不会产生任何东西。 addChildremoveChild - 执行生成事件。
    • 如果您要真正销毁一个对象 - 您将让它删除它附加在外部对象上的所有侦听器,这意味着任何未通过 this.addEventListener(...) 设置的侦听器。如果你不这样做,它永远不会被 gc'ed。
    猜你喜欢
    • 2011-12-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-02
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多