стиль css применяется к AppBar слишком поздно

У меня есть View, который содержит ListView. Когда представление загружается в первый раз, требуется некоторое время для создания ListCells. Чтобы пользовательский интерфейс не выглядел заблокированным, я обернул вызов listView.setItems в Platform.runLater и установил placeHolder в listView в качестве индикатора процесса загрузки.
Представления AppBar содержат TextField и несколько Buttons, которые стилизованы с помощью css. -файл. Хотя кнопки имеют правильный стиль, текстовое поле сначала появляется без примененного к нему стиля, т. е. окрашено в черный цвет, и как только ListView заполняется, стиль TextField также применяется.

В чем может быть причина такой задержки?

    @Override
    protected void onShowing() {
        appBar.setNavIcon(getBackButton());
        appBar.setTitle(searchField.getTextField());
        appBar.getActionItems().addAll(btnSort, btnFilter, moreMenu);
    }

    @Override
    protected void onShown() {
        setItems();
    }

    protected void setItems() {
        if (!firstShow) {
            lview.setItems(items);

        } else {
            if (!items.isEmpty()) {
                lview.setPlaceholder(new Label("loading data..."));
                Platform.runLater(() -> lview.setItems(items));
            }
            firstShow = false;
        }
    }

ИЗМЕНИТЬ:

Только -fx-text-fill применяется после задержки. Все остальные стили применяются немедленно.

.app-bar > .title-box > .text-field{ 
    -fx-border-width: 0.0; 
    -fx-border-insets: 0.0;
    -fx-padding: 0.0;
    -fx-font-family: 'Roboto Medium';
    -fx-font-size: 20.0;
    -fx-text-fill: -app-bar-item-color;
    -fx-prompt-text-fill: #59819C;
}

.app-bar > .action-items .button{
    -fx-text-fill: red;
}

Код для воспроизведения проблемы:

public class AppBarTestApp extends MobileApplication {

    private static final String ITEMS_VIEW = "items";

    @Override
    public void init() throws Exception {

        addViewFactory(HOME_VIEW, () ->
            {
                Button btnShowNextView = new Button("Show items view");
                btnShowNextView.setOnAction(e -> switchView(ITEMS_VIEW));

                return new View(HOME_VIEW, new StackPane(btnShowNextView));
            });

        addViewFactory(ITEMS_VIEW, this::loadItemsView);
    }

    @Override
    public void postInit(Scene scene) {
        scene.getStylesheets().add(getClass().getResource("/com/jns/appbartest/view/common.css").toExternalForm());
    }

    private View loadItemsView() {
        URL resource = ItemsPresenter.class.getResource("items.fxml");
        FXMLLoader fxmlLoader = new FXMLLoader(resource);

        try {
            fxmlLoader.load();

        } catch (IOException e) {
            e.printStackTrace();
        }

        return fxmlLoader.getRoot();
    }
}



public class ItemsPresenter {

    private static final MobileApplication app       = MobileApplication.getInstance();

    @FXML
    private View                           view;

    @FXML
    private ListView<String>               lview;

    private Button                         btnBack;
    private Button                         btnMoreMenu;
    private TextField                      txtSearch;

    private boolean                        firstShow = true;

    private ObservableList<String>         items = createItems();

    @FXML
    protected void initialize() {
        view.setOnShowing(e -> onShowing());
        view.setOnShown(e -> onShown());

        btnBack = MaterialDesignIcon.ARROW_BACK.button(e -> app.goHome());
        btnMoreMenu = MaterialDesignIcon.MORE_VERT.button();
        txtSearch = new TextField("Items");
        txtSearch.setPromptText("search");

        lview.setPlaceholder(new Label("no data to display"));
    }

    private void onShowing() {
        app.getAppBar().setNavIcon(btnBack);
        app.getAppBar().setTitle(txtSearch);
        app.getAppBar().getActionItems().add(btnMoreMenu);
    }

    private void onShown() {
        if (!firstShow) {
            lview.setItems(items);

        } else {
            firstShow = false;
            lview.setPlaceholder(new Label("loading data..."));

            Platform.runLater(() -> {
                try {
                    Thread.sleep(2000);  //to simulate a long lasting initial creation of listCells
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                lview.setItems(items);
            });
        }
    }

    private ObservableList<String> createItems() {
        ObservableList<String> items = FXCollections.observableArrayList();
        String name = "Person #";

        for (int i = 0; i < 20; i++) {
            items.add(name + i);
        }

        return items;
    }
}


<View fx:id="view" id="itemsView" xmlns="http://javafx.com/javafx/8.0.60" xmlns:fx="http://javafx.com/fxml/1"
    fx:controller="com.jns.appbartest.view.ItemsPresenter">
    <center>
        <MobileLayoutPane fx:id="pneListView">
            <center>
                <ListView fx:id="lview" />
            </center>
        </MobileLayoutPane>
    </center>
</View>

person jns    schedule 27.06.2017    source источник
comment
Я не могу воспроизвести вашу проблему. Я создал простой тест с помощью ListView, и я вижу текст текстового поля или текст подсказки, оформленный правильно, до того, как элементы появятся.   -  person José Pereda    schedule 27.06.2017
comment
Я добавил код для воспроизведения проблемы   -  person jns    schedule 28.06.2017


Ответы (1)


С опубликованным кодом я могу воспроизвести проблему на Android.

Очевидно, я принимаю эту часть:

private void onShown() {
    lview.setPlaceholder(new Label("loading data..."));
    Platform.runLater(() -> {
        try {
            Thread.sleep(2000);  //to simulate a long lasting initial creation of listCells
        } catch (InterruptedException e) { }
        lview.setItems(items);
    });
}

добавляется для имитации первоначального создания ячеек.

Но именно он показывает, с какой проблемой вы столкнулись: фиктивный образец показывает, что в потоке JavaFX (Platform.runLater) вы его блокируете (Thread.sleep(2000)). В это время текст textField черный, и только позже он стилизуется.

Если вы измените этот фрагмент на этот:

private void onShown() {
    lview.setPlaceholder(new Label("loading data..."));
    new Thread(new Task<Void>() {
        @Override
        protected Void call() throws Exception {
            try {
                Thread.sleep(2000);  //to simulate a long lasting initial creation of listCells
            } catch (InterruptedException e) { }
            return null;
        }

        @Override
        protected void succeeded() {
            lview.setItems(items);
        }
    }).start();
}

теперь тяжелая задача находится за пределами потока JavaFX, и вы можете видеть, что стиль textField сразу же.

Вернемся к вашей реальной проблеме: урок здесь может заключаться в том, что вы должны выходить из потока JavaFX для каждой тяжелой задачи и только в самый последний момент вызывать ее. Task — хороший механизм для этого.

person José Pereda    schedule 27.06.2017
comment
В моем приложении я извлекаю элементы из кеша, который инициализируется при запуске приложения, пока отображается предварительный загрузчик. Кажется, что пользовательский интерфейс заблокирован на некоторое время, когда создаются ListCells. Мне удалось повысить скорость первоначального заполнения ListView, установив для него fixedCellSize, однако заметная задержка остается. Даже с фиктивным примером, как возможно, что к кнопкам применяется css, а стиль TextField создается позже? - person jns; 28.06.2017
comment
Если вы запустите его на рабочем столе, вы даже не увидите ни одного узла на панели приложений, поэтому пользовательский интерфейс полностью заморожен. И это ожидаемый результат. На Android кажется, что для начала блокировки пользовательского интерфейса требуется больше времени, и за это короткое время узлы добавляются и частично стилизуются. Имея это в виду, если бы вы могли немного отложить создание ячейки, вы правильно визуализировали бы представление, хотя для завершения его полной загрузки потребуется больше времени. - person José Pereda; 28.06.2017
comment
Если я запускаю его на рабочем столе, все выглядит нормально. AppBar показывает все узлы в соответствующем стиле. Для быстрого теста я использовал PauseTransition для задержки создания ячейки, что решило проблему. Спасибо, Хосе. Мне только интересно, какова была бы правильная продолжительность задержки? - person jns; 28.06.2017
comment
Я добавил Listener к fillProperty узлам AppBar, что подтвердило то, что вы указали в своем комментарии. Узлы частично стилизованы, пока не будут установлены элементы listView, что прерывает стилизацию остальных узлов AppBar. В качестве обходного пути я добавляю узлы AppBar в методе инициализации Presenters в отбрасываемую сцену и вызываю applyCss до того, как представление будет показано в первый раз. - person jns; 28.06.2017