Обновление тега f xml при изменении состояния - PullRequest
0 голосов
/ 08 марта 2020

Я хочу создать какой-то тег 'security' f xml, который отключает / делает невидимыми его дочерние элементы в зависимости от состояния некоторого класса SecurityManager.

У меня есть следующие трудности. Когда состояние класса SecurityManager изменяется, я хочу, чтобы все securityTags обновили свое видимое свойство. Конечно, я могу при каждом вызове конструктора тега добавлять все узлы SecurityTag в список stati c и l oop поверх него, когда класс SecurityManger изменяет состояние. Но тогда, что, если узел тега безопасности будет удален из родительского? Как мне избавиться от этого в списке? Или, может быть, есть лучший способ справиться с этим?

public class SecurityTag extends Pane {

    public Security() {
        super();
        this.setVisible(false);
    }

}
public class SecurityManager {

    private boolean authorized;

    public SecurityManager() {
        this.authorized = false;
    }

    public void login() {
        this.authorized = true;
    }

    public void logout() {
        this.authorized = false;
    }

    public boolean isAuthorized() {
        return authorized;
    }

}

1 Ответ

0 голосов
/ 08 марта 2020

Самый простой способ сделать это - сделать свойство authorized в вашем SecurityManager свойстве JavaFX:

package org.jamesd.examples.security;

import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;

public class SecurityManager {

    private final ReadOnlyBooleanWrapper authorized ;

    public SecurityManager() {
        this.authorized = new ReadOnlyBooleanWrapper(false) ;
    }



    public void login() {
        this.authorized.set(true);
    }

    public void logout() {
        this.authorized.set(false);
    }

    public ReadOnlyBooleanProperty authorizedProperty() {
        return authorized.getReadOnlyProperty();
    }

    public boolean isAuthorized() {
        return authorizedProperty().get();
    }

}

Теперь вы можете просто привязать соответствующие свойства к свойству authorized из SecurityManager. В зависимости от свойства, которое вы связываете, вы можете сделать это непосредственно в F XML или в контроллере. Вы можете сделать экземпляр SecurityManager доступным для файла F XML, поместив его в namespace FXMLLoader и сделать его доступным для контроллера, просто передав его в качестве параметра конструктору контроллера, и установка контроллера вручную (т.е. без использования атрибута fx:controller) для FXMLLoader.

Вот пример файла F XML. Обратите внимание, как кнопка «Привилегированное действие» связывает ее видимость с менеджером безопасности с помощью

visible = "${securityManager.authorized}" 

. Вы также можете сделать

disable = "${ !securityManager.authorized}" 

, если вы предпочитаете просто отключить ее.

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets ?>
<?import javafx.scene.layout.BorderPane ?>
<?import javafx.scene.layout.VBox ?>
<?import javafx.scene.control.Button ?>
<?import javafx.scene.control.Label ?>

<BorderPane xmlns="http://javafx.com/javafx/11.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <top>
        <Label fx:id = "securityStatus"></Label>
    </top>
    <center>
        <VBox spacing="5" fillWidth="true">
            <Button text="Regular Action" maxWidth="Infinity"></Button>
            <Button text="Privileged Action" visible = "${securityManager.authorized}" maxWidth="Infinity"></Button>

            <padding>
                <Insets top="5" left="5" right="5" bottom="5"/>
            </padding>
        </VBox>
    </center>
    <left>
        <VBox spacing="5" fillWidth="true">
            <Button text="login" onAction="#login" maxWidth="Infinity"/>
            <Button text="logout" onAction="#logout" maxWidth="Infinity"/>

            <padding>
                <Insets top="5" left="5" right="5" bottom="5"/>
            </padding>
        </VBox>
    </left>
</BorderPane>

Вот контроллер. Текст метки привязан к состоянию диспетчера безопасности с использованием более сложной привязки, чем это может быть достигнуто в F XML:

package org.jamesd.examples.security;

import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class SecurityController {

    private final SecurityManager securityManager ;

    @FXML
    private Label securityStatus ;

    public SecurityController(SecurityManager securityManager) {
        this.securityManager = securityManager ;
    }

    public void initialize() {
        securityStatus.textProperty().bind(Bindings
            .when(securityManager.authorizedProperty())
            .then("Logged In")
            .otherwise("Logged Out")
        );
    }

    @FXML
    private void login() {
        securityManager.login();
    }

    @FXML
    private void logout() {
        securityManager.logout();
    }
}

И, наконец, вот как все это собрано:

package org.jamesd.examples.security;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class SecurityApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        SecurityManager securityManager = new SecurityManager();

        FXMLLoader loader = new FXMLLoader(getClass().getResource("SecurityExample.fxml"));
        loader.getNamespace().put("securityManager", securityManager);
        loader.setController(new SecurityController(securityManager));

        Scene scene = new Scene(loader.load());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

Обратите внимание, что этот подход позволяет избежать ненужного создания подклассов узлов JavaFX (например, Pane), что может привести к проблемам (например, вы можете разместить зависимые от безопасности узлы в существующей панели макета, и это делает более сложным использование стандартных макетов).


Если, как предлагается в комментариях, вы хотите, чтобы класс SecurityManager был агностирован c по отношению к JavaFX (и, вероятно, настольный Java) в общем), вы можете просто создать делегат для пользовательского интерфейса, который использует свойства JavaFX, и организовать его обновление при обновлении «реального» менеджера безопасности.

Например, вот SecurityManager, который реализует Классическая схема «слушателя»:

package org.jamesd.examples.security;

@FunctionalInterface
public interface AuthorizationListener {
    void authorizationChanged(boolean newStatus);
}

и

package org.jamesd.examples.security;

import java.util.ArrayList;
import java.util.List;

public class SecurityManager  {

    private boolean authorized ;
    private final List<AuthorizationListener> listeners ;

    public SecurityManager() {
        this.listeners = new ArrayList<>();
    }

    public void login() {
        setAuthorized(true);
    }

    public void logout() {
        setAuthorized(false);
    }

    public void addListener(AuthorizationListener listener) {
        listeners.add(listener);
    }

    public void removeListener(AuthorizationListener listener) {
        listeners.remove(listener);
    }

    public boolean isAuthorized() {
        return authorized;
    }

    private void setAuthorized(boolean authorized) {
        if (! this.authorized == authorized) {
            this.authorized = authorized ;
            listeners.forEach(l -> l.authorizationChanged(authorized));
        }
    }

}

Обратите внимание, что это абсолютно агности c по отношению к любой технологии просмотра.

Теперь вы можете a создать «делегат администратора безопасности интерфейса пользователя»:

package org.jamesd.examples.security;

import javafx.beans.property.ReadOnlyBooleanProperty;
import javafx.beans.property.ReadOnlyBooleanWrapper;

public class UISecurityDelegate  {

    private final ReadOnlyBooleanWrapper authorized ;

    private final SecurityManager manager ;

    public UISecurityDelegate(SecurityManager manager) {
        this.manager = manager ;
        this.authorized = new ReadOnlyBooleanWrapper(manager.isAuthorized()) ;
        manager.addListener(authorized::set);
    }

    public void login() {
        manager.login();
    }
    public void logout() {
        manager.logout();
    }

    public ReadOnlyBooleanProperty authorizedProperty() {
        return authorized.getReadOnlyProperty();
    }

    public boolean isAuthorized() {
        return authorizedProperty().get();
    }

}

и, наконец, обновить код интерфейса пользователя с помощью

package org.jamesd.examples.security;

import javafx.beans.binding.Bindings;
import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class SecurityController {

    private final UISecurityDelegate securityManager ;

    @FXML
    private Label securityStatus ;

    public SecurityController(UISecurityDelegate securityManager) {
        this.securityManager = securityManager ;
    }

    public void initialize() {
        securityStatus.textProperty().bind(Bindings
            .when(securityManager.authorizedProperty())
            .then("Logged In")
            .otherwise("Logged Out")
        );
    }

    @FXML
    private void login() {
        securityManager.login();
    }

    @FXML
    private void logout() {
        securityManager.logout();
    }
}

и

package org.jamesd.examples.security;

import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class SecurityApp extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception {

        // probably created by data or service layer, etc:
        SecurityManager securityManager = new SecurityManager();

        UISecurityDelegate securityDelegate = new UISecurityDelegate(securityManager) ;

        FXMLLoader loader = new FXMLLoader(getClass().getResource("SecurityExample.fxml"));
        loader.getNamespace().put("securityManager", securityDelegate);
        loader.setController(new SecurityController(securityDelegate));

        Scene scene = new Scene(loader.load());
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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