Hola a todos. Soy un desarrollador de Android con poca experiencia. Y hoy me gustaría compartir la experiencia de desarrollar un proyecto educativo en C ++ y Qt con simple navegación entre pantallas. Me encantaría escuchar críticas o adiciones a mi solución y espero que pueda ayudar a alguien y hacer la vida más fácil.
Tarea
Antes de comenzar, definamos dos términos para que al final no haya confusión.
Window es la ventana más común en Windows o en cualquier otro sistema operativo.
Pantalla: algún tipo de contenido que puede ser reemplazado por otros dentro de la ventana.
, - Trello, , . ? , .
, , QStackedWidget . - .
. , , . .
, , , :
.
.
.
.
— BaseFragment. , . , .
class BaseFragment: public QFrame {
Q_OBJECT
signals:
//
public:
BaseFragment();
~BaseFragment();
// -
};
BaseFragment. , QStackedWidget .
class LoginFragment: public BaseFragment
class StartFragment: public BaseFragment
class RegistrationFragment : public BaseFragment
, , , . , , .
class BaseScreensFactory {
public:
BaseScreensFactory();
~BaseScreensFactory();
virtual BaseFragment* create(QString tag);
virtual QString createStart();
};
:
create(QString tag) — ( );
createStart() — .
:
// screensfacrory.h
namespace screens {
static const QString SPLASH_TAG = "splash";
static const QString START_TAG = "start";
static const QString LOGIN_TAG = "login";
static const QString REGISTRATION_TAG = "registration";
// .....
};
class ScreensFactory: public BaseScreensFactory {
public:
ScreensFactory();
~ScreensFactory();
BaseFragment* create(QString tag) override;
QString createStart() override;
};
// screensfacrory.cpp
BaseFragment* ScreensFactory::create(QString tag) {
qDebug("ScreensFactory create");
if (tag == SPLASH_TAG) {
return new SplashFragment;
} else if (tag == START_TAG) {
return new StartFragment;
} else if (tag == LOGIN_TAG) {
return new LoginFragment;
} else if (tag == REGISTRATION_TAG) {
// .....
}
}
QString ScreensFactory::createStart() {
return SPLASH_TAG; // .
}
, , , . QStackedWidget .
:
navigateTo(tag) — .
back() — .
replace(tag) — .
newRootScreen(tag) — .
navigateToWhithData(tag, data) — , navigateTo(tag), - .
, , , navigateTo(REGISTRATION_TAG), , . newRootScreen(MAIN_TAG).
, , BaseFragment , .
class BaseFragment: public QFrame {
Q_OBJECT
signals:
//
void back();
void navigateTo(QString tag);
void newRootScreen(QString tag);
void replace(QString tag);
void navigateWhithData(QString tag, BaseModel* model);
public:
BaseFragment();
~BaseFragment();
//
virtual void onPause();
virtual void onResume();
virtual void setData(BaseModel* model);
};
, , , onPause(), , onResume() , . setData() .
. QStackedWidget BaseScreensFactory .
navigator.h:
class Navigator: public QObject {
Q_OBJECT
private:
QStackedWidget *currentContainer;
BaseScreensFactory *screensFactory;
QLinkedList<BaseFragment*> stack;
/**
* @brief createAndConnect
* @param tag .
*
*
* .
*
* @return .
*/
BaseFragment* createAndConnect(QString tag);
/**
* @brief connectFragment
* @param fragment
* .
*
*
*
* .
*
*/
void connectFragment(BaseFragment *fragment);
/**
* @brief disconnectFragment
* @param fragment
*
* .
*/
void disconnectFragment(BaseFragment *fragment);
public:
Navigator(
QStackedWidget *container,
BaseScreensFactory *screensFactory
);
~Navigator();
BaseFragment* getStartScreen();
public slots:
/**
* @brief navigateTo
* @param tag .
*
* .
*/
void navigateTo(QString tag);
/**
* @brief back
*
* .
*/
void back();
/**
* @brief replace
* @param tag
* .
*
*
* .
*/
void replace(QString tag);
/**
* @brief newRootScreen
* @param tag
* .
*
*
* .
*/
void newRootScreen(QString tag);
/**
* @brief navigateWhithData
* @param model
*
* navigateTo .
*/
void navigateWhithData(QString tag, BaseModel* model);
};
. . . connectFragment , . disconnectFragment . createAndConnect . getStartScreen , , .
BaseFragment* Navigator::getStartScreen() {
return createAndConnect(this->screensFactory->createStart());
}
void Navigator::connectFragment(BaseFragment *fragment) {
connect(fragment, &BaseFragment::back, this, &Navigator::back);
connect(fragment, &BaseFragment::replace, this, &Navigator::replace);
connect(fragment, &BaseFragment::navigateTo, this, &Navigator::navigateTo);
connect(fragment, &BaseFragment::newRootScreen, this, &Navigator::newRootScreen);
connect(fragment, &BaseFragment::navigateWhithData, this, &Navigator::navigateWhithData);
}
void Navigator::disconnectFragment(BaseFragment *fragment) {
disconnect(fragment, &BaseFragment::back, this, &Navigator::back);
disconnect(fragment, &BaseFragment::replace, this, &Navigator::replace);
disconnect(fragment, &BaseFragment::navigateTo, this, &Navigator::navigateTo);
disconnect(fragment, &BaseFragment::newRootScreen, this, &Navigator::newRootScreen);
disconnect(fragment, &BaseFragment::navigateWhithData, this, &Navigator::navigateWhithData);
}
BaseFragment* Navigator::createAndConnect(QString tag) {
BaseFragment *fragment = this->screensFactory->create(tag);
connectFragment(fragment);
return fragment;
}
, .
Navigator::Navigator(
QStackedWidget *container,
BaseScreensFactory *screensFactory
) {
this->screensFactory = screensFactory;
this->currentContainer = container;
BaseFragment* startFragment = getStartScreen();
this->stack.append(startFragment);
currentContainer->addWidget(stack.last());
currentContainer->setCurrentIndex(0);
}
. : navigateTo. , . .
void Navigator::navigateTo(QString tag) {
BaseFragment *newFragment = this->screensFactory->create(tag);
stack.last()->onPause();
disconnectFragment(stack.last());
connectFragment(newFragment);
stack.append(newFragment);
currentContainer->addWidget(newFragment);
currentContainer->setCurrentWidget(newFragment);
}
, . , . .
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
{
try {
container = new QStackedWidget;
this->factory = new ScreensFactory;
this->navigator = new Navigator(
this->container,
this->factory
);
this->setCentralWidget(container);
} catch (std::exception& e) {
qDebug("%s", e.what());
}
}
Ahora hemos mirado una gran cantidad de piezas de código, al principio puede parecer que no están muy conectadas. Después de mucho intentar averiguarlo, este sentimiento no desaparece, y en realidad es algo bueno. Como resultado, pudimos desarrollar varias pantallas de la aplicación de forma completamente independiente del resto de partes. Escribimos la implementación de BaseFragment y la agregamos a la fábrica.
Mi solución me ayudó mucho en la implementación del proyecto. Pude acelerar el desarrollo y dividir el código.
Gracias a todos los que han leído, espero que esto ayude a alguien.
Aquí está el enlace de GitHub del proyecto.