Введение
Привязки (статья 481) синхронизируют значения свойств непрерывно — для дискретных, именованных состояний интерфейса (свёрнутая/развёрнутая панель, выбранный/невыбранный элемент, режим редактирования) QML предоставляет специальный механизм State/Transition, декларативно описывающий набор возможных состояний и переходы между ними.
Концепция
State определяет именованное состояние как набор изменений свойств (PropertyChanges) относительно базового, «обычного» состояния элемента (состояние с пустым именем "", представляющее исходные значения, заданные непосредственно в самом элементе) — переключение между состояниями осуществляется присваиванием свойству state имени нужного состояния. Transition определяет, как именно должен происходить переход между двумя конкретными состояниями (через анимацию изменяющихся свойств, статья 487) — без явно объявленного Transition переключение состояния происходит мгновенно, без какой-либо плавности.
Пример кода: States и Transitions
import QtQuick
Rectangle {
id: panel
width: 200; height: 100
color: "lightblue"
states: [
State {
name: "expanded"
PropertyChanges { panel.height: 300; panel.color: "steelblue" }
},
State {
name: "collapsed"
PropertyChanges { panel.height: 30 }
}
// Состояние с пустым именем (по умолчанию) НЕ объявляется явно —
// оно соответствует исходным значениям, заданным прямо в Rectangle (height: 100, color: "lightblue")
]
transitions: [
Transition {
from: "*"; to: "*" // применяется к ЛЮБОМУ переходу между любыми состояниями
NumberAnimation { property: "height"; duration: 300; easing.type: Easing.InOutQuad }
ColorAnimation { property: "color"; duration: 300 }
}
]
MouseArea {
anchors.fill: parent
onClicked: panel.state = panel.state === "expanded" ? "collapsed" : "expanded"
}
}
// Состояние, зависящее от условия через привязку, а не только от явного присваивания
Rectangle {
id: statusIndicator
property bool isOnline: false
width: 100; height: 30
state: isOnline ? "online" : "offline" // STATE САМ может быть результатом привязки!
states: [
State { name: "online"; PropertyChanges { statusIndicator.color: "green" } },
State { name: "offline"; PropertyChanges { statusIndicator.color: "gray" } }
]
}
Пояснения к коду
Объявление states как массива State-объектов с именами "expanded"/"collapsed" показывает структуру механизма — каждое состояние явно перечисляет, какие свойства должны измениться (через PropertyChanges) относительно исходных, базовых значений, заданных непосредственно в самом Rectangle. transitions с from: "*"; to: "*" показывает универсальный переход, применяемый к любой паре состояний — для более тонкого контроля можно объявить отдельные Transition для конкретных пар from/to, если разным переходам нужна разная анимация. Пример statusIndicator показывает, что само свойство state может быть результатом привязки (isOnline ? "online" : "offline"), а не только явного, императивного присваивания — это даёт мощную комбинацию декларативного управления состояниями с декларативной логикой определения, какое состояние должно быть активным в зависимости от других данных приложения.
Подводные камни
- Дублирование одних и тех же значений свойств между базовым (неявным) состоянием и явно объявленными
State, что усложняет понимание того, какое именно значение свойства актуально в каждый конкретный момент — стоит явно проектироватьPropertyChangesкаждого состояния как изменения именно ОТНОСИТЕЛЬНО базовых значений, заданных в самом элементе, а не пытаться продублировать полный набор значений в каждом состоянии независимо. - Использование сложной, многошаговой императивной логики переключения состояний вместо простого, прямого присваивания
stateили привязки к условию — механизм состояний QML рассчитан на простое, прямое указание имени целевого состояния, и обходные, излишне сложные способы переключения (через множественные промежуточные шаги или вспомогательные свойства) часто говорят о неоптимальном использовании самого механизма состояний для конкретной задачи. - Забытое объявление
Transitionдля конкретной пары состояний, что приводит к «резкому», мгновенному переключению значений свойств без какой-либо анимации именно для этой конкретной пары, тогда как для других пар переходов анимация работает (если для них есть подходящий, более специфичныйTransition) — несогласованность в анимированности разных переходов одного и того же элемента может выглядеть как непродуманная, непоследовательная деталь интерфейса для конечного пользователя. - Игнорирование того, что
PropertyChangesбез явногоrestoreEntryValues: false(значение по умолчанию —true) автоматически восстанавливает исходное значение свойства при выходе из состояния — это поведение по умолчанию обычно ожидаемо и удобно (значения корректно «откатываются» при возврате к базовому состоянию), но для случаев, где это restoring поведение нежелательно (например, значение должно «запомниться» даже после выхода из состояния), нужно явно отключить его черезrestoreEntryValues: false, что легко упустить, ожидая иного, неожидаемого поведения по умолчанию.