【问题标题】:Zero width or height versus visible property in QMLQML 中的零宽度或高度与可见属性
【发布时间】:2017-08-30 09:32:47
【问题描述】:

将组件的widthheight 设置为零与将其visible 属性设置为false 的效果相同吗?

一个示例用例:

我有一个项目,它滑入窗口。滑动是通过将其高度从0 设置为x 以及当我将这个项目从x 关闭到0 时发生的。不想深入探讨为什么我不对项目的位置设置动画。当项目的高度为 0 时,我应该将其 visible 属性设置为 false 还是没有任何区别?

【问题讨论】:

    标签: qt qml


    【解决方案1】:

    不是真的,除非你剪辑。而且最好尽量避免剪裁。

    大小为零的Item 的子级仍然可见。

    而将可见设置为 false 将隐藏整个对象树。

    在您的特定情况下,只要它不会导致您有不想要的可见剩菜,这似乎并不重要。您当然不希望有诸如 visible: height 之类的绑定,因为这会在动画的每一步都不必要地执行。

    为了安全起见,您可以在动画上安装处理程序以切换可见性:

    // in the animation
    onStarted: if (!item.height) item.visible = true // show if start at 0
    onStopped: if (!item.height) item.visible = false // hide if end at 0
    

    如果您将可见性直接绑定到高度,这将避免您获得的持续重新评估,但仍将确保在您的对象开始扩展之前和完成收缩后关闭可见性。

    【讨论】:

    • 是的,在我的情况下,孩子们的身高也是 0。感谢您的澄清并指出visible 属性的绑定地狱。
    • 好吧,我不会称之为地狱,但这是您可以避免的开销,为什么不呢?
    • 我仍然会将visible 绑定到height,但要注意&& 对此绑定的评估,在某些条件下我知道它不会改变。 QML 在这方面似乎很聪明。除了确保隐藏整个对象树之外,您还可以在查询可见性的位置 上增加界面的清晰度。如果其他程序员想知道它,他需要查询visibleopacitydimensions,可能还需要查询对象树来确定该项目是否确实可见。我会考虑将visible 设置为正确的值,这是一种很好的做法。
    • @derM 最好使用计时器并在动画结束后简单地关闭可见性,而不是重新评估绑定。或者使用finished 并指望动画不会被异常中断。您不能真正保护评估,因为&& 本身需要评估。此外,它并没有比visible: height 更快。
    【解决方案2】:

    正如dtech 已经指出的,组件根节点的尺寸不会自动代表底层对象树的尺寸。举个例子:

    Item {
        id: root
        Text {
            id: txt
            text: 'some text produces implicit width'
        }
    }
    

    在此示例中,将显示txt 的文本,尽管root 的尺寸为width: 0; height: 0
    正如dtech 已经提到的,您可以将clip 设置为true,但这是不可取的,因为它会被传递给渲染器,渲染器会渲染Item 及其树,最后对其应用剪辑 - 分批进行。

    如果你有这样的事情:

    Item {
        Rectangle {
            anchors.fill: parent
            color: 'red'
        }
    }
    

    渲染器在渲染时不会做任何额外的事情,因为它可以与其余的在同一批次中处理。然而,作为开发人员,很难判断大小设置为0 时是否可见。因此建议始终正确设置visible

    我们可以简单地设置

    visible: width > 0 && height > 0 && opacity > 0
    

    只要我们不对这些属性中的任何一个进行动画处理或频繁更改它们,它就可以正常工作。至少对于动画我们可能有很好的了解,当这些属性中的任何一个可能变为0 并使用此信息来减少评估量时。

    QML 的好处在于,逻辑表达式是从左到右计算的,这意味着在我们的上一个示例中:

    • 如果width === 0height 发生变化,不会触发重新评估
    • 如果height === 0width 发生更改,则每次更改都会触发重新评估。

    这意味着,我们需要把最稳定的条件放在第一位。这可能是我们的关于这些值何时可能改变的信息。我建议使用animation.running 属性来防止在动画运行时重新评估绑定。

    让我们举一个更完整的例子:点击后,Rectangle 将从 width: 800 缩小到 width: 0 - 这将使其不可见。

    或者三个附加属性binding1, binding2, binding3 绑定到表达式,我们可以使用它们来设置visible。当绑定的特定部分被重新评估时,我们会记录下来。

    Rectangle {
        id: rect
        color: 'red'
        width: 800
        height: 600
    
        NumberAnimation {
            id: ani1
            target: rect
            property: 'width'
            from: 800
            to: 0
            duration: 3000
        }
    }
    MouseArea {
        anchors.fill: parent
        onClicked: ani1.running = true
    }
    
    property bool binding1: {console.log("1", !rect.width); return !rect.width}
    property bool binding2: {!ani1.running && (function() { console.log("2", !rect.width); return !rect.width })()}
    property bool binding3: {(function() { console.log("3", !rect.width); return !rect.width })() && !ani1.running}
    
    // equivalent, stripped of the logging:
    // property bool binding1: !rect.width
    // property bool binding2: !ani1.running && !rect.width
    // property bool binding3: !rect.width && !ani1.running
    

    正如我们所见,binding1 会在宽度发生变化时不断重新评估。这是不可取的。
    我们可以看到,binding2 仅在创建时评估一次,并且每当 ani 停止运行时。
    binding3 中,我们反其道而行之,我们首先评估width,然后评估ani 是否正在运行。这意味着,只要 width 发生变化,我们就会重新评估。

    我们也可以使用信号处理程序 ani.onStartedani.onStopped 并显式设置可见性,但这不是声明性,QML 鼓励您始终努力保持声明性。 p>

    【讨论】:

    • 如果我制作动画的项目是ListView 的代表,而这个ListView 被包裹在Item 中,我想设置这个Itemvisible 属性如果 ListView 中的所有代表都不可见,则为 false,即他们的 visible 属性为 false。我是否只是简单地遍历ListViewchildren 属性并检查其中是否有任何visible = true?听起来有点老套。
    • 我倾向于将代理包装在加载器中,而不是简单地隐藏代理,而是将它们从内存中卸载以获取隐藏对​​象。加载器设置为项目的大小,如果已卸载,则设置为 0x0。隐藏所有其代表都是大小为零的加载程序的列表视图没有实际好处,因为它没有任何可见的表示,隐藏它没有任何好处,不是内存而不是 CPU 周期,但是有好处通过使用装载机卸载隐藏的东西。因此,如果效率是优先事项,那就是要走的路。
    • 呃,这是一个复杂的问题。首先,您需要在模型中扮演一个角色,在某种程度上存储可见性。您可以使用模型信号来计算有多少项目满足可见性条件(例如 dataChanged、向上和向下计数)。您还可以使用QSortFilterProxyModel 来过滤该条件并使用count 属性。如果您不需要该条目的delegate,请在ListView 中使用它。这将自动进行卸载。如果您需要委托(例如因为Timer),请使用原始模型。然后使用 Loader 卸载视觉对象。
    • 如果您在两个可见项目之间有数百个不可见项目,这会产生各种影响:1. ListView 的 contentItem 总大小的猜测将被取消. 2. 滚动时,它们都会被创建。因此,通过使用QSortFilterProxyModel 过滤它们,根本 不拥有它们将为您节省大量不必要的项目的创建和破坏。如果代表有重要的(不可见的)部分,你可能已经搞砸了,因为你不能保证它们的存在(当项目不在视野范围内时)。将它们放在单独的Instantiator 中。
    猜你喜欢
    • 1970-01-01
    • 2018-02-07
    • 2019-07-17
    • 1970-01-01
    • 2010-12-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-24
    相关资源
    最近更新 更多