Как исправить ошибку рендеринга в javafx (ComboBox, ListView) - PullRequest
2 голосов
/ 22 сентября 2019

В довольно сложном приложении javafx я столкнулся с возможной ошибкой рендеринга.Последние два дня я мог отследить это до следующего простого приложения.Следующий SSCCE демонстрирует, что при определенных обстоятельствах некоторые компоненты javafx отображаются неправильно.В результате ComboBox и ListView не показывают измененное содержимое:

import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.control.TabPane.TabClosingPolicy;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class ComboManagedBug extends Application {
  public static void main(String[] args) {
    launch(args);
  }

  public void start(Stage primaryStage) {
    // add combo box
    ComboBox<String> combo = new ComboBox<>();
    combo.setPromptText("Choose a value...");
    combo.getItems().setAll("1", "2", "3");

    // add list view
    ListView<Label> list = new ListView<>();

    // add "add" button
    Button add = new Button("Add");
    add.setOnAction(e -> list.getItems().add(
      new Label(combo.getSelectionModel().getSelectedItem())));

    // add tab pane
    Tab tab1 = new Tab("First", new VBox(combo, add, list));
    Tab tab2 = new Tab("Second");
    TabPane tabs = new TabPane(tab1, tab2);
    tabs.setTabClosingPolicy(TabClosingPolicy.UNAVAILABLE); // important!

    // add "next" and "cancel" buttons at bottom
    Button next = new Button("Next");
    Button cancel = new Button("Cancel (Triggers refresh)");
    HBox buttons = new HBox(next, cancel);

    // install tab listener
    tabs.getSelectionModel().selectedItemProperty().addListener((a, b, c) -> {
      // intention is to show next button only on first tab
      boolean firstTab = c == tab1;
      next.setVisible(firstTab);
      next.setManaged(firstTab); // important!
    });

    // show
    VBox root = new VBox(new VBox(tabs, buttons)); // important!
    Scene scene = new Scene(root, 400, 400);
    primaryStage.setTitle("ComboBox/ListView Rendering Bug Demo");
    primaryStage.setScene(scene);
    primaryStage.show();
  }
}

Шаги для воспроизведения:

  1. Запустите программу
  2. Выберитекомбинированное значение -> ComboBox показывает выбранное значение -> OK
  3. Нажмите «Добавить» -> значение добавляется в ListView и отображается -> OK
  4. Активировать вторую вкладку
  5. Активировать первую вкладку
  6. Выберите значение комбо, отличное от предыдущего -> значение в ComboBox не изменится -> BUG
  7. Нажмите «Добавить» -> Содержимое ListView не изменяется -> BUG
  8. Нажмите «Отмена» внизу ->, хотя к этой кнопке не был добавлен прослушиватель, и ComboBox, и ListView теперь показывают правильные значения.Таким образом, оба контейнера, похоже, содержат правильные значения, но они не будут отображаться правильно до тех пор, пока не будет запущено обновление пользовательского интерфейса.

Обратите внимание, что есть три важных требования для воспроизведения этой ошибки (помечено как "важный""):

  1. Переключение управляемого состояния кнопки« Далее »при переключении вкладок, чтобы скрыть эту кнопку (обычный способ скрыть узел вместе с видимым состоянием узла, см. JavaFX -setVisible не «скрывает» элемент )
  2. TabClosingPolicy.UNAVAILABLE (очень распространенный случай)
  3. a VBox в VBox (что может легко произойти в реальной жизни при вложении различных узлов компонентов javafx).

Известна ли эта ошибка, и знает ли кто-нибудь обходной путь для этого?Я пробовал Platform.runLater (cancel.fire ()) и подобные вещи, но безуспешно.

Спасибо за любые подсказки, Питер.

Кстати, кроме этого, наша компания уже несколько лет использует javafx.По нашему опыту, это очень надежно, а программирование на javafx - это весело.Я надеюсь, что есть простое решение для нашей проблемы:)

1 Ответ

1 голос
/ 23 сентября 2019

Как обсуждалось в комментариях к исходному сообщению, ошибка возникает не в JavaFX 8 (Oracle), а в более поздних версиях (OpenJFX).Я нашел следующий обходной путь:

public static void fixTabRendering(TabPane tabs) {
    if (tabs.getTabClosingPolicy() != TabClosingPolicy.UNAVAILABLE) return;
    tabs.setTabClosingPolicy(TabClosingPolicy.SELECTED_TAB);
    for (Node node : tabs.lookupAll(".tab-close-button")) {
        // hide "close" button to imitate TabClosingPolicy.UNAVAILABLE
        node.setStyle("-fx-background-color:transparent;-fx-shape:null;-fx-pref-width:0.001");
    }
}

Этот код должен быть запущен после показа этапа (в противном случае lookupAll () возвращает null ), а также после добавления вкладокна панель вкладок.Последнее может быть достигнуто через прослушиватель табуляции:

tabs.getTabs().addListener((Change<?> change) ->
    Platform.runLater(() -> fixTabRendering(tabs)));

Platform.runLater () требуется, поскольку в противном случае lookupAll () может не возвращать узлыдобавлены вкладки.

Может быть, это решение кому-нибудь поможет:)

...