Как оказалось, JavaFX не обладает удобным инструментом, чтобы реализовать навигацию между окнами. Но это решается не сложно. Преследуемая цель:
Вариантов реализовать подобный инструмент несколько. Почему выбран именно этот — об этом ниже.
Для тех, кто привык воспринимать через схемы:
Каждый экран должен реализовывать интерфейс Controller (Controller.java).
Изучение JavaFX, начинается с рассказа о том, что бывает файл разметки FXML и сопровождающий его класс контроллера на языке Java. Реализация нашего интерфейса, позволит задать (setView) и получить(getView) корневой элемент разметки файла FXML. Метод Show — отобразит содержимое fxml файла.
Далее, базовый для всех наших экранов класс BaseController (BaseController.java)
Каждый контроллер экрана, необходимо наследовать от BaseController:
А теперь о том как работает навигация — файл Navigation.java:
Чтобы возвращаться назад, нам понадобиться история переходов по окнам. Используем для этого список:
Можно использовать класс приложения, чтобы всякий раз иметь возможность получать доступ к объекту навигации. (Main.java)
Допустим необходимо чтобы контроллер View2 передал строку текста контроллеру View3. Для этого, объявляем в контроллере View3:
1 способ. Если при навигации, мы бы использовали принцип: один Stage — один контроллер, то конечно бы у нас получилось все хорошо. Но что если экранов много? Это можно сравнить с просмотром некоторого сайта — когда каждая ссылка открывается в новом окне. Но при таком подходе обнаружились проблемы при отображении окон в полноэкранном режиме. Временами новое окно отображалось просто белым и причину этого, к сожалению, так и не удалось найти.
2 способ. Что если менять сцены в одном окне? Этот способ, хоть и работает, но имеет одну неприятную особенность — при смене сцены происходит некрасивое переключение — быстрое, но не приятное. Насколько удалось заметить, в этот момент окно просто исчезает на мгновение и потом появляется уже с новой сценой.
Готовый файлы можно скачать отсюда (VAnavigation)
- минимум программного кода чтобы открыть новый экран
- возможность перемещаться по экранам назад
- передать данные между окнами
Вариантов реализовать подобный инструмент несколько. Почему выбран именно этот — об этом ниже.
Для тех, кто привык воспринимать через схемы:
Каждый экран должен реализовывать интерфейс Controller (Controller.java).
public interface Controller {
Node getView();
void setView(Node view);
void Show();
}
Изучение JavaFX, начинается с рассказа о том, что бывает файл разметки FXML и сопровождающий его класс контроллера на языке Java. Реализация нашего интерфейса, позволит задать (setView) и получить(getView) корневой элемент разметки файла FXML. Метод Show — отобразит содержимое fxml файла.
Далее, базовый для всех наших экранов класс BaseController (BaseController.java)
public class BaseController implements Controller {
private Node view;
@Override
public Node getView() {
return view;
}
@Override
public void setView (Node view){
this.view = view;
}
@Override
public void Show() {
PreShowing();
Main.getNavigation().Show(this);
PostShowing();
}
public void PreShowing()
{
}
public void PostShowing()
{
}
}
Этот класс и берет на себя реализацию интерфейса Controller. Методы PreShowing() и PostShowing() дадут нам возможность сделать что-либо на нашем экране перед открытием или после открытия.Каждый контроллер экрана, необходимо наследовать от BaseController:
public class View1 extends BaseController implements Initializable {
...
}
А теперь о том как работает навигация — файл Navigation.java:
public class Navigation {
private final Stage stage;
private final Scene scene;
private List<Controller> controllers = new ArrayList<>();
public Navigation(Stage stage)
{
this.stage = stage;
scene = new Scene(new Pane());
stage.setScene(scene);
}
public Controller load(String sUrl)
{
try {
//loads the fxml file
FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource(sUrl));
Node root = fxmlLoader.load();
Controller controller = fxmlLoader.getController();
controller.setView(root);
return controller;
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
public void Show(Controller controller)
{
try {
scene.setRoot((Parent) controller.getView());
controllers.add(controller);
System.out.println("Add to history: " + controller.toString() + ". Total scenes: " + controllers.size());
}
catch(Exception e) {
e.printStackTrace();
}
}
public void GoBack()
{
if (controllers.size() > 1)
{
controllers.remove(controllers.get(controllers.size() - 1));
scene.setRoot((Parent) controllers.get(controllers.size() - 1).getView());
}
System.out.println("GoBack: " + controllers.get(controllers.size() - 1).toString() + ". Total scenes: " + controllers.size());
}
public void ClearHistory()
{
while (controllers.size() > 1)
controllers.remove(0);
System.out.println("ClearHistory. Total scenes: " + controllers.size());
}
}
При создании объекта Navigation, указывается Stage — это то что принято называть окном. В JavaFX каждому Stage может быть задана Scene — сцена — область внутри Stage. В этой области будет рисоваться содержимое файла разметки fxml. В нашем случае, переключение между экранами происходит на одной сцене — будем подменять то, что должно отображаться на сцене (в окне).Чтобы возвращаться назад, нам понадобиться история переходов по окнам. Используем для этого список:
private List<Controller> controllers = new ArrayList<>();Когда нам нужно будет сменить экран, нам останется сделать 2 шага:
- вызвать метод load, чтобы загрузить файл разметки FXML
- вызвать метод Show, который заменит содержимое сцены и добавит контроллер в список для истории.
Можно использовать класс приложения, чтобы всякий раз иметь возможность получать доступ к объекту навигации. (Main.java)
public class Main extends Application {
private static Navigation navigation;
public static Navigation getNavigation()
{
return navigation;
}
@Override
public void start(Stage primaryStage) throws Exception{
navigation = new Navigation(primaryStage);
primaryStage.setTitle("VA navigation");
primaryStage.show();
//navigate to first view
Main.getNavigation().load(View1.URL_FXML).Show();
}
public static void main(String[] args) {
launch(args);
}
}
И вот теперь в любом месте достаточно написатьMain.getNavigation().load(View1.URL_FXML).Show();и произойдет смена экрана.
Передача параметров между контроллерами.
Допустим необходимо чтобы контроллер View2 передал строку текста контроллеру View3. Для этого, объявляем в контроллере View3:
private String parameterFromView2;
public String getParameterFromView2() {
return parameterFromView2;
}
public void setParameterFromView2(String parameterFromView2) {
this.parameterFromView2 = parameterFromView2;
}
И когда будем вызвать View3 из View2 немного изменим вызов:View3 view3 = (View3)Main.getNavigation().load(View3.URL_FXML); view3.setParameterFromView2(txtParameter.getText()); view3.Show();Очень часто, получаемые в контроллерах параметры должны быть использованы для тех или иных действий перед отображением на экране. Именно для этого, в базовом классе, предусмотрена функция:
@Override
public void PreShowing() {
super.PreShowing();
lblParameter.setText(getParameterFromView2());
}
Почему именно так.
Почему именно смена содержимого сцены? Потому что опробованные 2 других способа имеют недостатки.1 способ. Если при навигации, мы бы использовали принцип: один Stage — один контроллер, то конечно бы у нас получилось все хорошо. Но что если экранов много? Это можно сравнить с просмотром некоторого сайта — когда каждая ссылка открывается в новом окне. Но при таком подходе обнаружились проблемы при отображении окон в полноэкранном режиме. Временами новое окно отображалось просто белым и причину этого, к сожалению, так и не удалось найти.
2 способ. Что если менять сцены в одном окне? Этот способ, хоть и работает, но имеет одну неприятную особенность — при смене сцены происходит некрасивое переключение — быстрое, но не приятное. Насколько удалось заметить, в этот момент окно просто исчезает на мгновение и потом появляется уже с новой сценой.
Готовый файлы можно скачать отсюда (VAnavigation)
Большое спасибо автору!
ОтветитьУдалитьРад, если получилось быть полезным!
Удалить