乍暖还寒时候,与上班提醒互道早安。
前言
自从前端大火了以后,UI数据双向绑定的ui框架愈发流行。作为前菜鸡安卓开发,我也是最近才知道谷歌还有推 Jetpack Compose
作为UI框架;其最大的亮点就是 数据双向绑定
和 声明式UI
。而作为现Qt开发,看到声明式UI的使用表示简直不要太爽啊!
实现声明式UI ≈ 实现一个 编译器
,非我等俗人可简单实现。那就退而求其次,先用Qt实现 UI与·数据双向绑定
。
原理
查找了一下,实现数据绑定的做法有大致如下几种:
- 发布者-订阅者模式
- 脏值检查
- 数据劫持
Qt 实现思路
首先明确我们需要实现什么:
实现一个UI组件,这个组件可实现数据双向绑定。我们更新绑定变量的时候,绑定这个变量的UI组件,数据也会同步更新。同理,我们直接修改UI组件的属性,例如滑动条的位置亦会同步修改变量值。
在 Qt - 一文理解信号槽机制(万字剖析整理) 中讲到 Qt的信号槽机制就有用到 发布-订阅模型
。那么我们将通过信号槽实现。
随着而来的是,信号槽机制依赖于 QObject
,元对象系统
。而我们绑定的变量可能是 int
、float
之类。为了能使用信号槽的通讯机制,我们必须将基本数据类型再封装一次。然后在变量类改变时通知控件类,在控件类改变时通知变量类。
在Qt中,控件类的改变本身就有相应的 changed()
信号,所以我们只需要接起来即可。
接下来的问题是如何知晓变量类改变,答案是 重载赋值运算符
。
源码
/*! hslider.h */
#ifndef HSLIDER_H
#define HSLIDER_H
#include <QWidget>
#include <QSlider>
#include <QDebug>
class HInt : public QObject
{
Q_OBJECT
public:
explicit HInt(int x = 0){number = x;}
void operator=(const HInt &newValue )
{ Q_EMIT(change(newValue.number)); number = newValue.number;}
signals:
void change(int newValue);
public slots:
void update(int newValue){ number = newValue; qDebug() << "HInt::update: " << newValue;}
private:
int number;
};
class HSlider : public QWidget
{
Q_OBJECT
public:
explicit HSlider(QWidget *parent = nullptr);
void Buid(HInt *newValue);
signals:
void change(int newValue);
public slots:
void update(int newValue);
private:
QSlider* mSlider;
HInt *mValue;
};
#endif // HSLIDER_H
/*! hslider.cpp */
#include "hslider.h"
HSlider::HSlider(QWidget *parent) : QWidget(parent)
{
mSlider = new QSlider(this);
connect(mSlider,&QSlider::valueChanged,this,&HSlider::change);
}
void HSlider::Buid(HInt *newValue){
if(newValue){
if(!mValue){
disconnect(mValue,&HInt::change,this,&HSlider::update);
disconnect(this,&HSlider::change,mValue,&HInt::update);
}
mValue = newValue;
connect(mValue,&HInt::change,this,&HSlider::update);
connect(this,&HSlider::change,mValue,&HInt::update);
}
}
void HSlider::update(int newValue){
mSlider->setValue(newValue);
qDebug() << "HSlider::update" << newValue;
}
效果
一些想法
- 这是一个简陋的封装及演示,有时候应该参考
QSlider
的实现自定义控件。 HInt
用起来让人很不爽,可能得找找有没有别的什么办法能监控到变量的改变(并不想用事件轮询)。或许我应该好好了解下 数据劫持 和 脏值检查 看能不能找到一些实现思路。