Notes d'étude JavaFX

Notes sur l'étude de JavaFX

environnement

OS Windows 10

Java

>java -version
java version "1.8.0_151"
Java(TM) SE Runtime Environment (build 1.8.0_151-b12)
Java HotSpot(TM) 64-Bit Server VM (build 25.151-b12, mixed mode)

Scene Builder JavaFX Scene Builder 8.4.1

Celui développé par Gluon

Propriétés JavaFX

JavaFX utilise ** JavaFX Properties **, qui est une extension de JavaBeans, comme moyen de représenter les propriétés des objets.

La propriété JavaFX implémente la propriété de l'objet comme suit.

package sample.javafx.property;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public class MyClass {
    private DoubleProperty value = new SimpleDoubleProperty();
    
    public final double getValue() {
        return this.value.get();
    }
    
    public final void setValue(double value) {
        this.value.set(value);
    }

    public DoubleProperty valueProperty() {
        return this.value;
    }
}
  1. Définissez la propriété («valeur») avec «privé»
  1. Définir Getter et Setter --Nommez la méthode selon les mêmes règles que les JavaBeans --Le type n'est pas un type wrapper, mais la valeur du contenu est insérée et sortie
  1. Définissez une méthode appelée «propriété nom de propriété» --Cela renvoie le type de wrapper tel quel

De nombreuses classes fournies par JavaFX implémentent cette propriété JavaFX. Par exemple, les méthodes correspondant à la propriété text de la classe Label sont répertoriées ci-dessous. (La définition réelle est la classe Labeled).

Observable Les propriétés JavaFX font plus que simplement déterminer les déclarations de méthodes, elles fournissent certaines fonctionnalités spéciales introuvables dans les JavaBeans. L'un d'eux est Observable.

Les classes qui implémentent ʻObservable` offrent la possibilité de surveiller le contenu invalide.

Le DoubleProperty utilisé dans la propriété JavaFX ci-dessus hérite également de ʻObservable`.

Cet ʻObservabledéfinit une méthode appelée ʻaddListener (InvalidationListener), qui vous permet d'enregistrer un auditeur pour être averti lorsque le contenu devient invalide.

package sample.javafx.property;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty();

        a.addListener(observable -> {
            System.out.println("observable=" + observable);
        });

        a.set(1.0);
        a.set(2.0);
    }
}

Résultat d'exécution


observable=DoubleProperty [value: 1.0]
observable=DoubleProperty [value: 2.0]

L''Observable 'modifié lui-même est passé à l'auditeur en tant qu'argument.

Désactiver et modifier

Certaines classes qui implémentent ʻObservable` n'effectuent pas de calculs immédiatement lorsque des modifications de contenu sont demandées (par exemple, lorsque la méthode Setter est appelée), et ne font les calculs que la prochaine fois que le contenu est demandé. Il y a quelque chose comme (calcul du retard).

En d'autres termes, il peut y avoir une période pendant laquelle le contenu n'est pas finalisé entre la demande de modification du contenu et le calcul réel.

Si ce contenu n'est pas finalisé, il est exprimé comme ** invalide **. Ensuite, lorsque le calcul est effectivement effectué et que le contenu est confirmé, il est exprimé en ** changement **.

ʻInvalidationListener` est maintenant rappelé lorsque le contenu devient invalide.

En fonction de la classe d'implémentation, le contenu peut être calculé immédiatement, donc dans ce cas, l'invalidation et le changement se produiront en même temps (après l'invalidation, le contenu sera confirmé (changé) immédiatement).

ObservableValue

package sample.javafx.property;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty();

        a.addListener((observableValue, oldValue, newValue) -> {
            System.out.println("observableValue=" + observableValue + ", oldValue=" + oldValue + ", newValue=" + newValue);
        });
        
        a.set(1.0);
        a.set(2.0);
    }
}

Résultat d'exécution


observableValue=DoubleProperty [value: 1.0], oldValue=0.0, newValue=1.0
observableValue=DoubleProperty [value: 2.0], oldValue=1.0, newValue=2.0

L'interface ʻObservableValue](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/beans/value/ObservableValue.html) existe en tant qu'interface qui hérite de Observable. Il fournit une méthode appelée ʻaddListener (ChangeListener) , qui vous permet d'enregistrer un auditeur pour être averti lorsqu'une valeur change. L'écouteur reçoit la ʻObservableValue` modifiée elle-même et les valeurs brutes avant et après la modification.

Les classes de propriétés fournies par JavaFX, y compris DoubleProperty, implémentent ʻObservableValue, donc les écouteurs ʻInvalidationListener et ChangeListener peuvent être enregistrés.

Binding ʻIl existe Binding comme interface qui hérite de ObservableValue`.

«Binding» représente le résultat du calcul qui combine plusieurs valeurs. Les valeurs agrégées sont surveillées par "Binding" afin que les résultats soient automatiquement recalculés lorsque les valeurs changent.

package sample.javafx.property;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty(1.0);
        DoubleProperty b = new SimpleDoubleProperty(2.0);

        DoubleBinding sum = a.add(b);

        System.out.println("sum=" + sum.get());
        
        a.set(3.0);

        System.out.println("sum=" + sum.get());
    }
}

Résultat d'exécution


sum=3.0
sum=5.0

Dans cet exemple, nous créons un "DoubleBinding" qui représente la somme de deux "DoubleProperty". Si vous modifiez la valeur de l'un des «DoubleProperty» et récupérez la valeur de «DoubleBinding», le total est la valeur recalculée.

De cette manière, "Binding" fournit un mécanisme pour collecter plusieurs valeurs, surveiller les changements dans chaque valeur et mettre à jour automatiquement les résultats du calcul.

Les valeurs collectées par Binding sont appelées ** source ** ou ** dependency **. Le Binding lui-même ne limite pas le type de la source, mais en réalité il s'agit généralement de ʻObservable ou ʻObservableValue.

Calcul du retard

package sample.javafx.property;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty(1.0);
        DoubleProperty b = new SimpleDoubleProperty(2.0);

        DoubleBinding sum = a.add(b);

        System.out.println("sum=" + sum);
        System.out.println("sum.get()=" + sum.get());
        System.out.println("sum=" + sum);
        
        a.set(3.0);

        System.out.println("sum=" + sum);
        System.out.println("sum.get()=" + sum.get());
        System.out.println("sum=" + sum);
    }
}

Résultat d'exécution


sum=DoubleBinding [invalid]
sum.get()=3.0
sum=DoubleBinding [value: 3.0]

sum=DoubleBinding [invalid]
sum.get()=5.0
sum=DoubleBinding [value: 5.0]

--Toutes les classes qui implémentent Binding qui existent dans la bibliothèque standard sont retardées dans le calcul.

Si vous enregistrez ChangeListener, le délai ne sera pas calculé.

package sample.javafx.property;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty(1.0);
        DoubleProperty b = new SimpleDoubleProperty(2.0);

        DoubleBinding sum = a.add(b);
        sum.addListener((observableValue, oldValue, newValue) -> {
            System.out.println("Modifié(oldValue=" + oldValue + ", newValue=" + newValue + ")");
        });

        System.out.println("sum=" + sum);
        System.out.println("sum.get()=" + sum.get());
        System.out.println("sum=" + sum);
        
        a.set(3.0);

        System.out.println("sum=" + sum);
        System.out.println("sum.get()=" + sum.get());
        System.out.println("sum=" + sum);
    }
}

Résultat d'exécution


sum=DoubleBinding [value: 3.0]
sum.get()=3.0
sum=DoubleBinding [value: 3.0]

Modifié(oldValue=3.0, newValue=5.0)

sum=DoubleBinding [value: 5.0]
sum.get()=5.0
sum=DoubleBinding [value: 5.0]

API de haut niveau

Il existe deux types d'API pour créer des Binding: ** API de haut niveau ** et ** API de bas niveau **.

L'API de niveau supérieur est divisée en ** API Fluent ** et ** méthode d'usine de classe Bindings **.

Fluent API

package sample.javafx.property;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty(2.0);

        DoubleBinding binding = a.add(3).multiply(2).subtract(4.0).divide(3.0);

        System.out.println("((2.0 + 3) * 2 - 4.0) / 3.0 = " + binding.get());
        
        a.set(5.0);

        System.out.println("((5.0 + 3) * 2 - 4.0) / 3.0 = " + binding.get());
    }
}

Résultat d'exécution


((2.0 + 3) * 2 - 4.0) / 3.0 = 2.0
((4.0 + 3) * 2 - 4.0) / 3.0 = 4.0

javafx.png

Méthode d'usine de la classe Bindings

package sample.javafx.property;

import javafx.beans.binding.Bindings;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty(2.0);

        DoubleBinding binding =
                Bindings.divide(
                    Bindings.subtract(
                        Bindings.multiply(
                            Bindings.add(a, 3)
                            ,2
                        )
                        ,4.0
                    )
                    ,3.0
                );  

        System.out.println("((2.0 + 3) * 2 - 4.0) / 3.0 = " + binding.get());
        
        a.set(5.0);

        System.out.println("((5.0 + 3) * 2 - 4.0) / 3.0 = " + binding.get());
    }
}

Résultat d'exécution


((2.0 + 3) * 2 - 4.0) / 3.0 = 2.0
((5.0 + 3) * 2 - 4.0) / 3.0 = 4.0

-La classe Bindings a un grand nombre de méthodes d'usine «Binding». A été

API de bas niveau

MyBinding.java


package sample.javafx.property;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;

public class MyBinding extends DoubleBinding {
    private final DoubleProperty source;

    public MyBinding(DoubleProperty source) {
        this.bind(source);
        this.source = source;
    }

    @Override
    protected double computeValue() {
        return ((this.source.get() + 3) * 2 - 4.0) / 3.0;
    }
}
package sample.javafx.property;

import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty(2.0);
        
        DoubleBinding binding = new MyBinding(a);

        System.out.println("((2.0 + 3) * 2 - 4.0) / 3.0 = " + binding.get());
        
        a.set(5.0);

        System.out.println("((5.0 + 3) * 2 - 4.0) / 3.0 = " + binding.get());
    }
}

Résultat d'exécution


((2.0 + 3) * 2 - 4.0) / 3.0 = 2.0
((5.0 + 3) * 2 - 4.0) / 3.0 = 4.0

Associer une propriété à la liaison

package sample.javafx.property;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

public class Main {
    public static void main(String[] args) {
        DoubleProperty a = new SimpleDoubleProperty();
        DoubleProperty b = new SimpleDoubleProperty();
        
        System.out.println("a=" + a + ", b=" + b);

        a.bind(b);

        System.out.println("a=" + a + ", b=" + b);
        
        b.set(3.0);

        System.out.println("a=" + a + ", b=" + b);
        System.out.println("a=" + a.get() + ", b=" + b);
    }
}

Résultat d'exécution


a=DoubleProperty [value: 0.0], b=DoubleProperty [value: 0.0]
a=DoubleProperty [bound, invalid], b=DoubleProperty [value: 0.0]
a=DoubleProperty [bound, invalid], b=DoubleProperty [value: 3.0]
a=3.0, b=DoubleProperty [value: 3.0]

--Si vous utilisez la méthode bind (ObservalbeValue) définie dans Property , La liaison unidirectionnelle peut être réalisée --Il surveille maintenant la valeur de ʻObservableValue` passée en argument, et lorsque la valeur change, il change sa propre valeur pour la même valeur que la cible surveillée.

Propriétés en lecture seule

MyClass.java


package sample.javafx.property;

import javafx.beans.property.ReadOnlyDoubleProperty;
import javafx.beans.property.ReadOnlyDoubleWrapper;

public class MyClass {
    private ReadOnlyDoubleWrapper value = new ReadOnlyDoubleWrapper();
    
    public final double getValue() {
        return this.value.getValue();
    }
    
    public final ReadOnlyDoubleProperty valueProperty() {
        return this.value.getReadOnlyProperty();
    }
    
    public void updateValue() {
        this.value.set(99.9);
    }
}

Main.java


package sample.javafx.property;

public class Main {
    public static void main(String[] args) {
        MyClass myClass = new MyClass();

        System.out.println(myClass.valueProperty());
        
        myClass.updateValue();

        System.out.println(myClass.valueProperty());
    }
}

Résultat d'exécution


ReadOnlyDoubleProperty [value: 0.0]
ReadOnlyDoubleProperty [value: 99.9]

--Si vous publiez DoubleProperty tel quel, vous pouvez réécrire librement la valeur à la destination de publication. --Si vous souhaitez empêcher la modification de la valeur à la destination de publication, ne publiez pas le setter et définissez le type de la propriété utilisée en interne sur le type ReadOnly ** Wrapper.

Liaison bidirectionnelle

javafx.jpg

package sample.javafx;

import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private TextField textField;
    
    private StringProperty value = new SimpleStringProperty();

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.textField.textProperty().bindBidirectional(this.value);
    }
    
    @FXML
    public void checkValue() {
        System.out.println("value=" + value.getValue());
    }
    
    @FXML
    public void resetValue() {
        this.value.set("reset!!");
    }
}

** Résultat d'exécution **

javafx.gif

Liaison bidirectionnelle pour StringProperty

La méthode de liaison bidirectionnelle fournie pour «Property » ne peut être liée que de manière bidirectionnelle à une propriété du même type «Property ».

D'autre part, StringProperty a les deux méthodes suivantes pour la liaison bidirectionnelle. A été ajouté.

Spécification de format

package sample.javafx;

import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;

import java.net.URL;
import java.text.DecimalFormat;
import java.text.Format;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private TextField textField;
    
    private DoubleProperty value = new SimpleDoubleProperty();

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Format format = new DecimalFormat("#,##0.00");
        this.textField.textProperty().bindBidirectional(this.value, format);
    }
    
    @FXML
    public void checkValue() {
        System.out.println("value=" + value.getValue());
    }
    
    @FXML
    public void resetValue() {
        this.value.set(9876.54);
    }
}

** Résultat d'exécution **

javafx.gif

Désignation du convertisseur

package sample.javafx;

import javafx.beans.property.ObjectProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.TextField;
import javafx.util.StringConverter;

import java.net.URL;
import java.time.DateTimeException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private TextField textField;
    
    private ObjectProperty<LocalDate> value = new SimpleObjectProperty<>(LocalDate.of(2017, 1, 1));

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("uuuu/MM/dd");
        
        this.textField.textProperty().bindBidirectional(this.value, new StringConverter<LocalDate>() {
            @Override
            public String toString(LocalDate date) {
                if (date == null) {
                    return "";
                }
                
                try {
                    return formatter.format(date);
                } catch (DateTimeException e) {
                    return "";
                }
            }

            @Override
            public LocalDate fromString(String text) {
                try {
                    return LocalDate.parse(text, formatter);
                } catch (DateTimeParseException e) {
                    return null;
                }
            }
        });
    }
    
    @FXML
    public void checkValue() {
        System.out.println("value=" + value.getValue());
    }
    
    @FXML
    public void resetValue() {
        this.value.set(LocalDate.of(2017, 1, 1));
    }
}

** Résultat d'exécution **

javafx.gif

-En spécifiant StringConverter, toute valeur et «StringProperty» sont bidirectionnels. Peut être lié à --Deux méthodes abstraites sont définies dans StringConverter --String toString (T) : Convertit l'objet T en String --T fromString (String) : Convertit String en objet T

collection

JavaFX fournit sa propre classe de collection, qui est une extension de la collection standard (List ou Map).

Liste contrôlable

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;

public class Main {
    public static void main(String[] args) {
        ObservableList<String> list = FXCollections.observableArrayList("fizz", "buzz");
        
        list.addListener((Change<? extends String> change) -> {
            System.out.println("la liste a changé changer=" + change);
        });
        
        list.add("foo");
        list.remove("buzz");
        FXCollections.sort(list);
    }
}

Résultat d'exécution


la liste a changé changer={ [foo] added at 2 }
la liste a changé changer={ [buzz] removed at 1 }
la liste a changé changer={ permutated by [0, 1] }

Vérifiez les changements

Lorsque la liste change, l'écouteur reçoit un objet Change.

L'objet Change contient des informations sur la manière dont la liste a été modifiée.

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;

public class Main {
    public static void main(String[] args) {
        ObservableList<String> list = FXCollections.observableArrayList("one", "two", "three");
        
        list.addListener((Change<? extends String> change) -> {
            System.out.println("==========================================");
            while (change.next()) {
                System.out.println("list=" + change.getList());
                if (change.wasAdded()) {
                    System.out.println("ajouter à: " + change);
                }
                if (change.wasRemoved()) {
                    System.out.println("Effacer: " + change);
                }
                if (change.wasPermutated()) {
                    System.out.println("Change l'ordre: " + change);
                }
                if (change.wasReplaced()) {
                    System.out.println("Remplacement: " + change);
                }
            }
        });

        list.add("FOO");
        list.remove("one");
        FXCollections.sort(list);
        list.set(1, "hoge");
    }
}

Résultat d'exécution


==========================================
list=[one, two, three, FOO]
ajouter à: { [FOO] added at 3 }
==========================================
list=[two, three, FOO]
Effacer: { [one] removed at 0 }
==========================================
list=[FOO, three, two]
Change l'ordre: { permutated by [2, 1, 0] }
==========================================
list=[FOO, hoge, two]
ajouter à: { [three] replaced by [hoge] at 1 }
Effacer: { [three] replaced by [hoge] at 1 }
Remplacement: { [three] replaced by [hoge] at 1 }

Vérifiez les éléments ajoutés

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        ObservableList<String> list = FXCollections.observableArrayList("one", "two", "three");

        list.addListener((Change<? extends String> change) -> {
            System.out.println("==========================================");
            while (change.next()) {
                System.out.println("list=" + change.getList());
                System.out.println("added=" + change.wasAdded());
                System.out.println("from=" + change.getFrom());
                System.out.println("to=" + change.getTo());
                System.out.println("addedSubList=" + change.getAddedSubList());
                System.out.println("addedSize=" + change.getAddedSize());
            }
        });

        list.add("FOO");
        list.addAll(Arrays.asList("hoge", "fuga"));
    }
}

Résultat d'exécution


==========================================
list=[one, two, three, FOO]
added=true
from=3
to=4
addedSubList=[FOO]
addedSize=1
==========================================
list=[one, two, three, FOO, hoge, fuga]
added=true
from=4
to=6
addedSubList=[hoge, fuga]
addedSize=2

--Lorsqu'un élément est ajouté, wasAdded () renvoie true

Rechercher les éléments supprimés

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;

public class Main {
    public static void main(String[] args) {
        ObservableList<String> list
            = FXCollections.observableArrayList(
                    "one", "two", "three", "four", "five", "six", "seven");
        
        list.addListener((Change<? extends String> change) -> {
            System.out.println("==========================================");
            System.out.println("list=" + change.getList());
            while (change.next()) {
                System.out.println("----------------------------------");
                System.out.println("removed=" + change.wasRemoved());
                System.out.println("from=" + change.getFrom());
                System.out.println("to=" + change.getTo());
                System.out.println("removed=" + change.getRemoved());
                System.out.println("removedSize=" + change.getRemovedSize());
            }
        });

        list.remove("three");
        list.removeAll("two", "seven");
        list.retainAll("one", "six");
    }
}

Résultat d'exécution


==========================================
list=[one, two, four, five, six, seven]
----------------------------------
removed=true
from=2
to=2
removed=[three]
removedSize=1
==========================================
list=[one, four, five, six]
----------------------------------
removed=true
from=1
to=1
removed=[two]
removedSize=1
----------------------------------
removed=true
from=4
to=4
removed=[seven]
removedSize=1
==========================================
list=[one, six]
----------------------------------
removed=true
from=1
to=1
removed=[four, five]
removedSize=2

--wasRemoved () retourne true lorsque l'élément est supprimé

Vérifier les changements dans l'ordre de tri

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;

public class Main {
    public static void main(String[] args) {
        ObservableList<String> list
            = FXCollections.observableArrayList("aaa", "ccc", "bbb", "eee", "ddd", "fff");
        
        list.addListener((Change<? extends String> change) -> {
            System.out.println("==========================================");
            System.out.println("new list=" + change.getList());
            while (change.next()) {
                System.out.println("permutated=" + change.wasPermutated());
                for (int oldIndex=change.getFrom(); oldIndex<change.getTo(); oldIndex++) {
                    int newIndex = change.getPermutation(oldIndex);
                    System.out.println("old(" + oldIndex + ") -> new(" + newIndex + ")");
                }
            }
        });

        System.out.println("old list=" + list);
        list.sort(String::compareTo);
    }
}

Résultat d'exécution


old list=[aaa, ccc, bbb, eee, ddd, fff]
==========================================
new list=[aaa, bbb, ccc, ddd, eee, fff]
permutated=true
old(0) -> new(0)
old(1) -> new(2)
old(2) -> new(1)
old(3) -> new(4)
old(4) -> new(3)
old(5) -> new(5)

--wasPermutated () renvoie true lorsque la liste est réorganisée --Si vous passez l'index avant le changement à getPermutated (int), l'index après le changement est retourné.

Vérifiez l'élément remplacé

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;

public class Main {
    public static void main(String[] args) {
        ObservableList<String> list = FXCollections.observableArrayList("one", "two", "three");

        list.addListener((Change<? extends String> change) -> {
            System.out.println("==========================================");
            while (change.next()) {
                System.out.println("list=" + change.getList());
                
                System.out.println("replaced=" + change.wasReplaced());
                System.out.println("added=" + change.wasAdded());
                System.out.println("removed=" + change.wasRemoved());
                
                System.out.println("from=" + change.getFrom());
                System.out.println("to=" + change.getTo());
                System.out.println("addedSubList=" + change.getAddedSubList());
                System.out.println("addedSize=" + change.getAddedSize());
                System.out.println("removed=" + change.getRemoved());
                System.out.println("removedSize=" + change.getRemovedSize());
            }
        });

        list.set(1, "FOO");
    }
}

Résultat d'exécution


==========================================
list=[one, FOO, three]
replaced=true
added=true
removed=true
from=1
to=2
addedSubList=[FOO]
addedSize=1
removed=[two]
removedSize=1

--wasReplaced () retourne true lorsqu'un élément est remplacé par un autre élément

Liste triée

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.SortedList;

import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        ObservableList<String> list = FXCollections.observableArrayList("one", "two", "three");
        SortedList<String> sortedList = list.sorted();

        System.out.println("list=" + list);
        System.out.println("sortedList=" + sortedList);

        list.addAll("four", "five", "six");

        System.out.println("list=" + list);
        System.out.println("sortedList=" + sortedList);
        
        sortedList.setComparator(Comparator.reverseOrder());

        System.out.println("list=" + list);
        System.out.println("sortedList=" + sortedList);
    }
}

Résultat d'exécution


list=[one, two, three]
sortedList=[one, three, two]

list=[one, two, three, four, five, six]
sortedList=[five, four, one, six, three, two]

list=[one, two, three, four, five, six]
sortedList=[two, three, six, one, four, five]

Liste filtrée

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.transformation.FilteredList;

public class Main {
    public static void main(String[] args) {
        ObservableList<String> list = FXCollections.observableArrayList("one", "two", "three");
        FilteredList<String> filteredList = list.filtered(e -> e.contains("e"));

        System.out.println("list=" + list);
        System.out.println("filteredList=" + filteredList);
        
        list.addAll("four", "five");

        System.out.println("list=" + list);
        System.out.println("filteredList=" + filteredList);

        filteredList.setPredicate(e -> e.contains("f"));

        System.out.println("list=" + list);
        System.out.println("filteredList=" + filteredList);
    }
}

Résultat d'exécution


list=[one, two, three]
filteredList=[one, three]

list=[one, two, three, four, five]
filteredList=[one, three, five]

list=[one, two, three, four, five]
filteredList=[four, five]

Carte surveillable

package sample.javafx.property;

import javafx.collections.FXCollections;
import javafx.collections.MapChangeListener.Change;
import javafx.collections.ObservableMap;

public class Main {
    public static void main(String[] args) {
        ObservableMap<String, String> map = FXCollections.observableHashMap();
        map.put("foo", "FOO");
        map.put("bar", "BAR");
        
        map.addListener((Change<? extends String, ? extends String> change) -> {
            System.out.println("==============================");
            System.out.println("map=" + change.getMap());
            System.out.println("key=" + change.getKey());
            System.out.println("added=" + change.wasAdded());
            System.out.println("valueAdded=" + change.getValueAdded());
            System.out.println("removed=" + change.wasRemoved());
            System.out.println("valueRemoved=" + change.getValueRemoved());
        });
        
        map.put("fizz", "FIZZ");
        map.put("bar", "BARBAR");
        map.remove("foo");
    }
}

Résultat d'exécution


==============================
map={bar=BAR, foo=FOO, fizz=FIZZ}
key=fizz
added=true
valueAdded=FIZZ
removed=false
valueRemoved=null
==============================
map={bar=BARBAR, foo=FOO, fizz=FIZZ}
key=bar
added=true
valueAdded=BARBAR
removed=true
valueRemoved=BAR
==============================
map={bar=BARBAR, fizz=FIZZ}
key=foo
added=false
valueAdded=null
removed=true
valueRemoved=FOO

--ʻObservableMapest fourni sous forme deMapsurveillable --UtilisezFXCollections.observableHashMap () pour créer une instance --Change dans Mapn'est pas aussi compliqué queList`

Feuille de style

De base

Structure des dossiers


`-src/main/
  |-java/sample/javafx/
  | |-Main.java
  | `-MainController.java
  |
  `-resources/
    |-main.fxml
    `-my-style.css

main.fxml

javafx.jpg

my-style.css


.label {
    -fx-background-color: skyblue;
}

Main.java


package sample.javafx;

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

import java.net.URL;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        URL url = this.getClass().getResource("/main.fxml");
        FXMLLoader loader = new FXMLLoader(url);
        Parent root = loader.load();
        
        Scene scene = new Scene(root);
        scene.getStylesheets().add("my-style.css");
        primaryStage.setScene(scene);

        primaryStage.show();
    }
}

** Résultat d'exécution **

javafx.jpg

Chargement de la feuille de style

Main.java


        Scene scene = new Scene(root);
        scene.getStylesheets().add("my-style.css");

Comment écrire du CSS

my-style.css


.label {
    -fx-background-color: skyblue;
}

sélecteur de classe

--Si vous définissez .xxxx dans HTML CSS, il correspond à la balise avec class =" xxxx " set.

Le styleClass par défaut défini sur le nœud

--Certains nœuds (Label, ListView, etc.) ont l'attribut styleClass nommé par défaut selon certaines règles de conversion. --Par exemple, la classe Label utilise par défaut label`` styleClass, et la classe ListView utilise par défaut list-view`` styleClass.

Label.java


...
public class Label extends Labeled {

    public Label() {
        initialize();
    }

...

    private void initialize() {
        getStyleClass().setAll("label");
        ...
    }

--ʻInitialzie () ʻest appelé dans le constructeur, et la chaîne label est passée à la propriété styleClass (la styleClass qui est automatiquement convertie à partir du nom de la classe n'est pas définie).

Entité de propriété

my-style.css


.my-class {
    -fx-background-color: pink;
    -fx-padding: 10px;
    
    -fx-border-color: black;
    -fx-border-width: 1px;
    -fx-border-style: solid;
    
    -fx-underline: true;
}

** Résultat d'exécution **

javafx.jpg

--A première vue, les propriétés spécifiées par CSS (telles que -fx-background-color et -fx-padding) semblent avoir -fx au début des propriétés qui peuvent être spécifiées par HTML CSS. Ainsi, si vous n'êtes pas sûr des propriétés que vous pouvez utiliser, vous pouvez ajouter -fx aux propriétés utilisées dans le CSS HTML. --Mais il y a en fait des propriétés qui n'existent pas dans HTML CSS, comme -fx-underline: true; --Text-decoration: underline; `pour HTML CSS

Définir n'importe quel attribut styleClass

Définir dans Scene Builder

javafx.jpg

main.fxml


<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.text.Font?>

<Label fx:id="label" alignment="CENTER" prefHeight="200.0" prefWidth="300.0"
       styleClass="my-class"
       text="CSS Test" xmlns="http://javafx.com/javafx/8.0.151" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.javafx.MainController">
   <font>
      <Font size="30.0" />
   </font>
</Label>

my-style.css


.my-class {
    -fx-font-weight: bold;
    -fx-underline: true;
    -fx-text-fill: red;
}

** Résultat d'exécution **

javafx.jpg

--La classe de style de Scene Builder vous permet de définir n'importe quel attribut de styleClass pour un élément

Définir à partir du programme

MainContoller.java


package sample.javafx;

import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @FXML
    private Label label;
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        ObservableList<String> styleClass = label.getStyleClass();
        styleClass.add("my-class");
        System.out.println("styleClass=" + styleClass);
    }
}

--Get ʻObservableList de styleClassavecgetStyleClass ()depuis le nœud --Tout attribut peut être ajouté à la liste par ʻadd ()

Charger un fichier de feuille de style local

Structure des dossiers


|-my-styles.css
`-src/main/java/
  `-sample/javafx/
    |-Main.java
    :

my-style.css


.my-class {
    -fx-background-color: green;
}

Main.java


package sample.javafx;

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

import java.net.URL;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        URL url = this.getClass().getResource("/main.fxml");
        FXMLLoader loader = new FXMLLoader(url);
        Parent root = loader.load();
        
        Scene scene = new Scene(root);
        scene.getStylesheets().add("file:./my-style.css"); //★ Spécifiez le fichier local
        primaryStage.setScene(scene);

        primaryStage.show();
    }
}

** Résultat d'exécution **

javafx.jpg

--Spécifiez file: [path] pour spécifier un fichier CSS local

Paramètres de l'image d'arrière-plan

Comment définir -fx-background-image

Structure des dossiers


|-image.jpg
`-src/main/
  |-resources/
  | |-img/
  | | `-image.jpg
  | |-my-style.css
  | `-main.fxml
  :

Spécifiez un fichier dans le chemin de classe

my-style.css


.my-class {
    -fx-background-image: url('/img/image.jpg');
}

--Spécifier avec ʻurl`

Spécifiez un fichier local

my-style.css


.my-class {
    -fx-background-image: url('file:./image.jpg');
}

--Lors de la spécification d'un fichier local, utilisez file: [path]

Capture d'événements, bouillonnement d'événements

javafx.jpg

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Node;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Pane;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private Pane bluePane;
    @FXML
    private Pane yellowPane;
    @FXML
    private Button button;
    @FXML
    private TextField textField;
    @FXML
    private Label label;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.registerHandlers(this.bluePane, "bluePane");
        this.registerHandlers(this.yellowPane, "yellowPane");
        this.registerHandlers(this.button, "button");
        this.registerHandlers(this.textField, "textField");
        this.registerHandlers(this.label, "label");
    }
    
    private void registerHandlers(Node node, String name) {
        this.registerEventFilter(node, name);
        this.registerEventHandler(node, name);
    }

    private void registerEventFilter(Node node, String name) {
        node.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> System.out.println("filter " + name));
    }

    private void registerEventHandler(Node node, String name) {
        node.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("handler " + name));
    }
}

** Résultat d'exécution **

javafx.jpg

Cliquez sur la zone bleue


filter bluePane
handler bluePane

Cliquez sur la zone jaune


filter bluePane
filter yellowPane
handler yellowPane
handler bluePane

Cliquez sur le bouton


filter bluePane
filter yellowPane
filter button
handler button

Cliquez sur le champ de texte


filter bluePane
filter yellowPane
filter textField
handler textField

Cliquez sur l'étiquette


filter bluePane
filter yellowPane
filter label
handler label
handler yellowPane
handler bluePane

La description

Lorsqu'un événement se produit, le traitement est exécuté dans l'ordre suivant

  1. Sélection de la cible
  2. Détermination de l'itinéraire [^ 2]
  3. Phase de capture des événements
  4. Phase de bouillonnement d'événement

[^ 2]: Dans le document officiel japonais, il est décrit comme "root", mais il est écrit comme "route" car il semble être confondu avec "root".

Sélection cible

Détermination de l'itinéraire

javafx.jpg

Phase de capture d'événement

Cliquez sur le champ de texte


filter bluePane
filter yellowPane
filter textField
...

--Dans la phase de capture d'événements, le filtre d'événements enregistré dans chaque nœud est exécuté de la racine à la cible sur la route déterminée.

MainController.java


    private void registerEventFilter(Node node, String name) {
        node.addEventFilter(MouseEvent.MOUSE_CLICKED, e -> System.out.println("filter " + name));
    }

Phase de bouillonnement d'événement

Cliquez sur l'étiquette


...
handler label
handler yellowPane
handler bluePane

MainController.java


    private void registerEventHandler(Node node, String name) {
        node.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> System.out.println("handler " + name));
    }

Consommez l'événement

MainController.java


package sample.javafx;

...

public class MainController implements Initializable {
    ...

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.registerHandlers(this.bluePane, "bluePane");
        
        this.registerEventFilter(this.yellowPane, "yellowPane");
        this.yellowPane.addEventHandler(MouseEvent.MOUSE_CLICKED, e -> {
            System.out.println("handler yellowPane");
            e.consume();
        });
        
        this.registerHandlers(this.button, "button");
        this.registerHandlers(this.textField, "textField");
        this.registerHandlers(this.label, "label");
    }
    
    ...
}

--Le gestionnaire d'événements dans la zone avec un arrière-plan jaune (yellowPane) exécute la méthode consume () de MouseEvent.

** Résultat d'exécution **

Cliquez sur la zone jaune


filter bluePane
filter yellowPane
filter label
handler label
handler yellowPane

Certains contrôles de l'interface utilisateur interrompent le bouillonnement d'événements

Cliquez sur le bouton


filter bluePane
filter yellowPane
filter button
handler button

Cliquez sur le champ de texte


filter bluePane
filter yellowPane
filter textField
handler textField

Cliquez sur l'étiquette


filter bluePane
filter yellowPane
filter label
handler label
handler yellowPane
handler bluePane

--Label exécute normalement une bulle d'événement, mais Button et TextField sont interrompus. --Toutes les phases de capture d'événements sont en cours d'exécution

Pour le moment, le document officiel dit ce qui suit.

Les gestionnaires par défaut de contrôle de l'interface utilisateur JavaFX consomment généralement la plupart des événements d'entrée.

1 Traitement des événements (version 8) | JavaFX: Traitement des événements

Cependant, je ne sais pas exactement où vérifier quelle classe consomme l'événement.

Glisser déposer

laissez tomber

De base

javafx.jpg

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;

public class MainController {
    
    @FXML
    public void onDragOver(DragEvent e) {
        e.acceptTransferModes(TransferMode.ANY);
        e.consume();
    }
    
    @FXML
    public void onDragDropped(DragEvent e) {
        Dragboard dragboard = e.getDragboard();
        String string = dragboard.getString();
        System.out.println("dropped string = " + string);
        e.consume();
    }
}

** Résultat d'exécution **

javafx.gif

La description

Accepter les gouttes

MainController.java


    @FXML
    public void onDragOver(DragEvent e) {
        e.acceptTransferModes(TransferMode.ANY);
        e.consume();
    }

--Enregistrez un gestionnaire pour l'événement DRAG_OVER pour le nœud de destination de dépôt --Ainsi, en exécutant la méthode DragEvent.acceptTransferModes (), vous pouvez accepter la suppression. --Dans l'argument, spécifiez le mode de transfert (COPY, MOVE ou LINK) à accepter avec la constante de TransferMode. ―― «TOUT» représente les trois modes de transfert (c'est-à-dire que tout est OK). --Si vous n'acceptez pas la suppression, le curseur de la souris deviendra une icône indiquant qu'il ne peut pas être déposé. --Enfin, exécutez la méthode consume () pour consommer l'événement. ―― Cela fonctionne même si vous ne le consommez pas, mais la documentation officielle mentionne toujours «consume ()». ――Peut-être que si le nœud parent a également un processus de glisser-déposer, il peut être empêché d'être exécuté par erreur.

Traitement au moment du dépôt

MainController.java


    @FXML
    public void onDragDropped(DragEvent e) {
        Dragboard dragboard = e.getDragboard();
        String string = dragboard.getString();
        System.out.println("dropped string = " + string);
        e.consume();
    }

--Pour le traitement de suppression, enregistrez un gestionnaire dans l'événement DRAG_DROPPED du nœud cible.

Déposer le fichier

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;

import java.io.File;
import java.util.List;

public class MainController {
    
    @FXML
    public void onDragOver(DragEvent e) {
        Dragboard dragboard = e.getDragboard();
        
        if (dragboard.hasFiles()) {
            e.acceptTransferModes(TransferMode.ANY);
        }

        e.consume();
    }
    
    @FXML
    public void onDragDropped(DragEvent e) {
        System.out.println("onDragDropped()");
        Dragboard dragboard = e.getDragboard();

        List<File> files = dragboard.getFiles();
        files.forEach(file -> System.out.println(file.getName()));

        e.consume();
    }
}

** Résultat d'exécution **

javafx.gif

La description

MainController.java


    @FXML
    public void onDragOver(DragEvent e) {
        Dragboard dragboard = e.getDragboard();
        
        if (dragboard.hasFiles()) {
            e.acceptTransferModes(TransferMode.ANY);
        }

        e.consume();
    }

MainController.java


    @FXML
    public void onDragDropped(DragEvent e) {
        System.out.println("onDragDropped()");
        Dragboard dragboard = e.getDragboard();

        List<File> files = dragboard.getFiles();
        files.forEach(file -> System.out.println(file.getName()));

        e.consume();
    }

HTML drop

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;

public class MainController {
    
    @FXML
    public void onDragOver(DragEvent e) {
        Dragboard dragboard = e.getDragboard();
        
        if (dragboard.hasHtml()) {
            e.acceptTransferModes(TransferMode.ANY);
        }

        e.consume();
    }
    
    @FXML
    public void onDragDropped(DragEvent e) {
        System.out.println("onDragDropped()");
        Dragboard dragboard = e.getDragboard();

        System.out.println("[html]   = " + dragboard.getHtml());
        System.out.println("[string] = " + dragboard.getString());
        System.out.println("[files]  = " + dragboard.getFiles());

        e.consume();
    }
}

** Résultat d'exécution **

javafx.gif

La description

MainController.java


    @FXML
    public void onDragDropped(DragEvent e) {
        System.out.println("onDragDropped()");
        Dragboard dragboard = e.getDragboard();

        System.out.println("[html]   = " + dragboard.getHtml());
        System.out.println("[string] = " + dragboard.getString());
        System.out.println("[files]  = " + dragboard.getFiles());

        e.consume();
    }

--Si vous déposez le texte du navigateur, vous pouvez obtenir le HTML qui reproduit le texte avec getHtml ()

Changer le style lors de la chute

my-style.css


.drag-over {
    -fx-text-fill: red;
}

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.TransferMode;

public class MainController {
    
    @FXML
    private Label dropLabel;
    
    @FXML
    public void onDragEntered() {
        this.dropLabel.getStyleClass().add("drag-over");
    }
    
    @FXML
    public void onDragExited() {
        this.dropLabel.getStyleClass().remove("drag-over");
    }
    
    ...
}

** Résultat d'exécution **

javafx.gif

La description

traîne

javafx.jpg

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.control.Label;
import javafx.scene.input.ClipboardContent;
import javafx.scene.input.DragEvent;
import javafx.scene.input.Dragboard;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;

public class MainController {
    
    @FXML
    private Label dragLabel;
    
    @FXML
    public void onDragDetected(MouseEvent e) {
        Dragboard dragboard = this.dragLabel.startDragAndDrop(TransferMode.ANY);
        
        ClipboardContent content = new ClipboardContent();
        content.putString("Drag Test");
        dragboard.setContent(content);

        e.consume();
    }
    
    @FXML
    public void onDragOver(DragEvent e) {
        e.acceptTransferModes(TransferMode.ANY);
        e.consume();
    }
    
    @FXML
    public void onDragDropped(DragEvent e) {
        System.out.println("onDragDropped()");
        Dragboard dragboard = e.getDragboard();
        System.out.println(dragboard.getString());
        
        e.consume();
    }
}

** Résultat d'exécution **

javafx.gif

La description

MainController.java


    @FXML
    private Label dragLabel;
    
    @FXML
    public void onDragDetected(MouseEvent e) {
        Dragboard dragboard = this.dragLabel.startDragAndDrop(TransferMode.ANY);
        
        ClipboardContent content = new ClipboardContent();
        content.putString("Drag Test");
        dragboard.setContent(content);

        e.consume();
    }

--Drag est démarré en enregistrant un gestionnaire dans l'événement DRAG_DETECTED.

référence

dialogue

Préparation

javafx.jpg

package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.control.Alert;

public class MainController {
    
    @FXML
    public void showDialog() {
        ...
    }
}

Assurez-vous que la méthode showDialog () est appelée lorsque vous cliquez sur le bouton.

De base

Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.showAndWait();

** Résultat d'exécution **

javafx.jpg

Confirmer le bouton sélectionné

Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
Optional<ButtonType> buttonType = alert.showAndWait();
buttonType.ifPresent(System.out::println);

** Résultat d'exécution **

Cliquez respectivement sur les boutons "OK" et "Annuler".

ButtonType [text=OK, buttonData=OK_DONE]
ButtonType [text=Annuler, buttonData=CANCEL_CLOSE]

[^ 1]: ButtonData est NON ou CANCEL_CLOSE

Définir un message

Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Messeji");
alert.showAndWait();

javafx.jpg

Type de dialogue

INFORMATION

javafx.jpg

CONFIRMATION

javafx.jpg

WARNING

javafx.jpg

ERROR

javafx.jpg

Spécifiez le bouton

Alert alert = new Alert(
        Alert.AlertType.INFORMATION, 
        "Messeji",
        ButtonType.APPLY,
        ButtonType.CANCEL,
        ButtonType.CLOSE,
        ButtonType.FINISH,
        ButtonType.NEXT,
        ButtonType.NO,
        ButtonType.OK,
        ButtonType.PREVIOUS,
        ButtonType.YES);
alert.showAndWait();

javafx.jpg

--ButtonType peut être spécifié après le troisième argument du constructeur

Ordre de placement des boutons

** ButtonData ** défini sur une constante ButtonType

ButtonType ButtonData qui est défini
APPLY APPLY
CANCEL CANCEL_CLOSE
CLOSE CANCEL_CLOSE
FINISH FINISH
NEXT NEXT_FORWARD
NO NO
OK OK_DONE
PREVIOUS BACK_PREVIOUS
YES YES

** Ordre de disposition de ButtonData pour chaque OS **

OS Ordre de placement
Windows L_E+U+FBXI_YNOCAH_R
Mac OS L_HE+U+FBIX_NCYOA_R
Linux L_HE+UNYACBXIO_R

--Cette chaîne mystérieuse correspond au ** code de séquence de boutons **, où chaque caractère est affecté à la constante ButtonData. --Par exemple, le code de séquence de boutons pour ButtonData.LEFT est L, ButtonData.HELP_2 est ʻE`, et ainsi de suite.

javafx.jpg

Placez n'importe quel bouton

ButtonType myButton = new ButtonType("Bouton");
Alert alert = new Alert(Alert.AlertType.INFORMATION, "Messeji", myButton);
alert.showAndWait().ifPresent(System.out::println);

** Résultat d'exécution **

javafx.jpg

Sortie de la console lorsque vous cliquez sur le bouton "Botan"


ButtonType [text=Bouton, buttonData=OTHER]

Jugement lorsqu'il est fermé par un autre que le bouton

Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Messeji", ButtonType.YES);
Optional<ButtonType> buttonType = alert.showAndWait();
String result = buttonType.map(ButtonType::getText).orElse("Pas de choix");
System.out.println(result);

** Résultat d'exécution **

javafx.jpg

Résultat une fois fermé avec ʻESC`

Pas de choix

--Suivant, lorsqu'il y a plusieurs boutons d'annulation

Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Messeji", ButtonType.YES, ButtonType.NO, ButtonType.CANCEL);
Optional<ButtonType> buttonType = alert.showAndWait();
String result = buttonType.map(ButtonType::getText).orElse("Pas de choix");
System.out.println(result);

** Résultat d'exécution **

javafx.jpg

1 tasse" 2. "Non" 3. "Annuler" 4. ESC 5. Alt + F4 6. Fermez la fenêtre

Essayez de fermer la boîte de dialogue dans l’ordre de.

Oui
Non
Annuler
Annuler
Annuler
Annuler

--Quand chaque bouton est enfoncé, ce bouton est la valeur de retour de showAndWait () --Si la boîte de dialogue est fermée par une méthode autre que le bouton, cela signifie que le bouton "Annuler" a été enfoncé. --C'est quand il y a plusieurs boutons d'annulation.

  1. ButtonData est CANCEL_CLOSE`` ButtonType
  2. ButtonData.isCancelButton () retourne true ButtonType C'est parce que la valeur de retour est déterminée dans l'ordre de priorité de -.

Pour résumer la valeur de retour de showAndWait () lorsque la boîte de dialogue est fermée par une méthode autre qu'un bouton,

  1. S'il n'y a pas de bouton d'annulation [^ 1], ʻOptional.empty () `
  2. S'il y a un bouton d'annulation, son ButtonType
  3. S'il y a deux boutons d'annulation ou plus, la valeur de retour «ButtonType» est déterminée dans l'ordre de priorité suivant.
  4. ButtonType ButtonData est CANCEL_CLOSE
  5. ButtonData.isCancelButton () retourne true ButtonType

Divers paramètres de texte

Seul contentText peut être défini avec l'argument constructeur, mais le titre, etc. peut être modifié après la génération de ʻAlert`.

Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle("Prends-le");
alert.setHeaderText("En-tête");
alert.setContentText("Quand vous venez ici");
alert.showAndWait();

javafx.jpg

Chacun peut également être désactivé en définissant «null».

Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.setTitle(null);
alert.setHeaderText(null);
alert.setContentText("Quand vous venez ici");
alert.showAndWait();

javafx.jpg

fil

L'histoire de base lors de l'utilisation de threads avec JavaFX

Fil UI

Le traitement de l'interface graphique JavaFX est exécuté sur un thread dédié (** thread UI **) appelé JavaFX Application Thread.

MainController.java


package sample.javafx;

import javafx.fxml.Initializable;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        System.out.println("thread = " + Thread.currentThread().getName());
    }
}

Résultat d'exécution


thread = JavaFX Application Thread

S'il est implémenté normalement, le processus sera exécuté sur ce thread d'interface utilisateur.

Cependant, si un traitement fastidieux est exécuté sur ce thread d'interface utilisateur, le traitement des événements tels que la mise à jour de l'interface utilisateur et le fonctionnement de la souris sera attendu. Ensuite, l'application ne peut accepter l'opération d'aucun utilisateur et l'apparence ne change pas, c'est-à-dire un soi-disant état solide.

Pour éviter cela, le processus fastidieux doit être exécuté dans un thread distinct du thread d'interface utilisateur.

Appuyez sur l'interface utilisateur à partir d'un fil de discussion non-UI

Les composants de l'interface utilisateur JavaFX ne sont accessibles qu'à partir des threads de l'interface utilisateur. Des exceptions sont désormais levées lorsque vous essayez d'accéder à un composant d'interface utilisateur depuis l'extérieur du thread d'interface utilisateur.

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class MainController {

    @FXML
    private Button button;
    
    @FXML
    public void click() {
        new Thread(() -> {
            System.out.println("thread = " + Thread.currentThread().getName());
            button.setText("hoge");
        }).start();
    }
}

Résultat d'exécution


thread = Thread-4
Exception in thread "Thread-4" java.lang.IllegalStateException: Not on FX application thread; currentThread = Thread-4
	at com.sun.javafx.tk.Toolkit.checkFxUserThread(Toolkit.java:279)
	at com.sun.javafx.tk.quantum.QuantumToolkit.checkFxUserThread(QuantumToolkit.java:423)
	at javafx.scene.Parent$2.onProposedChange(Parent.java:367)
        ...

De telles restrictions existent souvent non seulement dans JavaFX, mais également dans d'autres frameworks liés à l'interface graphique tels que Swing.

L'interface graphique semble être très difficile à atteindre avec la sécurité des threads lors de l'exécution du multithread. Pour cette raison, la plupart des frameworks GUI ont un thread dédié pour les opérations de l'interface utilisateur, et l'interface utilisateur n'est accessible qu'à partir de ce thread dédié (la recherche de «GUI, single thread» révèle de nombreuses histoires à ce sujet. viens).

Dans JavaFX, Platform.runLater () est un moyen de faire fonctionner des composants d'interface utilisateur à partir d'un autre thread d'interface utilisateur. Il existe une méthode appelée .html # runLater-java.lang.Runnable-).

MainController.java


package sample.javafx;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.scene.control.Button;

public class MainController {

    @FXML
    private Button button;
    
    @FXML
    public void click() {
        new Thread(() -> {
            System.out.println("thread = " + Thread.currentThread().getName());
            
            Platform.runLater(() -> {
                System.out.println("runLater thread = " + Thread.currentThread().getName());
                button.setText("hoge");
            });
        }).start();
    }
}

Résultat d'exécution


thread = Thread-4
runLater thread = JavaFX Application Thread

L'expression lambda (type Runnable) passée à runLater () sera maintenant exécutée sur le thread d'interface utilisateur (JavaFx Application Thread). Par conséquent, vous pouvez toucher en toute sécurité le thread d'interface utilisateur du processus passé à ce runLater ().

Comment gérer facilement les threads avec JavaFX

JavaFX fournit un mécanisme pour faciliter l'implémentation du traitement threadé.

Worker Le traitement des threads utilisé dans JavaFX est résumé sous la forme Worker.

Worker définit une API pratique qui ne se trouve pas dans le Thread normal. (Il s'agit d'une interface en soi, et l'implémentation spécifique est Task ou Service (Il est réalisé par: //docs.oracle.com/javase/jp/8/javafx/api/javafx/concurrent/Service.html)

Cycle de vie des travailleurs

Les travailleurs ont un cycle de vie et l'état change comme suit.

javafx.png

READY

--Lorsque Worker est créé, il passe d'abord à l'état READY. -Le seul état appelé ** état de démarrage ** est cet état READY.

SCHEDULED

--Ensuite, lorsque le démarrage de Worker est planifié, il sera dans l'état SCHEDULED. --Si vous utilisez un pool de threads, vous pouvez attendre dans l'état SCHEDULED pour attendre que le pool devienne libre. --Selon l'implémentation de Worker, il peut être démarré immédiatement sans être planifié. Cependant, même dans ce cas, il sera toujours à l'état RUNNING après avoir traversé SCHEDULED.

RUNNING

SUCCEEDED

FAILED

CANCELLED

--Si l'annulation est exécutée avant l'état final, cet état sera atteint.

Worker a [stateProperty ()](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/concurrent/Worker.html#" pour obtenir des informations sur ces états. Des propriétés telles que stateProperty--) et runningProperty () sont disponibles. ..

Task Une classe appelée Task est préparée comme une classe qui implémente Worker.

package sample.javafx;

import javafx.concurrent.Task;
import javafx.fxml.Initializable;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Runnable task = new Task<Void>() {

            @Override
            protected Void call() throws Exception {
                System.out.println("task.call()");
                System.out.println("thread = " + Thread.currentThread().getName());
                return null;
            }
        };

        new Thread(task).start();
    }
}

Résultat d'exécution


task.call()
thread = Thread-4

Annuler

package sample.javafx;

import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    private Worker worker;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Task<Void> task = new Task<Void>() {

            @Override
            protected Void call() throws Exception {
                System.out.println("start...");
                final long max = 10000000000000L;
                
                for (long i=0; i<max; i++) {
                    if (this.isCancelled()) {
                        System.out.println("cancelled!!");
                        return null;
                    }
                }

                System.out.println("finished");
                return null;
            }
        };
        
        this.worker = task;
        new Thread(task).start();
    }
    
    @FXML
    public void stop() {
        this.worker.cancel();
    }
}

ʻInitialize () lance la tâche, et la méthode stop () enregistrée dans le gestionnaire d'événements du bouton appelle la méthode cancel () de Worker`.

Résultat d'exécution


start...
cancelled!!

le progrès

javafx.jpg

package sample.javafx;

import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.ProgressBar;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private ProgressBar progressBar;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Task<Void> task = new Task<Void>() {

            @Override
            protected Void call() throws Exception {
                final long max = 100000000L;
                
                for (long i=0; i<=max; i++) {
                    this.updateProgress(i, max);
                }
                
                return null;
            }
        };
        
        this.progressBar.progressProperty().bind(task.progressProperty());
        new Thread(task).start();
    }
}

** Résultat d'exécution **

javafx.gif

La description

        this.progressBar.progressProperty().bind(task.progressProperty());

--Worker expose une propriété appeléeprogressProperty ()

                for (long i=0; i<=max; i++) {
                    this.updateProgress(i, max);
                }

--Utilisez la méthode ʻupdateProgress () pour mettre à jour la progression de Task --Passez le nombre de cas traités dans le premier argument et le nombre total dans le deuxième argument (la méthode qui passedoubleest également surchargée) --Ce ʻupdateProgress ()peut être exécuté à partir d'un thread non-UI sans aucun problème. --Dans lequel runLater () est utilisé

Publier des valeurs arbitraires

javafx.jpg

package sample.javafx;

import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.Label;

import java.io.IOException;

public class MainController {

    @FXML
    private Label label;

    @FXML
    public void start() throws IOException {
        MyTask task = new MyTask();
        this.label.textProperty().bind(task.valueProperty());
        
        new Thread(task).start();
    }
    
    private static class MyTask extends Task<String> {

        @Override
        protected String call() throws Exception {
            for (int i=0; i<100000000; i++) {
                this.updateValue("i=" + i);
            }
            
            return "finished!!";
        }
    }
}

** Résultat d'exécution **

javafx.gif

La description

    private static class MyTask extends Task<String> {

        @Override
        protected String call() throws Exception {
            for (int i=0; i<100000000; i++) {
                this.updateValue("i=" + i);
            }
            
            return "finished!!";
        }
    }

--Task a une propriété appelée valeur

Écouteur d'événements

package sample.javafx;

import javafx.concurrent.Task;
import javafx.fxml.FXML;

import java.io.IOException;

public class MainController {

    @FXML
    public void start() throws IOException {
        Task<Void> task = new Task<Void>() {

            @Override
            protected Void call() throws Exception {
                return null;
            }
        };
        
        task.setOnScheduled(event -> System.out.println("scheduled thread=" + Thread.currentThread().getName()));
        task.setOnRunning(event -> System.out.println("running thread=" + Thread.currentThread().getName()));
        task.setOnSucceeded(event -> System.out.println("succeeded thread=" + Thread.currentThread().getName()));
        
        new Thread(task).start();
    }
}

** Résultat d'exécution **

scheduled thread=JavaFX Application Thread
running thread=JavaFX Application Thread
succeeded thread=JavaFX Application Thread

Propriété d'exception

package sample.javafx;

import javafx.concurrent.Task;
import javafx.fxml.FXML;

import java.io.IOException;

public class MainController {

    @FXML
    public void start() throws IOException {
        Task<Void> task = new Task<Void>() {

            @Override
            protected Void call() throws Exception {
                throw new RuntimeException("test");
            }
        };
        
        task.setOnFailed(event -> {
            Throwable exception = task.getException();
            System.out.println("exception=" + exception);
        });
        
        new Thread(task).start();
    }
}

Résultat d'exécution


exception=java.lang.RuntimeException: test

Publier ObservableList

package sample.javafx;

import javafx.application.Platform;
import javafx.beans.property.ReadOnlyListProperty;
import javafx.beans.property.ReadOnlyListWrapper;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.Label;

import java.util.concurrent.TimeUnit;

public class MainController {
    
    @FXML
    private Label label;
    
    @FXML
    public void start() {
        MyTask myTask = new MyTask();
        this.label.textProperty().bind(myTask.listProperty().asString());

        Thread thread = new Thread(myTask);
        thread.setDaemon(true);
        thread.start();
    }
    
    private static class MyTask extends Task<Void> {
        private ReadOnlyListWrapper<String> list = new ReadOnlyListWrapper<>(FXCollections.observableArrayList());

        public final ObservableList<String> getList() {
            return list.get();
        }

        public ReadOnlyListProperty<String> listProperty() {
            return list.getReadOnlyProperty();
        }

        @Override
        protected Void call() throws Exception {
            for (int i=0; i<10; i++) {
                String value = String.valueOf(i);
                Platform.runLater(() -> this.list.add(value));
                
                TimeUnit.SECONDS.sleep(1);
            }
            return null;
        }
    }
}

Service

package sample.javafx;

import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.concurrent.Worker;
import javafx.fxml.FXML;

import java.io.IOException;

public class MainController {

    private Service<Void> service = new Service<Void>() {
        @Override
        protected Task<Void> createTask() {
            System.out.println("Service.createTask()");
            
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    System.out.println("Task.call()");
                    return null;
                }
            };
        }
    };
    
    @FXML
    public void start() throws IOException {
        if (!this.service.getState().equals(Worker.State.READY)) {
            System.out.println("reset");
            this.service.reset();
        }
        System.out.println("start");
        this.service.start();
    }
}

Assignez la méthode start () à l'événement d'action du bouton et exécutez-la plusieurs fois.

Résultat d'exécution


start
Service.createTask()
Task.call()

reset
start
Service.createTask()
Task.call()

reset
start
Service.createTask()
Task.call()

--Task est censé être jetable, et il n'est pas censé être utilisé pour réutiliser la même instance à nouveau.

Ré-exécuter

Lors de la vérification de l'état et de la réexécution


    @FXML
    public void start() throws IOException {
        if (!this.service.getState().equals(Worker.State.READY)) {
            System.out.println("reset");
            this.service.reset();
        }
        System.out.println("start");
        this.service.start();
    }

restart()Une fois réécrit avec


    @FXML
    public void start() throws IOException {
        System.out.println("restart");
        this.service.restart();
    }

Spécifiez le fil à utiliser

Vérifiez d'abord l'opération par défaut.

package sample.javafx;

import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.fxml.FXML;

import java.io.IOException;

public class MainController {

    private Service<Void> service = new Service<Void>() {
        @Override
        protected Task<Void> createTask() {
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    Thread thread = Thread.currentThread();
                    System.out.println("thread.name = " + thread.getName() + ", daemon = " + thread.isDaemon());
                    return null;
                }
            };
        }
    };
    
    @FXML
    public void start() throws IOException {
        this.service.restart();
    }
}

Exécutez plusieurs fois restart ()

Résultat d'exécution


thread.name = Thread-4, daemon = true
thread.name = Thread-5, daemon = true
thread.name = Thread-6, daemon = true
thread.name = Thread-7, daemon = true

--Par défaut, Service créera un nouveau thread démon chaque fois que vous démarrez Task. --Si vous souhaitez spécifier le thread à utiliser (par exemple, utilisez le thread du pool de threads), implémentez comme suit

Spécifiez le thread utilisé par le service


package sample.javafx;

import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.fxml.FXML;

import java.io.IOException;
import java.util.concurrent.Executors;

public class MainController {

    private Service<Void> service = new Service<Void>() {
        {
            this.setExecutor(Executors.newFixedThreadPool(3));
        }
        
        @Override
        protected Task<Void> createTask() {
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    Thread thread = Thread.currentThread();
                    System.out.println("thread.name = " + thread.getName() + ", daemon = " + thread.isDaemon());
                    return null;
                }
            };
        }
    };
    
    @FXML
    public void start() throws IOException {
        this.service.restart();
    }
}

Résultat d'exécution


thread.name = pool-2-thread-1, daemon = false
thread.name = pool-2-thread-2, daemon = false
thread.name = pool-2-thread-3, daemon = false
thread.name = pool-2-thread-1, daemon = false
thread.name = pool-2-thread-2, daemon = false

--Spécifiez Executor dans la propriété ʻexecutordeService. --Ensuite, Service obtiendra le thread pour exécuter Task à partir de ʻexecutor.

Diverses propriétés

package sample.javafx;

import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.control.ProgressBar;

import java.io.IOException;

public class MainController {
    @FXML
    private ProgressBar progressBar;

    private Service<Void> service = new Service<Void>() {
        
        @Override
        protected Task<Void> createTask() {
            return new Task<Void>() {
                @Override
                protected Void call() throws Exception {
                    int max = 10000000;
                    for (int i=0; i<max; i++) {
                        this.updateProgress(i, max);
                    }
                    
                    return null;
                }
            };
        }
    };
    
    @FXML
    public void start() throws IOException {
        this.service.restart();
        this.progressBar.progressProperty().bind(this.service.progressProperty());
    }
}

--Service implémente Worker, donc il fournit des propriétés telles que progress comme Task. --Chaque propriété est liée à la propriété de Tâche que Service exécute

référence

Effets visuels

Comment appliquer des effets visuels tels que l'éclairage et les ombres portées.

En tant que mécanisme, il semble que le graphique de la scène soit une fois rendu sous forme d'image de pixel et que le traitement de l'image est effectué pour projeter des ombres ou éclairer dessus.

Essayez d'appliquer divers effets visuels aux graphiques de scène suivants et voyez ce qui se passe.

javafx.jpg

Effet Bloom

** Résultat d'exécution **

javafx.jpg

La mise en oeuvre

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.effect.Bloom;
import javafx.scene.layout.Pane;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    @FXML
    private Pane pane;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Bloom bloom = new Bloom();
        this.pane.setEffect(bloom);
    }
}

Effet de flou

Flou de boîte

** Résultat d'exécution **

javafx.jpg

La mise en oeuvre

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.effect.BoxBlur;
import javafx.scene.layout.Pane;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    @FXML
    private Pane pane;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        BoxBlur boxBlur = new BoxBlur();
        boxBlur.setWidth(5);
        boxBlur.setHeight(5);
        
        this.pane.setEffect(boxBlur);
    }
}

Flou de mouvement

** Résultat d'exécution **

javafx.jpg

La mise en oeuvre

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.effect.MotionBlur;
import javafx.scene.layout.Pane;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    @FXML
    private Pane pane;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        MotionBlur motionBlur = new MotionBlur();
        motionBlur.setAngle(45.0);
        motionBlur.setRadius(40.0);
        
        this.pane.setEffect(motionBlur);
    }
}

-Lors de l'utilisation de MotionBlur, flou de mouvement (comme lorsqu'un objet se déplace) Flou) peut être appliqué

flou gaussien

** Résultat d'exécution **

javafx.jpg

La mise en oeuvre

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.effect.GaussianBlur;
import javafx.scene.layout.Pane;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    @FXML
    private Pane pane;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        GaussianBlur gaussianBlur = new GaussianBlur();
        gaussianBlur.setRadius(10.0);
        
        this.pane.setEffect(gaussianBlur);
    }
}

Effet d'ombre portée

** Résultat d'exécution **

javafx.jpg

La mise en oeuvre

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    @FXML
    private Pane pane;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        DropShadow dropShadow = new DropShadow();
        dropShadow.setOffsetX(5.0);
        dropShadow.setOffsetY(5.0);
        dropShadow.setWidth(20.0);
        dropShadow.setHeight(20.0);
        dropShadow.setColor(Color.valueOf("CACA00"));

        this.pane.setEffect(dropShadow);
    }
}

-Si vous utilisez DropShadow, vous pouvez utiliser l'effet d'ombre portée. Peut être appliqué --Spécifiez la position relative (offset) de l'ombre avec ʻoffsetX, ʻoffsetY --Utilisez width et height pour spécifier le degré de flou des ombres dans les directions verticale et horizontale. --Spécifiez la couleur de l'ombre avec color --Utilisez spread pour spécifier le degré de diffusion de l'ombre ( 0.0 à 1.0).

Effet d'ombre intérieure

Apportez quelques modifications au graphique de la scène pour le rendre plus facile à voir.

javafx.jpg

** Résultat d'exécution **

javafx.jpg

La mise en oeuvre

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.effect.InnerShadow;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    @FXML
    private Pane pane;
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        InnerShadow innerShadow = new InnerShadow();
        innerShadow.setOffsetX(7.0);
        innerShadow.setOffsetY(7.0);
        innerShadow.setColor(Color.valueOf("500"));
        innerShadow.setRadius(10.0);
        
        this.pane.setEffect(innerShadow);
    }
}

-Si vous utilisez InnerShadow, vous pouvez utiliser l'ombre intérieure (ombre qui ressemble à un creux à l'intérieur). ) L'effet peut être appliqué --Spécifiez la position relative (offset) de l'ombre avec ʻoffsetX, ʻoffsetY --Spécifiez la longueur verticale et horizontale de l'ombre avec width, height --Spécifiez la couleur de l'ombre avec color

réflexion

** Résultat d'exécution **

javafx.jpg

La mise en oeuvre

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.effect.Reflection;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    @FXML
    private Label label;
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Reflection reflection = new Reflection();
        reflection.setTopOffset(-25.0);
        reflection.setTopOpacity(0.8);
        reflection.setFraction(0.8);

        this.label.setEffect(reflection);
    }
}

-Si vous utilisez Reflection, il semble que l'objet se reflète sur le sol. ) L'effet peut être appliqué --Avec topOffset, spécifiez la position relative (offset) de la partie supérieure de la partie réfléchie. --Spécifiez la transparence de la partie supérieure de la partie réfléchie avec topOpacity --Utilisez fraction pour spécifier le pourcentage de la partie à refléter ( 0,0 à 1,0, la valeur par défaut est 0,75, c'est-à-dire que 75% seulement sont reflétés) --Avec bottomOpacity, spécifiez la transparence en bas de la partie réfléchie.

Combinez des effets visuels

** Résultat d'exécution **

javafx.jpg

La mise en oeuvre

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;
import javafx.scene.effect.DropShadow;
import javafx.scene.effect.Reflection;
import javafx.scene.paint.Color;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    
    @FXML
    private Label label;
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Reflection reflection = new Reflection();
        reflection.setTopOffset(-40.0);
        reflection.setTopOpacity(0.8);
        reflection.setFraction(0.8);

        DropShadow dropShadow = new DropShadow();
        dropShadow.setOffsetX(5.0);
        dropShadow.setOffsetY(5.0);
        dropShadow.setColor(Color.valueOf("AA0"));

        reflection.setInput(dropShadow);

        this.label.setEffect(reflection);
    }
}

référence

médias

Comment fonctionnent les fichiers multimédias

Code codec

――Si les données multimédias sont laissées telles quelles, même quelques minutes de contenu seront volumineuses, telles que des centaines de Mo ou Go.

récipient

--Dans le fichier multimédia, plusieurs données multimédias sont stockées dans un état combiné.

Formats d'encodage et conteneurs pris en charge par JavaFX

Format d'encodage

Format d'encodage voix/Vidéo
PCM voix
MP3 voix
AAC voix
H.264/AVC Vidéo
VP6 Vidéo

PCM

--Abréviation pour la modulation de code d'impulsion

** MP3 (format encodé) **

AAC

--Abréviation pour le codage audio avancé

H.264/AVC

VP6

Format du conteneur

récipient Format d'encodage(Vidéo) Format d'encodage(voix)
AIFF - PCM
WAV - PCM
MP3 - MP3
FLV VP6 MP3
MP4 H.264/AVC AAC

AIFF

--Abréviation pour le format de fichier d'échange audio

WAV

--Abréviation pour le format audio de forme d'onde

** MP3 (conteneur) **

FLV

MP4

Il semble que les formats d'encodage largement utilisés (MP3, ACC, H.264) soient au moins supportés.

Lire des fichiers multimédias dans des conteneurs d'encodage non pris en charge

référence

Lire le fichier multimédia

package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

import java.nio.file.Path;
import java.nio.file.Paths;

public class MainController {
    
    private MediaPlayer player;
    
    @FXML
    public void start() {
        Path music = Paths.get("./media/music.m4a");
        Media media = new Media(music.toUri().toString());
        this.player = new MediaPlayer(media);
        this.player.play();
    }
}

Transition d'état MediaPlayer

javafx.png

Statut La description
UNKNOWN Immédiatement après la création et la lecture n'est pas terminée
READY Prêt à charger et à jouer
PLAYING État de lecture
PAUSED État en pause
PLAYINGLorsque vous revenez à, il reprendra là où il a été arrêté.
STOPPED État arrêté
PLAYINGJouez depuis le début en revenant à
STALLED La lecture en streaming ne contient pas suffisamment d'informations pour continuer à jouer
HALTED Un état dans lequel le lecteur ne peut pas jouer en raison d'une erreur
DIPOSED Les joueurs sont détruits et les ressources sont libérées

--MediaPlayer effectue une transition d'état comme indiqué dans la figure ci-dessus.

Lire le fichier de ressources

Structure des dossiers


`-src/main/
  |-java/
  | `-sample/javafx/
  |   :
  `-resources/
    |-...
    `-media/
      `-music.mp3
package sample.javafx;

import javafx.fxml.Initializable;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        URL url = MainController.class.getResource("/media/music.mp3");
        Media media = new Media(url.toString());
        MediaPlayer player = new MediaPlayer(media);
        
        player.play();
    }
}

Jouer à plusieurs reprises

package sample.javafx;

...

public class MainController implements Initializable {
    private MediaPlayer player;
    
    ...

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Path music = Paths.get("./media/music.mp3");
        Media media = new Media(music.toUri().toString());
        this.player = new MediaPlayer(media);

        this.player.setCycleCount(3);
    }
}

--setCycleCount (int) peut être utilisé pour spécifier le nombre de fois qu'un fichier multimédia doit être lu en continu. --Si vous spécifiez MediaPlayer.INDEFINITE, il sera lu en continu indéfiniment.

Temps de lecture

package sample.javafx;

...
import javafx.util.Duration;

public class MainController implements Initializable {
    private MediaPlayer player;
    
    ...

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Path music = Paths.get("./media/music.mp3");
        Media media = new Media(music.toUri().toString());
        this.player = new MediaPlayer(media);
        
        this.player.setCycleCount(3);

        Duration cycleDuration = this.player.getCycleDuration();
        System.out.println("cycleDuration=" + cycleDuration);
        Duration totalDuration = this.player.getTotalDuration();
        System.out.println("totalDuration=" + totalDuration);

        this.player.setOnReady(() -> {
            System.out.println("[onReady] cycleDuration=" + this.player.getCycleDuration());
            System.out.println("[onReady] totalDuration=" + this.player.getTotalDuration());
        });
    }
}

Résultat d'exécution


cycleDuration=UNKNOWN
totalDuration=UNKNOWN
[onReady] cycleDuration=9541.0 ms
[onReady] totalDuration=28623.0 ms

Classe de durée

package sample.javafx.property;

import javafx.util.Duration;

public class Main {
    public static void main(String[] args) {
        Duration duration = new Duration(90000.0);
        System.out.println("duration=" + duration);
        System.out.println("duration.seconds=" + duration.toSeconds());
        System.out.println("duration.minutes=" + duration.toMinutes());

        Duration oneMinute = Duration.seconds(60);
        System.out.println("oneMinute=" + oneMinute);
        System.out.println("oneMinute.minutes=" + oneMinute.toMinutes());
    }
}

Résultat d'exécution


duration=90000.0 ms
duration.seconds=90.0
duration.minutes=1.5

oneMinute=60000.0 ms
oneMinute.minutes=1.0

Format de chaîne

package sample.javafx.property;

import javafx.util.Duration;

public class Main {
    public static void main(String[] args) {
        Duration duration = new Duration(12294027.0);
        String text = format(duration);
        System.out.println("text=" + text);
    }
    
    public static String format(Duration duration) {
        long millis = (long) duration.toMillis();
        long absMillis = Math.abs(millis);
        long hours = absMillis / 3_600_000;
        long minutes = (absMillis / 60_000) % 60;
        long seconds = (absMillis / 1_000) % 60;
        long milliseconds = absMillis % 1_000;

        return (millis < 0 ? "-" : "") + String.format("%02d:%02d:%02d.%03d", hours, minutes, seconds, milliseconds);
    }
}

Résultat d'exécution


text=03:24:54.027

Spécifiez la plage à jouer

package sample.javafx;

...

public class MainController implements Initializable {
    private MediaPlayer player;
    
    ...

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Path music = Paths.get("./media/music.mp3");
        Media media = new Media(music.toUri().toString());
        this.player = new MediaPlayer(media);
        
        this.player.setCycleCount(3);
        
        this.player.setStartTime(Duration.seconds(1));
        this.player.setStopTime(Duration.seconds(3));

        this.player.setOnReady(() -> {
            System.out.println("cycleDuration=" + this.player.getCycleDuration());
            System.out.println("totalDuration=" + this.player.getTotalDuration());
        });
    }
}

Résultat d'exécution


cycleDuration=2000.0 ms
totalDuration=6000.0 ms

Métadonnées

Diverses métadonnées telles que le chanteur, le nom de l'album, les paroles et la pochette d'album peuvent être ajoutées au fichier MP3. Vous pouvez accéder à ces métadonnées à partir de JavaFX (les métadonnées FLV semblent être disponibles, mais omises ici).

package sample.javafx;

import javafx.collections.ObservableMap;
import javafx.fxml.Initializable;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;

...

public class MainController implements Initializable {
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Path music = Paths.get("./media/music2.mp3");
        Media media = new Media(music.toUri().toString());
        MediaPlayer player = new MediaPlayer(media);
        
        player.setOnReady(() -> {
            ObservableMap<String, Object> metadata = media.getMetadata();

            metadata.forEach((key, value) -> {
                System.out.println("key=" + key + ", value=" + value);
            });
        });
    }
}

Résultat d'exécution


key=comment-1, value=iTunPGAP[eng]=0  
key=album artist, value=ClariS 
key=comment-0, value=[eng]=  
key=image, value=javafx.scene.image.Image@1ca0c8fb
key=artist, value=ClariS 
key=raw metadata, value={ID3=java.nio.HeapByteBufferR[pos=779441 lim=789675 cap=789675]}
key=composer, value=ryo 
key=album, value=SHIORI 
key=comment-3, value=iTunSMPB[eng]= 00000000 00000210 0000096C 0000000000E5EE04 00000000 007D1469 00000000 00000000 00000000 00000000 00000000 00000000 
key=comment-2, value=iTunNORM[eng]= 000022CF 000021D8 0000C4E5 0000FF84 0002901F 0004CBF5 0000823D 0000832E 0004A049 00049D87 
key=genre, value=  
key=title, value=Une histoire que vous ne connaissez pas

Changer la position de lecture

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.util.Duration;

...

public class MainController implements Initializable {
    
    private MediaPlayer player;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Path music = Paths.get("./media/music2.mp3");
        Media media = new Media(music.toUri().toString());
        this.player = new MediaPlayer(media);
        this.player.play();
    }
    
    @FXML
    public void seek() {
        this.player.seek(Duration.minutes(2));
    }
}

--Utilisez la méthode seek (Duration seekTime) pour changer la position de lecture --Le «seekTime» spécifié dans l'argument spécifie la période à partir du début des données multimédia. --Même si vous spécifiez startTime, ce n'est pas la période de startTime (probablement) ――Il ressemble à ↓ sur la figure

javafx.jpg

Afficher la vidéo

javafx.jpg

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaView;

import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private MediaView mediaView;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Path music = Paths.get("./media/movie.mp4");
        Media media = new Media(music.toUri().toString());
        MediaPlayer player = new MediaPlayer(media);
        
        this.mediaView.setMediaPlayer(player);
        player.play();
    }
}

--Utilisez MediaView pour afficher la vidéo

AudioClip Pour les petits effets sonores avec un temps de lecture court, il est plus efficace d'utiliser ʻAudioClip que Media`.

package sample.javafx;

import javafx.fxml.Initializable;
import javafx.scene.media.AudioClip;

import java.net.URL;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ResourceBundle;

public class MainController implements Initializable {

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Path music = Paths.get("./media/music2.mp3");
        AudioClip audioClip = new AudioClip(music.toUri().toString());
        audioClip.play();
    }
}

--Comme avec Media, passez une chaîne URL à l'argument constructeur pour créer une instance.

La différence avec «Media» est la suivante.

--Lecture avec un délai minimum (lecture immédiate)

Voir Javadoc pour plus de détails.

Essayez de créer un lecteur de musique simple

Créons un simple lecteur de musique tout en utilisant le contenu jusqu'à présent.

javafx.jpg

package sample.javafx;

import javafx.beans.binding.BooleanBinding;
import javafx.beans.binding.StringBinding;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.media.Media;
import javafx.scene.media.MediaPlayer;
import javafx.scene.media.MediaPlayer.Status;
import javafx.util.Duration;

import java.net.URL;
import java.nio.file.Paths;
import java.util.ResourceBundle;
import java.util.stream.Stream;

public class SimpleMusicPlayerController implements Initializable {

    private MediaPlayer player;
    
    @FXML
    private Button playButton;
    @FXML
    private Button stopButton;
    @FXML
    private Button pauseButton;

    @FXML
    private Label volumeLabel;
    @FXML
    private Label totalTimeLabel;
    @FXML
    private Label currentTimeLabel;
    
    @FXML
    private Slider volumeSlider;
    @FXML
    private Slider seekSlider;

    @FXML
    public void play() {
        this.player.play();
    }

    @FXML
    public void stop() {
        this.player.stop();
    }

    @FXML
    public void pause() {
        this.player.pause();
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Media media = new Media(Paths.get("./media/music.m4a").toUri().toString());
        this.player = new MediaPlayer(media);

        //Étiquette de volume
        this.volumeLabel.textProperty().bind(this.player.volumeProperty().asString("%.2f"));
        //Étiquette de durée totale de lecture
        this.player.setOnReady(() -> {
            Duration total = this.player.getTotalDuration();
            this.totalTimeLabel.setText(this.format(total));
        });
        //Étiquette de la durée de lecture actuelle
        this.currentTimeLabel.textProperty().bind(new StringBinding() {
            {bind(player.currentTimeProperty());}
            
            @Override
            protected String computeValue() {
                Duration currentTime = player.getCurrentTime();
                return format(currentTime);
            }
        });
        
        //Bouton jouer
        this.playButton.disableProperty().bind(new BooleanBinding() {
            {bind(player.statusProperty());}
            
            @Override
            protected boolean computeValue() {
                boolean playable = playerStatusIsAnyOf(Status.READY, Status.PAUSED, Status.STOPPED);
                return !playable;
            }
        });
        //Bouton d'arrêt
        this.stopButton.disableProperty().bind(new BooleanBinding() {
            {bind(player.statusProperty());}
            
            @Override
            protected boolean computeValue() {
                boolean stoppable = playerStatusIsAnyOf(Status.PLAYING, Status.PAUSED, Status.STALLED);
                return !stoppable;
            }
        });
        //Bouton Pause
        this.pauseButton.disableProperty().bind(new BooleanBinding() {
            {bind(player.statusProperty());}

            @Override
            protected boolean computeValue() {
                boolean pausable = playerStatusIsAnyOf(Status.PLAYING, Status.STALLED);
                return !pausable;
            }
        });
        
        //Curseur de volume
        this.player.volumeProperty().bind(this.volumeSlider.valueProperty());
        this.volumeSlider.setValue(1);
        
        //Barre de recherche
        this.seekSlider.valueProperty().addListener((value, oldValue, newValue) -> {
            if (seekSliderIsChanging()) {
                Duration seekTime = this.player.getTotalDuration().multiply((Double) newValue);
                this.player.seek(seekTime);
            }
        });

        this.player.currentTimeProperty().addListener((value, oldValue, newValue) -> {
            if (!seekSliderIsChanging()) {
                double totalDuration = this.player.getTotalDuration().toMillis();
                double currentTime = newValue.toMillis();
                this.seekSlider.setValue(currentTime / totalDuration);
            }
        });

        //Post-traitement à la fin de la lecture
        this.player.setOnEndOfMedia(() -> {
            this.player.seek(this.player.getStartTime());
            this.player.stop();
        });
    }
    
    private boolean playerStatusIsAnyOf(Status... statuses) {
        Status status = this.player.getStatus();
        return Stream.of(statuses).anyMatch(candidate -> candidate.equals(status));
    }

    private boolean seekSliderIsChanging() {
        return this.seekSlider.isValueChanging() || this.seekSlider.isPressed();
    }
    
    private String format(Duration duration) {
        long millis = (long) duration.toMillis();
        long minutes = (millis / 60_000) % 60;
        long seconds = (millis / 1_000) % 60;

        return String.format("%02d:%02d", minutes, seconds);
    }
}

Affichage de la durée de lecture actuelle

        //Étiquette de la durée de lecture actuelle
        this.currentTimeLabel.textProperty().bind(new StringBinding() {
            {bind(player.currentTimeProperty());}
            
            @Override
            protected String computeValue() {
                Duration currentTime = player.getCurrentTime();
                return format(currentTime);
            }
        });

    ...
    
    private String format(Duration duration) {
        long millis = (long) duration.toMillis();
        long minutes = (millis / 60_000) % 60;
        long seconds = (millis / 1_000) % 60;

        return String.format("%02d:%02d", minutes, seconds);
    }

Contrôle des boutons

        //Bouton jouer
        this.playButton.disableProperty().bind(new BooleanBinding() {
            {bind(player.statusProperty());}
            
            @Override
            protected boolean computeValue() {
                boolean playable = playerStatusIsAnyOf(Status.READY, Status.PAUSED, Status.STOPPED);
                return !playable;
            }
        });
        //Bouton d'arrêt
        this.stopButton.disableProperty().bind(new BooleanBinding() {
            {bind(player.statusProperty());}
            
            @Override
            protected boolean computeValue() {
                boolean stoppable = playerStatusIsAnyOf(Status.PLAYING, Status.PAUSED, Status.STALLED);
                return !stoppable;
            }
        });
        //Bouton Pause
        this.pauseButton.disableProperty().bind(new BooleanBinding() {
            {bind(player.statusProperty());}

            @Override
            protected boolean computeValue() {
                boolean pausable = playerStatusIsAnyOf(Status.PLAYING, Status.STALLED);
                return !pausable;
            }
        });

    ...

    private boolean playerStatusIsAnyOf(Status... statuses) {
        Status status = this.player.getStatus();
        return Stream.of(statuses).anyMatch(candidate -> candidate.equals(status));
    }

--Utilisez status of MediaPlayer pour contrôler disabled du bouton --Déterminez l'état dans lequel chaque bouton peut être cliqué et définissez son refus sur la propriété disabled.

Curseur de volume

javafx.jpg

        //Étiquette de volume
        this.volumeLabel.textProperty().bind(this.player.volumeProperty().asString("%.2f"));

        ...

        //Curseur de volume
        this.player.volumeProperty().bind(this.volumeSlider.valueProperty());
        this.volumeSlider.setValue(1);

--Le volume peut être ajusté avec la propriété volume

Barre de recherche

  1. Ajustez la position du curseur en fonction de la durée de lecture
  2. Ajustez la durée de lecture lorsque l'utilisateur actionne le curseur

javafx.jpg

Temps de lecture → barre de recherche

        this.player.currentTimeProperty().addListener((value, oldValue, newValue) -> {
            if (!seekSliderIsChanging()) {
                double totalDuration = this.player.getTotalDuration().toMillis();
                double currentTime = newValue.toMillis();
                this.seekSlider.setValue(currentTime / totalDuration);
            }
        });

    ...

    private boolean seekSliderIsChanging() {
        return this.seekSlider.isValueChanging() || this.seekSlider.isPressed();
    }

Barre de recherche → Durée de lecture

        this.seekSlider.valueProperty().addListener((value, oldValue, newValue) -> {
            if (seekSliderIsChanging()) {
                Duration seekTime = this.player.getTotalDuration().multiply((Double) newValue);
                this.player.seek(seekTime);
            }
        });

--Enregistrez un écouteur dans la propriété value du curseur --Multipliez la nouvelle valeur du curseur («0.0» à «1.0») par le temps de lecture total pour trouver l'heure de destination («seekTime»). --Et utilisez cette valeur pour exécuter seek () de MediaPlayer

Traitement à la fin de la lecture

        //Post-traitement à la fin de la lecture
        this.player.setOnEndOfMedia(() -> {
            this.player.seek(this.player.getStartTime());
            this.player.stop();
        });

-- status reste PLAYING lorsque le temps de lecture atteint stopTime

référence

menu

barre de menu

javafx.jpg

** Résultat d'exécution **

javafx.jpg

La description

--Utilisez MenuBar pour créer une barre de menus

Mettre en œuvre le processus lorsqu'un menu est sélectionné

javafx.jpg

package sample.javafx;

import javafx.fxml.FXML;

public class MainController {
    
    @FXML
    public void open() {
        System.out.println(""Ouvrir" est sélectionné");
    }
}

Séparateur

javafx.jpg

** Résultat d'exécution **

javafx.jpg

Sous-menu

javafx.jpg

** Résultat d'exécution **

javafx.jpg

Attribuer une touche de raccourci

javafx.jpg

** Résultat d'exécution **

javafx.jpg

Memonic

javafx.jpg

package sample.javafx;

import javafx.fxml.FXML;

public class MainController {
    
    @FXML
    public void open() {
        System.out.println("open");
    }
    
    @FXML
    public void close() {
        System.out.println("close");
    }
    
    @FXML
    public void fullScreen() {
        System.out.println("full screen");
    }
}

Chaque menu est associé à une méthode de contrôleur.

** Résultat d'exécution **

javafx.gif

――C'est un peu difficile à comprendre, mais vous ne pouvez utiliser le menu qu'avec le clavier sans utiliser la souris. ――Comme vous pouvez le voir en le touchant, un mouvement honnête est suspect

javafx.jpg

javafx.jpg

  1. Cochez "Analyse mnémotechnique"
  2. Mettez un trait de soulignement (_) dans Texte avant l'alphabet auquel vous souhaitez attribuer un mnémonique.

Attribuer des mnémoniques au menu japonais

javafx.jpg

** Résultat d'exécution **

javafx.jpg

Ajouter une icône

javafx.jpg

Structure des dossiers


`-src/main/
  |-java/
  | :
  `-resources/
    `-img/
      `-open.png
package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.MenuItem;
import javafx.scene.image.ImageView;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private MenuItem openMenuItem;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        ImageView image = new ImageView("/img/open.png ");
        this.openMenuItem.setGraphic(image);
    }
}

** Résultat d'exécution **

javafx.jpg

Vérifier le menu

javafx.jpg

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.CheckMenuItem;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private CheckMenuItem checkMenuItem;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.checkMenuItem.selectedProperty().addListener((value, oldValue, newValue) -> {
            System.out.println(newValue ? "Vérifié" : "Non coché");
        });
    }
}

** Résultat d'exécution **

javafx.gif

Menu bouton radio

javafx.jpg

package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.RadioMenuItem;
import javafx.scene.control.ToggleGroup;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private ToggleGroup group1;
    @FXML
    private RadioMenuItem hogeRadioMenuItem;
    @FXML
    private RadioMenuItem fugaRadioMenuItem;
    @FXML
    private RadioMenuItem piyoRadioMenuItem;
    
    @FXML
    private ToggleGroup group2;
    @FXML
    private RadioMenuItem fizzRadioMenuItem;
    @FXML
    private RadioMenuItem buzzRadioMenuItem;
    

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.hogeRadioMenuItem.setUserData("hoge");
        this.fugaRadioMenuItem.setUserData("fuga");
        this.piyoRadioMenuItem.setUserData("piyo");
        this.group1.selectedToggleProperty().addListener((toggle, oldValue, newValue) -> {
            if (newValue != null) {
                System.out.println("[group1] " + newValue.getUserData());
            }
        });
        
        this.fizzRadioMenuItem.setUserData("fizz");
        this.buzzRadioMenuItem.setUserData("buzz");
        this.group2.selectedToggleProperty().addListener((toggle, oldValue, newValue) -> {
            if (newValue != null) {
                System.out.println("[group2] " + newValue.getUserData());
            }
        });
    }
}

** Résultat d'exécution **

javafx.gif

Toggle Group sur fxml


  <RadioMenuItem fx:id="hogeRadioMenuItem" text="Hoge">
     <toggleGroup>
        <ToggleGroup fx:id="group1" />
     </toggleGroup>
  </RadioMenuItem>

--fx: id est l'identifiant spécifié par Toggle Group

Toggle Injection de groupe


    @FXML
    private ToggleGroup group1;
        this.hogeRadioMenuItem.setUserData("hoge");
        this.fugaRadioMenuItem.setUserData("fuga");
        this.piyoRadioMenuItem.setUserData("piyo");
        this.group1.selectedToggleProperty().addListener((toggle, oldValue, newValue) -> {
            if (newValue != null) {
                System.out.println("[group1] " + newValue.getUserData());
            }
        });

--Le menu sélectionné est identifié par la valeur enregistrée dans ʻuserData de l'instance d'élément de menu. --ʻUserData est de type ʻObject`, vous pouvez donc entrer n'importe quelle valeur «Je ne l'utilise généralement pas beaucoup, mais il peut être utilisé dans des situations comme celle-ci pour identifier les éléments sélectionnés dans un groupe.

Menu contextuel

javafx.jpg

** Résultat d'exécution **

javafx.jpg

référence

Agrandir la fenêtre

Main.java


package sample.javafx;

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

import java.net.URL;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        URL url = this.getClass().getResource("/main.fxml");
        FXMLLoader loader = new FXMLLoader(url);
        Parent root = loader.load();

        Scene scene = new Scene(root);
        primaryStage.setScene(scene);
        
        primaryStage.setMaximized(true); //★ Maximiser

        primaryStage.show();
    }
}

Ouvrir une autre fenêtre

De base

Structure des dossiers


`-src/main/
  |-java/
  | `-sample/javafx/
  |   |-MainController.java
  |   `-OtherController.java
  |
  `-resources/
    |-main.fxml
    `-other.fxml

other.fxml

javafx.jpg

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;

public class MainController {
    
    @FXML
    public void openOtherWindow() throws IOException {
        //Chargez fxml et
        URL fxml = this.getClass().getResource("/other.fxml");
        FXMLLoader loader = new FXMLLoader(fxml);
        Pane pane = loader.load();
        
        //Créer une scène
        Scene scene = new Scene(pane);
        
        //Enregistrez la scène sur la scène
        Stage stage = new Stage();
        stage.setScene(scene);
        
        //afficher
        stage.show();
    }
}

--Utilisez FXMLLoader pour charger fxml

Obtenez le contrôleur

MainController.java


package sample.javafx;

public class MainController {
    
    public void hello() {
        System.out.println("Hello MainController!!");
    }
}

Main.java


package sample.javafx;

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

import java.net.URL;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        URL url = this.getClass().getResource("/main.fxml");
        FXMLLoader loader = new FXMLLoader(url);
        Parent root = loader.load();
        
        MainController controller = loader.getController();
        controller.hello();
        
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);

        primaryStage.show();
    }
}

** Résultat d'exécution **

Hello MainController!!

Paramètres du propriétaire

** Comportement lorsqu'aucun propriétaire n'est défini **

javafx.gif

--Si vous n'avez pas défini le propriétaire (ʻowner) dans Stage`, la fermeture de la première fenêtre ouverte ne fermera pas la fenêtre ouverte plus tard.

** Définir le propriétaire **

Main.java


package sample.javafx;

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

import java.net.URL;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        URL url = this.getClass().getResource("/main.fxml");
        FXMLLoader loader = new FXMLLoader(url);
        Parent root = loader.load();

        MainController controller = loader.getController();
        controller.setStage(primaryStage); //★ Réglez la scène du contrôleur sur le contrôleur
        
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);

        primaryStage.show();
    }
}

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;

public class MainController {
    
    private Stage stage; //★ L'étape de ce contrôleur est définie

    public void setStage(Stage stage) {
        this.stage = stage;
    }

    @FXML
    public void openOtherWindow() throws IOException {
        URL fxml = this.getClass().getResource("/other.fxml");
        FXMLLoader loader = new FXMLLoader(fxml);
        Pane pane = loader.load();

        Scene scene = new Scene(pane);

        Stage stage = new Stage();
        stage.setScene(scene);
        stage.initOwner(this.stage); //★ Définir le propriétaire

        stage.showAndWait();
    }
}

** Résultat d'exécution **

javafx.gif

modal

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Modality;
import javafx.stage.Stage;

import java.io.IOException;
import java.net.URL;

public class MainController {

    private Stage stage;

    public void setStage(Stage stage) {
        this.stage = stage;
    }

    @FXML
    public void openOtherWindow() throws IOException {
        URL fxml = this.getClass().getResource("/other.fxml");
        FXMLLoader loader = new FXMLLoader(fxml);
        Pane pane = loader.load();

        Scene scene = new Scene(pane);

        Stage stage = new Stage();
        stage.setScene(scene);
        stage.initOwner(this.stage);
        stage.initModality(Modality.WINDOW_MODAL); //★ Définir la modalité

        stage.showAndWait();
    }
}

** Résultat d'exécution **

javafx.gif

--ʻInitModality () `peut rendre la scène modale

constant sens
NONE Ne bloquez pas les autres fenêtres
WINDOW_MODAL Bloquer la fenêtre du propriétaire
(Ne bloquez pas les fenêtres autres que celles du propriétaire)
APPLICATION_MODAL Bloquer toutes les fenêtres

référence

Incorporer un autre fxml

De base

** Structure des dossiers **

`-src/main/resources/
  |-embedded.fxml
  `-main.fxml

** Incorporer fxml (Embedded.fxml) **

javafx.jpg

** Intégration de la destination fxml (main.fxml) **

javafx.jpg

main.fxml


<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.text.Font?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.151" xmlns:fx="http://javafx.com/fxml/1">
   <top>
      <Label text="Main" BorderPane.alignment="CENTER">
         <font>
            <Font size="40.0" />
         </font>
      </Label>
   </top>
   <center>
      <fx:include source="embedded.fxml" />
   </center>
</BorderPane>

La description

--Spécifiez le fichier fxml à incorporer dans l'attribut source de <fx: include>

** Les tentatives d'inclusion dans SceneBuilder échouent **

javafx.jpg

--SceneBuilder semble avoir un menu pour inclure fxml (Fichier-> Inclure-> FXML) --Mais lorsque j'essaie d'inclure fxml en utilisant ceci, il ne parvient pas à inclure avec Échec d'inclusion '***. ―― Parfois tu réussis, mais je ne suis pas sûr --Il ne semble pas que SceneBuilder ne prenne pas en charge l'inclusion, donc si vous modifiez le fxml directement à la main et écrivez <fx: include>, il affichera un aperçu correctement. ――Je ne connais pas la cause, mais pour le moment, il semble prudent d'écrire uniquement <fx: include>`.

Obtenez le contrôleur fxml intégré

organisation des fichiers


`-src/main/
  |-java/
  | `-sample/javafx/
  |   |-Main.java
  |   |-MainController.java
  |   `-EmbeddedController.java
  `-resources/
    |-main.fxml
    `-embedded.fxml

embedded.fxml

javafx.jpg

EmbeddedController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.scene.control.Label;

public class EmbeddedController {
    @FXML
    private Label label;
    
    public void setMessage(String message) {
        this.label.setText(message);
    }
}

main.fxml

javafx.jpg

main.fxml


...

<BorderPane fx:id="pane" ... fx:controller="sample.javafx.MainController">
   <top>
      <Label text="Main" BorderPane.alignment="CENTER">
         ...
      </Label>
   </top>
   <center>
      <fx:include fx:id="embedded" source="embedded.fxml" />
   </center>
</BorderPane>

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private EmbeddedController embeddedController;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        this.embeddedController.setMessage("Hello!!");
    }
}

** Résultat d'exécution **

javafx.jpg

La description

Afficher le fxml intégré dans une fenêtre distincte

embedded.fxml

javafx.jpg

EmbeddedController.java


package sample.javafx;

import javafx.beans.binding.StringBinding;
import javafx.beans.property.IntegerProperty;
import javafx.beans.property.SimpleIntegerProperty;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Label;

import java.net.URL;
import java.util.ResourceBundle;

public class EmbeddedController implements Initializable {
    
    private IntegerProperty count = new SimpleIntegerProperty(0);
    
    @FXML
    private Label label;
    
    @FXML
    public void countUp() {
        int now = this.count.get();
        this.count.set(now + 1);
    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        StringBinding output = this.count.asString("count = %d");
        this.label.textProperty().bind(output);
    }
}

main.fxml

javafx.jpg

main.fxml


<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.Button?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.layout.BorderPane?>
<?import javafx.scene.text.Font?>

<BorderPane maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="200.0" prefWidth="300.0" xmlns="http://javafx.com/javafx/8.0.151" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.javafx.MainController">
   <fx:define>
      <fx:include fx:id="embeddedPane" source="embedded.fxml" />
   </fx:define>
   <top>
      <Label text="Main" BorderPane.alignment="CENTER">
         <font>
            <Font size="40.0" />
         </font>
      </Label>
   </top>
   <center>
      <Button mnemonicParsing="false" onAction="#openWindow" text="Open Window" BorderPane.alignment="CENTER">
         <font>
            <Font size="25.0" />
         </font>
      </Button>
   </center>
</BorderPane>

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Modality;
import javafx.stage.Stage;

import java.net.URL;
import java.util.ResourceBundle;

public class MainController implements Initializable {
    @FXML
    private Pane embeddedPane;
    
    private Stage embeddedWindow;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Scene scene = new Scene(this.embeddedPane);
        this.embeddedWindow = new Stage();
        this.embeddedWindow.setScene(scene);
        this.embeddedWindow.initModality(Modality.APPLICATION_MODAL);
    }
    
    @FXML
    public void openWindow() {
        this.embeddedWindow.showAndWait();
    }
}

** Résultat d'exécution **

javafx.gif

La description

main.fxml


...

<BorderPane ... fx:controller="sample.javafx.MainController">
   <fx:define>
      <fx:include fx:id="embeddedPane" source="embedded.fxml" />
   </fx:define>
   ...
</BorderPane>

MainController.java


package sample.javafx;

...

public class MainController implements Initializable {
    @FXML
    private Pane embeddedPane;
    
    private Stage embeddedWindow;

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        Scene scene = new Scene(this.embeddedPane);
        this.embeddedWindow = new Stage();
        this.embeddedWindow.setScene(scene);
        this.embeddedWindow.initModality(Modality.APPLICATION_MODAL);
    }
    
    @FXML
    public void openWindow() {
        this.embeddedWindow.showAndWait();
    }
}

--DI le FXML intégré (ʻembedded.fxml) au contrôleur intégré (MainController.java) avec @ FXML (ʻembeddedPane) --Si vous créez une Stage en utilisant ʻembeddedPane, vous pouvez afficher la fenêtre avec show () ʻou showAndWait ().

référence

Boîte de dialogue de sélection de fichier

javafx.jpg

--Cliquez sur le bouton pour afficher la boîte de dialogue de sélection de fichier

Main.java


package sample.javafx;

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

import java.net.URL;

public class Main extends Application {

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

    @Override
    public void start(Stage primaryStage) throws Exception {
        URL url = this.getClass().getResource("/main.fxml");
        FXMLLoader loader = new FXMLLoader(url);

        Parent root = loader.load();
        MainController controller = loader.getController();
        controller.setStage(primaryStage);
        
        Scene scene = new Scene(root);
        primaryStage.setScene(scene);

        primaryStage.show();
    }
}

MainController.java


package sample.javafx;

import javafx.fxml.FXML;
import javafx.stage.FileChooser;
import javafx.stage.Stage;

import java.io.File;

public class MainController {
    
    private Stage stage;
    
    @FXML
    public void openFileDialog() {
        FileChooser chooser = new FileChooser();
        File file = chooser.showOpenDialog(this.stage);
        System.out.println("file=" + file);
    }

    public void setStage(Stage stage) {
        this.stage = stage;
    }
}

** Résultat d'exécution **

javafx.jpg

Boîte de dialogue affichée

javafx.jpg

Sélectionnez un fichier et cliquez sur "Ouvrir"

Sortie de la console


file=C:\Program Files\Java\jdk-9.0.1\README.html

La description

        FileChooser chooser = new FileChooser();
        File file = chooser.showOpenDialog(this.stage);
        System.out.println("file=" + file);

--Utilisez la classe FileChooser pour afficher la boîte de dialogue de sélection de fichier. --Lorsque vous créez une instance et exécutez la méthode showOpenDialog (Stage), une boîte de dialogue de sélection de fichier pour sélectionner un seul fichier s'ouvre. --Spécifiez le propriétaire Stage dans l'argumentStage --Si un propriétaire est spécifié, les opérations sur le propriétaire seront bloquées (deviendront modales) tant que la boîte de dialogue de sélection de fichier est ouverte.

Définir le titre

        FileChooser chooser = new FileChooser();
        chooser.setTitle("Juste Sentaku");
        File file = chooser.showOpenDialog(this.stage);

** Résultat d'exécution **

javafx.jpg

Spécifiez le répertoire à afficher initialement

        FileChooser chooser = new FileChooser();
        chooser.setInitialDirectory(new File("C:/Program Files/java/jdk-9.0.1"));
        File file = chooser.showOpenDialog(this.stage);

--setInitialDirectory (File) vous permet de spécifier le répertoire à afficher lorsque la boîte de dialogue est ouverte. --Si rien n'est spécifié, le répertoire par défaut est affiché pour chaque environnement (OS). ――Il semble que la classe FileChooser elle-même n'ait pas de fonction pour enregistrer le répertoire (fichier) ouvert la dernière fois. --Ainsi, si vous voulez rouvrir le répertoire précédemment ouvert, vous pouvez le faire en vous rappelant le répertoire ouvert séparément et en spécifiant le répertoire avec setInitialDirectory () lors de la nouvelle ouverture de la boîte de dialogue.

Affinez les fichiers sélectionnables par extension

package sample.javafx;

import javafx.collections.ObservableList;
import javafx.stage.FileChooser.ExtensionFilter;
...

public class MainController {
    
    private Stage stage;
    
    @FXML
    public void openFileDialog() {
        FileChooser chooser = new FileChooser();
        
        ObservableList<ExtensionFilter> extensionFilters = chooser.getExtensionFilters();
        extensionFilters.add(new ExtensionFilter("N'importe quoi", "*.*"));
        extensionFilters.add(new ExtensionFilter("Avec juste l'image", "*.jpg ", "*.jpeg ", "*.png ", "*.gif"));
        extensionFilters.add(new ExtensionFilter("Vidéo des jours de pluie", "*.mp4"));
        
        File file = chooser.showOpenDialog(this.stage);
        System.out.println("file=" + file);
    }

    ...
}

** Résultat d'exécution **

javafx.jpg

Ouvre une boîte de dialogue dans laquelle vous pouvez sélectionner plusieurs fichiers

        FileChooser chooser = new FileChooser();
        List<File> files = chooser.showOpenMultipleDialog(this.stage);
        if (files == null) {
            System.out.println("files=" + files);
        } else {
            files.forEach(System.out::println);
        }

** Résultat d'exécution **

javafx.jpg

Sortie de la console


C:\Program Files\Java\jdk-9.0.1\COPYRIGHT
C:\Program Files\Java\jdk-9.0.1\README.html
C:\Program Files\Java\jdk-9.0.1\release

--Lorsque vous ouvrez une boîte de dialogue avec showOpenMultipleDialog (Stage), une boîte de dialogue dans laquelle vous pouvez sélectionner plusieurs fichiers s'ouvre.

Boîte de dialogue pour sélectionner où enregistrer le fichier

        FileChooser chooser = new FileChooser();
        File file = chooser.showSaveDialog(this.stage);
        System.out.println("file=" + file);

** Résultat d'exécution **

javafx.jpg

Sortie de la console


file=C:\Users\Public\hoge

Boîte de dialogue de sélection de répertoire

package sample.javafx;

import javafx.stage.DirectoryChooser;
...

public class MainController {
    
    private Stage stage;
    
    @FXML
    public void openFileDialog() {
        DirectoryChooser chooser = new DirectoryChooser();
        File directory = chooser.showDialog(this.stage);
        System.out.println("directory=" + directory);
    }

    ...
}

** Résultat d'exécution **

javafx.jpg

Sortie de la console


directory=C:\Users\Public\Downloads

--Utilisez DirectoryChooser pour ouvrir la boîte de dialogue de sélection d'un répertoire. --L'utilisation est la même que celle du FileChooser de base, sauf que la cible de sélection est un répertoire (tel que ʻinitialDirectory`).

référence

Alignez les boutons et les étiquettes sur la même largeur (hauteur) et disposez-les verticalement (horizontalement)

Apparence

javafx.jpg

Construction

javafx.jpg

** Réglage de la taille de chaque élément vertical **

javafx.jpg

--Utilisez VBox pour organiser verticalement avec la même largeur

Recommended Posts

Notes d'étude JavaFX
Notes d'étude de puzzle
Notes d'étude Docker
[Java] Notes d'étude
Notes d'étude de base de Maven
Notes d'étude de classe interne et de style lambda
JUnit 4 notes
Politique d'étude
note java
Notes de l'étude Spring Framework [Partie 1] Conteneur DI
Mémorandum JavaFx
notes synchronisées