我们将创建两个自定义组件: 1. `NumericInputField`:一个输入框,当点击时弹出数字键盘。 2. `NumericKeyboard`:一个可缩放的数字键盘。 设计思路: - `NumericInputField` 是一个常规的输入框,但点击后会弹出 `NumericKeyboard`。 - `NumericKeyboard` 是一个弹出式键盘,它的大小可以等比例缩放(通过设置宽度,高度按比例计算)。 - 键盘内部包含数字0-9、小数点、退格、取消和确定按钮。 - 键盘的尺寸和字体大小都可以通过属性设置。 步骤: 1. 创建 `NumericKeyboard` 组件。 2. 创建 `NumericInputField` 组件,它包含一个 `TextInput` 和一个弹出键盘的机制。 3. 在 `NumericInputField` 中,当点击输入框时,显示 `NumericKeyboard`,并将键盘与输入框的位置关联(例如在输入框下方弹出)。 注意:为了支持等比例缩放,键盘的宽度可以设置,高度则根据宽度按比例计算(例如高度是宽度的0.6倍)。键盘内部的按钮大小和字体大小也根据键盘的当前尺寸进行缩放。 由于要求每个自定义组件的代码,我们将分别实现: 组件一:NumericKeyboard.qml 组件二:NumericInputField.qml 然后提供一个使用示例。
可缩放弹出式数字软键盘组件
下面我将实现一个完整的可缩放弹出式数字软键盘组件,包含两个自定义组件:NumericInputField
(输入框)和 NumericKeyboard
(键盘)。这两个组件都支持等比例缩放。
cpp
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQuick.Window 2.12
import QtQml 2.12
import QtQuick.Layouts 1.12
import "./image"
import QtQuick.Layouts 1.3
Window {
width: 1200
height: 800
visible: true
title: "可缩放弹出式数字键盘"
color: "#f8fafc"
ColumnLayout {
anchors.fill: parent
anchors.margins: 30
spacing: 20
// 标题
Text {
text: "可缩放弹出式数字键盘"
font.pixelSize: 28
font.bold: true
color: "#1e293b"
Layout.alignment: Qt.AlignCenter
}
// 示例1:基本使用
NumericInputField {
id: basicInput
Layout.preferredWidth: 300
Layout.preferredHeight: 60
placeholder: "点击输入金额"
Layout.alignment: Qt.AlignCenter
}
// 示例2:整数输入
NumericInputField {
id: integerInput
Layout.preferredWidth: 250
Layout.preferredHeight: 50
placeholder: "点击输入数量"
allowDecimal: false // 禁用小数点
Layout.alignment: Qt.AlignCenter
}
// 示例3:自定义键盘大小
NumericInputField {
id: customSizeInput
Layout.preferredWidth: 350
Layout.preferredHeight: 70
placeholder: "点击输入百分比"
keyboardScale: 0.7 // 键盘缩放比例
Layout.alignment: Qt.AlignCenter
}
// 控制面板
RowLayout {
Layout.alignment: Qt.AlignCenter
spacing: 20
// 键盘缩放控制
Slider {
id: scaleSlider
from: 0.5
to: 1.2
value: 0.8
Layout.preferredWidth: 200
onValueChanged: {
basicInput.keyboardScale = value
integerInput.keyboardScale = value
customSizeInput.keyboardScale = value
}
}
// 缩放值显示
Text {
text: "键盘缩放: " + scaleSlider.value.toFixed(2)
font.pixelSize: 16
color: "#64748b"
}
// 小数点开关
Switch {
id: decimalSwitch
checked: true
text: "允许小数点"
}
}
// 结果展示
GridLayout {
columns: 3
columnSpacing: 30
rowSpacing: 10
Layout.alignment: Qt.AlignCenter
Text { text: "金额:"; font.bold: true; color: "#475569" }
Text { text: basicInput.text || "0.00"; color: "#334155" }
Text { text: "缩放: " + basicInput.keyboardScale.toFixed(2); color: "#64748b" }
Text { text: "数量:"; font.bold: true; color: "#475569" }
Text { text: integerInput.text || "0"; color: "#334155" }
Text { text: "缩放: " + integerInput.keyboardScale.toFixed(2); color: "#64748b" }
Text { text: "百分比:"; font.bold: true; color: "#475569" }
Text { text: customSizeInput.text || "0.00"; color: "#334155" }
Text { text: "缩放: " + customSizeInput.keyboardScale.toFixed(2); color: "#64748b" }
}
}
// 应用键盘设置
Component.onCompleted: {
integerInput.allowDecimal = decimalSwitch.checked
}
// 小数点开关变化
Connections {
target: decimalSwitch
function onCheckedChanged() {
integerInput.allowDecimal = decimalSwitch.checked
}
}
}
cpp
//NumericInputField
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtQml 2.12
import "../image"
Rectangle {
id: root
width: 300
height: 60
radius: 10
color: "white"
border.color: root.activeFocus ? "#4f46e5" : "#d1d5db"
border.width: 2
// ===== 公共属性 =====
property alias text: inputField.text // 输入文本
property string placeholder: "点击输入" // 占位符文本
property real keyboardScale: 0.4 // 键盘缩放比例
property bool allowDecimal: true // 是否允许小数点
// ===== 键盘实例 =====
property NumericKeyboard keyboard: NumericKeyboard {
id: numericKeyboard
parent: Overlay.overlay // 确保键盘显示在最上层
allowDecimal: root.allowDecimal
visible: false
}
// ===== 输入区域 =====
TextInput {
id: inputField
anchors.fill: parent
anchors.margins: 15
verticalAlignment: Text.AlignVCenter
font.pixelSize: 24
clip: true
readOnly: true // 只能通过键盘输入
// 占位符文本
Text {
text: root.placeholder
font: inputField.font
color: "#9ca3af"
visible: inputField.text.length === 0 && !root.activeFocus
anchors.verticalCenter: parent.verticalCenter
}
}
// ===== 键盘图标 =====
Rectangle {
width: 40
height: 40
radius: 20
color: "#eef2ff"
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 10
Text {
text: "⌨"
font.pixelSize: 24
anchors.centerIn: parent
}
}
// ===== 点击弹出键盘 =====
MouseArea {
anchors.fill: parent
onClicked: {
// 设置键盘位置和大小
numericKeyboard.width = root.width * 3 // 键盘比输入框宽
// numericKeyboard.keyboardScale = keyboardScale
numericKeyboard.text = inputField.text
// 打开键盘
numericKeyboard.open()
// 连接信号
numericKeyboard.accepted.connect(handleAccepted)
}
}
// ===== 键盘接受事件处理 =====
function handleAccepted() {
inputField.text = numericKeyboard.text
}
// ===== 键盘关闭时断开连接 =====
Connections {
target: numericKeyboard
function onClosed() {
numericKeyboard.accepted.disconnect(handleAccepted)
}
}
// ===== 输入框获得焦点样式 =====
onActiveFocusChanged: {
border.color = activeFocus ? "#4f46e5" : "#d1d5db"
}
}
cpp
//NumericKeyboard
import QtQuick 2.12
import QtQuick.Controls 2.12
import QtGraphicalEffects 1.12
Popup {
id: root
width: Math.min(parent.width * 0.9, 600) // 宽度自适应,最大600px
height: width * 0.6 // 高度按比例计算
anchors.centerIn: parent
modal: true
dim: true
closePolicy: Popup.CloseOnEscape | Popup.CloseOnPressOutside
// ===== 公共属性 =====
property alias text: inputField.text // 绑定输入文本
property bool allowDecimal: true // 是否允许小数点
property real scaleFactor: width / 600 // 缩放因子(基于600px基准)
property int baseFontSize: 24 // 基准字体大小
property color buttonColor: "#f0f0f0" // 按钮默认颜色
// ===== 信号 =====
signal accepted() // 点击确定时触发
signal canceled() // 点击取消时触发
// ===== 键盘背景 =====
background: Rectangle {
anchors.fill: parent
color: "blue"
radius: 10 * scaleFactor
border.color: "#d1d5db"
border.width: 1 * scaleFactor
// 键盘阴影效果
layer.enabled: true
layer.effect: DropShadow {
color: "#40000000"
radius: 10 * scaleFactor
samples: 21
verticalOffset: 3 * scaleFactor
}
}
// ===== 输入框区域 =====
Rectangle {
id: inputContainer
width: parent.width - 40 * scaleFactor
height: 60 * scaleFactor
anchors.horizontalCenter: parent.horizontalCenter
anchors.top: parent.top
anchors.topMargin: 20 * scaleFactor
color: "white"
radius: 8 * scaleFactor
border.color: inputField.activeFocus ? "#4f46e5" : "#d1d5db"
border.width: 2 * scaleFactor
// 输入框
TextInput {
id: inputField
anchors.fill: parent
anchors.margins: 15 * scaleFactor
font.pixelSize: baseFontSize * scaleFactor + 4
verticalAlignment: Text.AlignVCenter
clip: true
readOnly: true // 禁止直接编辑
// 文本改变动画
Behavior on text {
SequentialAnimation {
PropertyAnimation {
target: inputContainer
property: "border.color"
to: "#4f46e5"
duration: 100
}
PropertyAnimation {
target: inputContainer
property: "border.color"
to: "#d1d5db"
duration: 300
}
}
}
}
// 清除按钮
Rectangle {
width: 30 * scaleFactor
height: 30 * scaleFactor
radius: 15 * scaleFactor
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.right
anchors.rightMargin: 10 * scaleFactor
color: clearMouseArea.containsPress ? "#fee2e2" : "#fef2f2"
visible: inputField.text.length > 0
Text {
text: "×"
font.pixelSize: baseFontSize * scaleFactor
color: "#ef4444"
anchors.centerIn: parent
}
MouseArea {
id: clearMouseArea
anchors.fill: parent
onClicked: inputField.text = ""
}
}
}
// ===== 键盘网格布局 =====
Grid {
id: grid
anchors.top: inputContainer.bottom
anchors.topMargin: 20 * scaleFactor
anchors.horizontalCenter: parent.horizontalCenter
spacing: 10 * scaleFactor
columns: 5
// 数字键1-9
Repeater {
model: 9
KeyButton {
text: index + 1
width: keyWidth
height: keyHeight
onClicked: inputField.insert(inputField.cursorPosition, text)
}
}
// 小数点键(根据allowDecimal显示/隐藏)
KeyButton {
text: "."
width: keyWidth
height: keyHeight
visible: root.allowDecimal
onClicked: {
if (!inputField.text.includes(".")) {
inputField.insert(inputField.cursorPosition, text)
}
}
}
// 数字0
KeyButton {
text: "0"
width: keyWidth
height: keyHeight
onClicked: inputField.insert(inputField.cursorPosition, text)
}
// 退格键
KeyButton {
text: "⌫"
width: keyWidth
height: keyHeight
bgColor: "#fecaca"
textColor: "#b91c1c"
onClicked: inputField.text = inputField.text.slice(0, -1)
}
// 取消键
KeyButton {
text: "取消"
width: keyWidth
height: keyHeight
bgColor: "#e5e7eb"
onClicked: {
root.canceled()
root.close()
}
}
// 确定键
KeyButton {
text: "确定"
width: keyWidth
height: keyHeight
bgColor: "#4f46e5"
textColor: "white"
onClicked: {
root.accepted()
root.close()
}
}
}
// ===== 计算按键尺寸 =====
property real keyWidth: (root.width - 40 * scaleFactor - 2 * grid.spacing) / 6
property real keyHeight: keyWidth * 0.8
// ===== 弹出动画 =====
enter: Transition {
ParallelAnimation {
NumberAnimation { property: "opacity"; from: 0; to: 1; duration: 300 }
NumberAnimation { property: "scale"; from: 0.8; to: 1; duration: 300; easing.type: Easing.OutBack }
}
}
// ===== 关闭动画 =====
exit: Transition {
ParallelAnimation {
NumberAnimation { property: "opacity"; from: 1; to: 0; duration: 300 }
NumberAnimation { property: "scale"; from: 1; to: 0.8; duration: 300 }
}
}
}
KeyButton
cpp
//KeyButton
import QtQuick 2.12
// ===== 自定义按钮组件 =====
Rectangle {
id: btn
radius: 8 * root.scaleFactor
color: mouseArea.pressed ? Qt.darker(bgColor, 1.2) : bgColor
border.color: mouseArea.pressed ? Qt.darker(borderColor, 1.2) : borderColor
border.width: 1 * root.scaleFactor
// ===== 可自定义属性 =====
property alias text: label.text
property color bgColor: root.buttonColor
property color textColor: "black"
property color borderColor: "#d1d5db"
// 按钮文本
Text {
id: label
anchors.centerIn: parent
font.pixelSize: root.baseFontSize * root.scaleFactor
color: btn.textColor
}
// 鼠标区域
MouseArea {
id: mouseArea
anchors.fill: parent
onClicked: btn.clicked()
}
// 点击效果动画
scale: mouseArea.pressed ? 0.95 : 1.0
Behavior on scale {
NumberAnimation { duration: 100 }
}
// 点击信号
signal clicked()
}