Альтернатива удаленному impl_isTreeVisible()

Мы полагаемся на Node.impl_isTreeVisible(), потому что isVisible не работает должным образом (или, по крайней мере, так, как мы этого хотим).

/**
 * @treatAsPrivate implementation detail
 * @deprecated This is an internal API that is not intended for use and will be removed in the next version
 */
@Deprecated
public final boolean impl_isTreeVisible() {
    return impl_treeVisibleProperty().get();
}

У нас есть пользовательский узел, который содержит Plot. Это получает непрерывные данные. Мы хотим избежать обновления графика, если он не виден (все еще управляется/рендерится, но скрыт). Если узел помещен на вкладку, которая не выбрана, поэтому он не виден в окне, то использование isVisible все равно возвращает значение true. Это приводит к тому, что узел на выбранной вкладке отображается каждый раз при обновлении графика.

Это будет оцениваться как true, даже если узел не виден в окне приложения.

if (isVisible()) {
    updatePlot()
}

Поэтому мы использовали следующее, которое работает так, как мы этого хотим.

if (impl_isTreeVisible()) {
    updatePlot()
}

Однако это больше не будет работать в Java 9, поскольку такие методы удалены. Есть ли новый подход к этому в Java 9?

Обновление: просмотрев исходный код Java 9 для javafx.scene.Node, я обнаружил метод isTreeVisible(), который выглядит как замена impl_isTreeVisible. Однако, глядя на Javadoc, я не могу найти это isTreeVisible(). http://download.java.net/java/jdk9/docs/api/javafx/scene/Node.html

Попытка использования примера с использованием isTreeVisible() не будет компилироваться с Java 9

Java9AppTest.java:50: error: cannot find symbol
                    if (text1.isTreeVisible()) {
                         ^
  symbol:   method isTreeVisible()
  location: variable text1 of type Text

Update2: сначала не удалось увидеть, что isTreeVisible() является частным пакетом.

Update3. Взглянув еще раз на исходный код Node, я начал проверять NodeHelper, можно ли использовать его для получения isTreeVisible(), однако пакет NodeHelper не виден. Хотя использование --add-exports для com.sun.javafx.scene для получения доступа к NodeHelper работает.

--add-exports javafx.graphics/com.sun.javafx.scene=ALL-UNNAMED

Затем я могу прочитать состояние isTreeVisible() узла.

final boolean isTreeVisible = NodeHelper.isTreeVisible(node);

Пример кода

Содержит две вкладки, каждая со своим текстом. Имеет задачу, которая обновляет каждый текст. Использование isVisible() обновит каждый текст на обеих вкладках. Использование impl_isTreeVisible() будет обновлять только тот текст, который действительно виден. Имеет смысл обновлять текст, даже если он не виден. Это просто для иллюстрации проблемы. Замените текст фоновым процессом, который выполняет гораздо большую нагрузку на процессор.

import javafx.application.Application;
import javafx.concurrent.Task;
import javafx.scene.Scene;
import javafx.scene.control.Tab;
import javafx.scene.control.TabPane;
import javafx.scene.layout.VBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;

public class Java9AppTest extends Application {

    private Text text1, text2;

    public static void main(String[] args) {
        Java9AppTest.launch(args);
    }

    public void start(Stage stage) throws Exception {
        TabPane root = new TabPane();

        VBox box1 = new VBox();
        text1 = new Text();
        text1.setText("Hello World!");
        text1.textProperty().addListener((observable, oldValue,     newValue) -> {
            System.out.println("text1 changed from " + oldValue + " to " + newValue);
        });
        box1.getChildren().addAll(text1);

        Tab tab1 = new Tab("Tab 1");
        tab1.setContent(box1);

        VBox box2 = new VBox();
        text2 = new Text();
        text2.setText("Another Hello World!");
        text2.textProperty().addListener((observable, oldValue, newValue) -> {
            System.out.println("text2 changed from " + oldValue + " to " + newValue);
        });
        box2.getChildren().add(text2);

        Tab tab2 = new Tab("Tab 2");
        tab2.setContent(box2);

        root.getTabs().addAll(tab1, tab2);

        Task<Void> task = new Task<Void>() {
            /* (non-Javadoc)
             * @see javafx.concurrent.Task#call()
             */
            @Override
            protected Void call() throws Exception {
                final String oldText = "Hello World!";
                final String newText = "New Hello World!";
                while (true) {
                    if (text1.isVisible()) {
                        if (text1.getText().equals(oldText)) {
                            text1.setText(newText);
                        } else {
                            text1.setText(oldText);
                        }
                    }

                    if (text2.isVisible()) {
                        if (text2.getText().equals(oldText)) {
                            text2.setText(newText);
                        } else {
                            text2.setText(oldText);
                        }
                    }

                    Thread.sleep(2000);
                }
            }

        };

        stage.setScene(new Scene(root));
        stage.setWidth(200);
        stage.setHeight(200);
        stage.setTitle("JavaFX 9 Application");
        stage.show();

        Thread thread = new Thread(task, "Task");
        thread.start();
    }

}

person DJViking    schedule 10.05.2017    source источник
comment
В Java 9 для этого нет нового подхода: на самом деле в Java 8 нет старого подхода для этого. Предполагается, что комментарий @treatAsPrivate подразумевает, что вы должны рассматривать это как частный метод: т. е. его не следует вызывать из подкласса. (По сути, команда JavaFX хотела сделать это приватным, но не могла этого сделать без реализации проекта Jigsaw, который был перенесен с Java 8 на 9.) Заменой public final boolean impl_isTreeVisible() в Java 9 является пакетно-приватный метод final boolean isTreeVisible().   -  person James_D    schedule 10.05.2017
comment
Так что в основном это звучит так, как будто вы делаете что-то совсем не так, как задумано. Можете ли вы опубликовать простой пример того, где, по вашему мнению, вам это нужно? Как правило, в тех редких случаях, когда мне нужен пользовательский компонент, я создаю подкласс Control и пишу скин, который является подклассом SkinBase, переопределяя layoutChildren(). AIUI, layoutChildren() будет вызываться только при необходимости.   -  person James_D    schedule 10.05.2017
comment
Обновление нашего сюжета, который находится в другом месте. Он может быть размещен на вкладке или может быть скрыт на другом узле. Дело в том, что он скрыт, но isVisible возвращает true. Если мы обновим этот график, это приведет к тому, что видимые узлы будут отображаться каждый раз. Использование isTreeVisible возвращает false, если узел Plot действительно не виден. Возможно, наша реализация Plot неверна, поскольку она приводит к тому, что все другие узлы в других местах отображаются при его обновлении.   -  person DJViking    schedule 11.05.2017
comment
Да, я все это понял. Как вы на самом деле реализуете это? Можете ли вы привести полный, но простой пример?   -  person James_D    schedule 11.05.2017
comment
Похоже, что в Java 9 есть isTreeVisible() при чтении исходного кода, но его нет в Javadoc.   -  person DJViking    schedule 09.10.2017
comment
Поскольку этот метод является пакетным.   -  person James_D    schedule 09.10.2017
comment
@James_D: Да, ты прав. Сначала я не увидел, что это приватный пакет.   -  person DJViking    schedule 09.10.2017
comment
Вопрос, связанный с моим здесь.   -  person DJViking    schedule 09.10.2017
comment
несвязанный: вы не должны изменять какой-либо узел в сцене вне потока fx-приложения .. странно, что Text не выдает (Label делает)   -  person kleopatra    schedule 10.10.2017
comment
вы можете поднять вопрос о перемещении isTreeVisible в общедоступную область в параде ошибок - вариант использования должен быть достаточно распространенным - незаконный доступ будет удален в будущих версиях (не уверен, когда, планировалось 10, но это было до выпуска jdk стратегия изменена на повременную, то есть каждые полгода)   -  person kleopatra    schedule 10.10.2017


Ответы (2)


Я предлагаю добавить свойство к вашему узлу, которое контролирует, хотите ли вы обновить график. Так что вместо if (impl_isTreeVisible()) { просто есть if (shouldUpdate) {. При изменении выбора вкладки просто переключите свойство. Так что, по сути, ваш TabPane будет контролировать, будет ли обновляться сюжет.

В качестве альтернативы вы можете передать TabPane своему узлу и запросить выбранную вкладку: tabPane.getSelectionModel().getSelectedIndex(). Однако это означает, что ваш узел должен знать, на какой вкладке он находится.

person Community    schedule 10.05.2017
comment
Непригодный. Этот хак возможен, но узел может быть либо на вкладке, либо в свернутом окне. - person DJViking; 09.10.2017

  • Вкладка имеет свойство selected, привяжите это свойство к свойству update вашего графика, которое определяет, перерисовываете ли вы свой график.
  • В вашем элементе управления (или его скине) добавьте прослушиватель к свойству update графика, где вы приостанавливаете или возобновляете прослушивание вашего источника ввода или приостанавливаете или возобновляете таймер, который получает данные.

Это решение не добавляет дополнительных зависимостей к графу объектов, типу контейнера, в котором он должен находиться, и позволяет при необходимости создавать более сложные привязки (например, кнопку паузы) и упрощает тестирование, поскольку это свойство контролируется автономно.

В зависимости от реализации источника данных это решение также может приостановить ваш источник данных, если оно определит, что нет прослушивателей, активно обрабатывающих ваши данные.

person M. le Rutte    schedule 11.05.2017