WebEngineView + 虚拟键盘 - 调整大小后保持滚动位置(重点输入)

Posted

技术标签:

【中文标题】WebEngineView + 虚拟键盘 - 调整大小后保持滚动位置(重点输入)【英文标题】:WebEngineView + virtual keyboard - maintain scroll position after resizing (focused input) 【发布时间】:2021-03-05 08:56:44 【问题描述】:

我有一个基于 WebEngineView 和 Qt Quick 虚拟键盘的非常简单的浏览器应用程序。

一切正常 - 每次单击 web 视图中的输入时,键盘都会完美显示,但令我困扰的是,如果我单击底部的输入,键盘会在打开后覆盖它,我不能看看我在输入什么。

我尝试通过调整 WebEngineView 元素的大小以适应键盘高度来解决这个问题,就像大多数移动应用程序一样。有效,我可以在键盘下滚动页面,但键盘仍然覆盖输入,我需要手动滚动。

有什么方法可以调整网页视图的滚动位置,使键盘不会覆盖来自 QML 的焦点输入? 我不能在单个网站上这样做,因为我允许导航到用户想要的任何网站,所以我需要一些通用方法。

这是我的 qml 代码:

import QtQuick 2.12
import QtQuick.Window 2.12
import FreeVirtualKeyboard 1.0
import QtWebEngine 1.8

Window 
    id: appContainer;
    visible: true
    width: 1280
    height: 600
    title: qsTr("WebEngineView")
    property string pathUrl: "https://www.w3schools.com/html/html_forms.asp"

    WebEngineView 
        id: webview
        width: appContainer.width
        url: appContainer.pathUrl
        height: appContainer.height
    

    /*
      Virtual keyboard
    */
     InputPanel 
        id: inputPanel
        z: 99
        y: appContainer.height
        anchors.left: parent.left
        anchors.right: parent.right
        states: State 
            name: "visible"
            when: Qt.inputMethod.visible
            PropertyChanges 
                target: inputPanel
                y: appContainer.height - inputPanel.height

            
        
        transitions: Transition 
            from: ""
            to: "visible"
            reversible: true
            ParallelAnimation 
                NumberAnimation 
                    properties: "y"
                    duration: 150
                    easing.type: Easing.InOutQuad
                
            

            onRunningChanged: 
                if(!running && inputPanel.state == "visible") 
                    // finished showing keyboard
                    webview.height = appContainer.height - inputPanel.height
                    console.log('Keyboard shown')
                 else if(running && inputPanel.state != "visible") 
                    // begins to hide keyboard
                    webview.height = appContainer.height
                    console.log('Keyboard starts to hide');
                
            
        
    

到目前为止,调整大小部分工作正常 - 我在 onRunningChanged 中执行此操作,因此 webview 在转换开始之前和结束之后调整大小 - 这可以防止在转换期间显示丑陋的空白空间。

更新webview.runjavascriptscrollIntoView在显示键盘后达到了我想要的效果:

webview.runJavaScript("document.activeElement.scrollIntoView(block: 'nearest', inline: 'nearest', behavior: 'smooth')");

但是我不确定这是否是最好的解决方案,因为我不喜欢将 javascript 评估纳入流程的事实。我想知道是否还有其他“本地”方式可以做到这一点。

【问题讨论】:

【参考方案1】:

调整 WebEngineView 的大小,滚动到视图中

调整WebEngineView 大小的问题在于,HTML 会看到您的设备屏幕突然缩小,并可能决定呈现截然不同的布局,例如将菜单从屏幕顶部移动到屏幕的一侧。

即使这没有发生,布局也发生了变化。新“屏幕”上的位置与旧“屏幕”上的位置不对应,没有 1:1 的关系,这就是为什么它首先滚动到一个看似随机的位置。

我们可以tell webpage 到scroll a focused element into view of new viewport:

如果它已经在屏幕上,那么什么都不会发生。 如果没有,网页会滚动以使元素尽可能适合屏幕。 scrollIntoView 具有根据需要滚动到屏幕顶部/底部的参数

所以当屏幕键盘打开时:

    保存原图scrollPosition 调整大小WebEngineView (可选)将 scrollPosition 分配给保存的值 - 尽管它可能对您没有任何好处 使用runJavaScript 确定activeElement 并使其成为scrollIntoView

当屏幕键盘关闭时重复相同的步骤。

不要调整大小,手动滚动

另一种方法是不调整“屏幕”的大小,如果元素被覆盖,只需将其滚动到视图中。

这需要 Qt 更改 VisualViewport,同时保持 LayoutViewport 不变(有关更多信息,请参阅 this 和 this),但似乎 Qt 无法做到这一点,至少不能仅通过 QML。

这让我们不得不手动进行:使用getBoundingClientRect 确定位置,计算键盘覆盖了多少空间,如果它不在我们计算的未覆盖视图区域内 - scrollTo 它。

(您仍然需要runJavaScript 才能进入网页)

也许this 和this 这样的问题会有所帮助

其他选项

@Hazelnutek 报告了document.activeElement.scrollIntoViewIfNeeded() 的一些成功

请参阅下面 cmets 中对此答案的讨论:

【讨论】:

我采用了第一种方法(调整大小)——但我注意到了一个问题。 scrollIntoView 函数似乎有点错误,并且在某些类型的网站上(我认为全屏网站没有任何实际滚动)调用它实际上会移动整个视口,并且在键盘关闭后它不会返回。因此我想要更好的东西,但我认为scrollTo 在没有实际滚动行为的页面上根本不起作用?需要做transform: translate.. 之类的事情吗? QML 的transform: translate 可能不起作用,因为我认为 QML 没有这种对 webkit 页面画布的访问权限。而是尝试在有问题的页面上设置 scrollPosition ( doc.qt.io/qt-5/… ) - 它有效吗?如果没有,可能需要一些 javascript hack - 这远远超出了我的专业知识。这个问题可能不是 Qt 特有的,所以问另一个问题,例如“我如何滚动一个不可滚动的网站?”带有javascriptwebpage等标签,列出违规网站,附上你的javascript代码。 如果您的主要问题只是关闭键盘后页面没有回滚,您可以尝试使用 window.scrollY ( developer.mozilla.org/en-US/docs/Web/API/Window/scrollY ) 保存当前位置,然后使用 window.scroll() 和保存的号码,或者您可以尝试保存和设置上述 QML 的scrollPosition。实验。看看有没有什么工作至少在某种程度上。另请注意,您可以使用userScripts 属性(doc.qt.io/qt-5/…)将任何 javascript 注入网页 尚未测试,但我猜scrollPosition 只有在整个视口可滚动时才有效,但有些网站是全屏的,当显示键盘时,它们的全部内容必须移动。 scrollIntoView 会这样做,但有时会出现错误并使视口向上移动。我在键盘关闭(焦点改变)后再次调用document.activeElement.scrollIntoViewIfNeeded(...) 解决了这个问题。工作至今。我认为只要只改变高度而不改变宽度,就css响应性而言,调整大小不应该是坏事 如果您的问题得到解决,请接受此答案或添加您的答案以及有效方法的简短描述并接受。这会将您的问题标记为不需要更多答案。谢谢。

以上是关于WebEngineView + 虚拟键盘 - 调整大小后保持滚动位置(重点输入)的主要内容,如果未能解决你的问题,请参考以下文章

如何在WebEngineView中调整滚动条的宽度?

Qt 5.5 WebEngineView 和多点触控

动画虚拟键盘

移动web 怎么捕获虚拟键盘弹出和关闭事件

浏览器的虚拟/屏幕键盘

Android 3:模拟虚拟钢琴键盘的软键盘行为