QML TextField 应用:自动关联与表单提交

前几篇把 TextField 的属性、校验、样式分头讲完了,这篇不碰新东西,把它们串起来做两个最常见的表单应用:搜索框自动补全、登录表单。

这篇是两个 demo:

  • 自动关联 --- 输入时弹出匹配项,点一下自动填入
  • 表单提交 --- 用户名/密码带图标,两个都填了登录按钮才亮

自动关联

先看搜索框的联想功能:你每敲一个字符,下面立刻弹出一串匹配项,点一下就填进输入框。整个交互牵扯四个组件:TextField 负责输入,Popup 负责弹层,ListView 展示结果,ItemDelegate 做可点击的候选行。

演示代码

源码里样式代码占了不少行,跟核心逻辑无关,这里全部折叠掉,只看三件事:怎么过滤、怎么弹出来、点完怎么填回去。

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts

FadeInAnimation {
    property var keywords: ["a", "ab", "abc", "bcd", "bcde", "cdef", "cdefg", "cdefgh"]
    property var matches: []

    // ... 省略外层布局与标题组件 TitleSeparator ...

    TextField {
        id: autoCompleteField
        placeholderText: "输入文本..."
        // ... 背景样式省略 ...

        onTextChanged: {
            if (text.length > 0) {
                matches = keywords.filter(function(key) {
                    return key.toLowerCase().indexOf(text.toLowerCase()) === 0
                })
                autoCompletePopup.visible = matches.length > 0
            } else {
                autoCompletePopup.visible = false
            }
        }
    }

    Popup {
        id: autoCompletePopup
        parent: autoCompleteField
        y: parent.height + 5
        width: parent.width
        height: 180

        contentItem: ListView {
            model: matches
            clip: true

            delegate: ItemDelegate {
                width: parent.width
                height: 30
                text: modelData
                // ... 悬停配色省略 ...

                onClicked: {
                    autoCompleteField.text = modelData
                    autoCompletePopup.visible = false
                }
            }

            ScrollIndicator.vertical: ScrollIndicator {}
        }
    }
}

关键逻辑解析

整个自动补全交互流程可以串成一条链路:用户输入内容 → 过滤出匹配候选项 → 弹出下拉弹窗 → 点击候选项回填内容,拆开看每一步实现都不难。

过滤逻辑 写在 onTextChanged 里面。keywords 存放全部候选词,matches 保存筛选出来的结果。 调用 filter() 做前缀匹配,用 indexOf(...) === 0 判断开头是否吻合,再通过 toLowerCase() 忽略大小写,哪怕输入大写 CD,也能搜到小写的 cdef。 有匹配结果就显示弹窗,输入框清空就把弹窗隐藏。

弹窗最麻烦的是定位问题Popup 如果不指定父组件 parent,会直接跑到窗口左上角,和输入框互不相关。我们把 parent 指定为输入框 autoCompleteField,将弹窗依附输入框;设置 y: parent.height + 5,弹窗就贴在输入框下方,宽度和输入框保持一致,看起来就是输入框自带的下拉面板。

点击选择回填交给 ListView 和 ItemDelegatemodel 绑定过滤结果数组 matches,数组一变,列表会自动更新,不用手动写刷新逻辑。 每个候选项是一个 ItemDelegate,点击触发 onClicked,拿 modelData 回填到输入框文本,再关闭弹窗,补全就完成。 至于鼠标悬停时背景变色,只是在 delegate 的背景样式里判断 hovered 状态,属于UI体验优化,不改变核心业务逻辑。

适用场景

大多数的"边输边联想"的地方都能用:搜索框提示、邮箱后缀补全、标签输入、命令行补全。把 keywords 换成动态数据源,输入时请求一次后端接口,就是搜索引擎式的实时建议了。

表单提交

第二个场景是登录表单:用户名、密码各一个输入框,密码用圆点掩码显示,两个框都填了登录按钮才亮。前几篇的图标嵌入、密码模式、background 样式、属性绑定,这里一次性全用上。

演示代码

两个输入框结构完全一样,区别只有图标和密码模式,所以只贴用户名框,密码框的差异在注释里说明。

qml 复制代码
import QtQuick
import QtQuick.Controls
import QtQuick.Controls.Basic
import QtQuick.Layouts

FadeInAnimation {
    // ... 省略外层布局与标题组件 TitleSeparator ...

    TextField {
        id: usernameField
        placeholderText: "用户名"
        leftPadding: 40                 // 给左边图标留位置
        verticalAlignment: Text.AlignVCenter

        background: Rectangle {
            color: "white"
            border.color: usernameField.activeFocus ? "#2196F3" : "#BBDEFB"
            border.width: usernameField.activeFocus ? 2 : 1
            radius: 4

            // 图标直接嵌在背景里,锚定在左侧
            Image {
                source: "/icons/user-icon.png"
                anchors.left: parent.left
                anchors.leftMargin: 10
                anchors.verticalCenter: parent.verticalCenter
                width: 20
                height: 20
            }
        }
    }

    // passwordField 结构同上,只差两处:
    //   1. 加 echoMode: TextField.Password 让输入显示为掩码
    //   2. 图标换成 /icons/lock-icon.png

    Button {
        text: "登录"
        enabled: usernameField.text !== "" && passwordField.text !== ""
        // ... 背景三态样式省略 ...
        onClicked: console.log("尝试登录:", usernameField.text)
    }
}

关键逻辑解析

有两个地方值得拿出来说。

  • 密码框 密码框关键就一行代码:echoMode: TextField.Password,开启之后输入的字符就会变成圆点。再配上锁形图标,用户一看就知道这里要输密码。掩码 + 图标组合在一起,比只做其中一种提示要清晰直观。
  • 登录按钮是这个 Demo 的核心,只用一行绑定就搞定状态控制:
vbnet 复制代码
enabled: usernameField.text !== "" && passwordField.text !== ""

这就是声明式绑定的优势:不用自己写监听回调。只要用户名或者密码输入框内容改动,enabled 会自动重新计算,按钮能不能点的状态自动更新。

按钮背景做了三套样式:正常是蓝色、按下变深蓝、禁用显示浅蓝,依靠 parent.enabledparent.pressed 的三元条件来切换。文字颜色也跟着启用状态联动,禁用的时候文字会变淡,让用户一眼就明白按钮现在无法点击。

适用场景

大多数"表单提交"的常规设计:登录、注册、搜索提交、设置保存。核心套路是"输入框 + 按钮 + enabled 联动",记住这个组合,搭任何表单都很快。

两个 demo 对比

维度 自动关联 表单提交
核心组件 TextField + Popup + ListView TextField + Button
联动方式 onTextChanged 主动触发过滤 enabled 属性声明式绑定
数据流 输入 → 过滤 → 显示 → 点击填充 输入 → 启用状态 → 点击提交
适用场景 搜索补全、标签联想 登录、注册、设置保存

从表格能看出两种联动思路:自动补全靠 onTextChanged 写逻辑主动触发,是命令式 ;登录按钮靠 enabled 属性绑定自动同步,是声明式。实际项目里两种常常混着用------计算复杂、要自己控制流程的用命令式,简单依赖关系的用声明式,代码会干净很多。

运行验证

  1. Qt Creator 打开 qml_textfield/CMakeLists.txt
  2. Ctrl+R 运行
  3. 在左侧导航切换"自动关联"、"表单提交"
  4. 自动关联:输入 a 看弹出 a / ab / abc、输入 cd 看弹出 cdef / cdefg / cdefgh、点击候选项看自动填入
  5. 表单提交:初始看登录按钮禁用(浅蓝)、只填用户名看按钮仍禁用、两个都填看按钮启用(蓝色)、点登录看控制台输出

扩展复用方向

  • 把自动补全封装成 AutoCompleteTextField 组件,model 属性接收数据源,onSelected 信号通知选中项
  • 自动补全数据源改成动态:onTextChanged 里调 C++ 后端或网络接口,实现搜索引擎式实时建议
  • 表单加校验:用户名用正则、密码要求长度 8+ 含大小写,校验未通过时按钮禁用并提示原因
  • 登录按钮加 BusyIndicator,点击后显示加载状态,模拟异步请求
  • 表单加"记住密码"、"自动登录"勾选框,用 SwitchCheckBox 组件配合 TextField 使用

已验证环境

相关推荐
sycmancia3 小时前
Qt——自定义控件温度计
开发语言·qt
艾莉丝努力练剑4 小时前
【Qt:问题解决】Qt程序入口点错误修复:MinGW ABI兼容问题
java·服务器·开发语言·qt·文件系统·动静态库
我是小灰灰吖16 小时前
QT在线安装器使用指南:从下载到配置完整教程
linux·qt·ubuntu
C++ 老炮儿的技术栈18 小时前
从 Qt Designer 属性编辑器的层级可以看到继承链
c语言·数据库·c++·qt·sqlite·visual studio
我是小灰灰吖20 小时前
Ubuntu 22.04 + Qt 6.9.3 应用程序打包与部署完整指南
qt·ubuntu
qq_401700411 天前
Qt自定义信号槽详解:带参发射 vs sender()获取,两种多信号关联方案全掌握
java·数据库·qt
郝学胜-神的一滴1 天前
Qt 高级编程 042:进度条从入门到自定义美化
开发语言·c++·qt·软件开发·用户界面
AI的探索之旅1 天前
嵌入式视觉学习路线:Qt + OpenCV + 工业相机,从零到综合项目
qt·opencv·学习
我是小灰灰吖1 天前
Ubuntu22.04,OpenCV 4.5.5 环境搭建
linux·qt·ubuntu