QML ListView和键导航 - 处理单个键事件

时间:2017-01-27 15:28:51

标签: c++ qt events keyboard qml

我正在使用Qt 5.6.2(Windows 10)并拥有一个QObject派生的映射器,可将QKeyEvent发送到当前关注的QML项目。我的基类有一个虚拟mapToKey()函数,然后在我想要使用的各种映射器中覆盖它。例如,下面的映射函数是我的LeftArrowMapper的一部分,并发送左箭头键事件:

void LeftArrowMapper::mapToKey()
{
    // Retrieve root of QML context
    QObject* root = engine->rootObjects()[0];
    // Get currently focused item
    QQuickItem *item = (root->property("activeFocusItem")).value<QQuickItem *>();
    if (item == NULL) qDebug() << "No item";

    // Create a key and a key event
    Qt::Key key = Qt::Key_Left;
    QKeyEvent* event = new QKeyEvent(QKeyEvent::KeyPress,
                                     key,
                                     Qt::NoModifier,
                                     KeySequence(key).toString());

    // Send it to the focused QML item
    QCoreApplication::postEvent(item,event);
}

如果给定项目支持关键事件,它将作出相应的反应。否则,收到的键事件不会触发任何响应。我已使用TextField(一个基于自定义Text的组件(仅使用通过键事件发送的文本更新text属性)等对其进行了测试。我还没有&# 39;遇到任何问题。直到我开始使用ListView ...

我尝试使用键盘的左右箭头键导航ListView,并使用我的映射器的mapToKey()功能人工创建关键事件(点这里:映射硬件事件,如按真实按钮到Qt事件)。在Qt 5.6中,将focus属性设置为true,可以使用键进行导航(在5.8中,可能还有5.7,keyNavigationEnabled属性必须设置为true;没有Qt 5.6)中的这种财产。

我的ListView如下所示:

ListView {
    id: list
    orientation: Qt.Horizontal
    width: rootWindow.width
    height: 50

    Keys.onPressed: {
        console.log("list: " + event.key + " : " + event.text)
        if (event.key == Qt.Key_Left) console.log("Moving to the left");
        else if (event.key == Qt.Key_Right) console.log("Moving to the right");
    }

    model: ListModel {
        id: items
    }
    delegate: Rectangle {
        width: 50
        height: 50
        Text {
            anchors.centerIn: parent
            text: "[" + name + "]"
        }
    }

    Component.onCompleted: function() {
        for(var i = 1; i <= 100; i++) {
            items.append({"name" : i});
        }
    }
    onFocusChanged: {
        console.log("List focus has changed");
    }
}

我发现的问题是这个QML组件处理关键事件的奇怪方式。似乎它试图模仿基于动力学的滑动(加速度被考虑),当您滚动浏览其中的项目时,您可以使用鼠标。这意味着以下内容:

  • 如果箭头键长时间保持(大约 2秒),则会触发滚动视图 - 一旦启用,它就不会滚动单个项目而是多个
  • 在触发上述滚动后,用户可以继续按箭头键继续快速滚动。此时,每个键事件滚动一个项目也是可能的,但是......
  • 更改方向会导致上述行为重置,因此用户需要执行另一项操作&#34;触发滚动&#34;事件以向相反方向滚动
  • 触发多个单独的键事件也会触发滚动,但这次是单个项目。此外,我上面提到的时间间隔似乎并不适用于此。在他可以选择列表中的下一个/上一个项目之前,用户需要按下给定的箭头键7次(至少这是我计算的数量)

在内部,映射似乎需要一定量的按下并释放按住-X-seconds 键事件才能实际触发ListView中的一些回复。

现在我的一位同事创建了我们正在使用的ListView的自定义键导航,它运行正常。但是,我想知道是否有一些秘密设置(或者我们都错过了文档中的备忘录)来处理这个问题。我可以看到这种类型的互动应用(即模拟加速),但对于我正在努力的场景,我真的不需要它,或者至少现在不需要它。另外,如果我想在某些时候添加伪加速部分,知道如何完成这将是非常有用的。

1 个答案:

答案 0 :(得分:2)

按左/右箭头键时ListView中基于动能的滑动是来自ListView.highlight的动画。为了使其可见,我们可以在您的列表中添加一些代码:

ListView {
    id: list

    Keys.onPressed: {
        console.log(list.currentIndex);
        //...
    }
    delegate: Item { // so we can see highlight in background
        width: 50
        height: 50
        Text {
            anchors.centerIn: parent
            text: "[" + name + "]"
        }
    }
    highlight: Rectangle {
        width: 50
        height: 50
        color: "pink"
    }
    //...
}

当您按右箭头键时,currentIndex会更改,并且突出显示会移动到该索引。如果您明确更改currentIndex,则可以看到相同的动画:

Button {
    text: "go to 45"
    onClicked: list.currentIndex = 45;
}

要自定义动画,您可以更改ListView.highlightMoveDurationListView.highlightMoveVelocity属性,或将ListView.highlightFollowsCurrentItem设置为false以将其关闭。您也可以按照ListView example code制作自己的高光动画。

  

在内部,映射似乎需要一定量的按下并释放或按住X秒键事件才能在ListView中实际触发某些响应。

ListView处理关键事件时没有任何魔力,它只是经常更改currentIndex,而QML中的动画可以完美地处理它。