Notes sur l'étude de JavaFX
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
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;
}
}
DoubleProperty
.DoubleProperty
lui-même est une interface, et SimpleDoubleProperty
est fourni en tant que classe d'implémentation.final
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.
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
.
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.
Binding
est désactivé jusqu'au recalculpackage 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]
ChangeListener
, il sera toujours calculé immédiatement.Binding
, mais est commun à toutes les implémentations de calcul de retard qui implémentent ʻObservableValue`.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
Binding
avec une interface fluide
--ʻA.add (3) .multiply (2) .subtract (4.0) .divide (3.0) `partieNumberExpression
est NumberBinding
, mais comme NumberBinding
est également une sous-interface de NumberExpression
, appelez Fluent API tel quel. Être capable depackage 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é
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
Binding
et crée sa propre classeBinding
.MyBinding
qui hérite de DoubleBinding
.
--Enregistrez le ʻObservablesurveillé avec la méthode
bind ()` qui existe dans la classe parent.enregistré avec
bind ()est changé,
computeValue ()` est appelé, donc implémentez-le pour que le résultat du calcul soit renvoyé.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.
bind ()
pour l'implémenter simplement.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
.
ReadOnly ** Wrapper
peut changer sa valeur normalement, mais l'objet obtenu par getReadOnlyProperty ()
est en lecture seule.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 **
Property
s sont bidirectionnels. Peut être liéLa méthode de liaison bidirectionnelle fournie pour «Property
D'autre part, StringProperty a les deux méthodes suivantes pour la liaison bidirectionnelle. A été ajouté.
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 **
Format
comme deuxième argumentStringProperty
sera défini sur une chaîne formatée avec Format
, et l'autre propriété sera définie sur le résultat analysé.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 **
-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
JavaFX fournit sa propre classe de collection, qui est une extension de la collection standard (List
ou Map
).
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] }
java.util.List
et possède une fonction de surveillance. A été ajouté pour réaliser
--Utilisez la méthode de fabrique de FXCollections
pour créer une instance
--FXCollections
est une classe utilitaire pour les collections JavaFX qui a des méthodes similaires à java.util.Collections
.
--ʻAddListener (ListChangeListener) peut enregistrer des écouteurs pour être averti des changements --ʻObservableList
hérite également de ʻObservable --Et ʻObservable
définit également une méthode avec le même nom ʻaddListener (InvalidationListener) `mais des arguments différents.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 }
next ()
** avant de pouvoir obtenir les modifications de l'objet Change
**
--Lorsque vous essayez d'obtenir des modifications sans appeler next ()
, ʻIllegalStateException` est levénext ()
.
--Si vous utilisez removeAll ()
décrit plus tard, plusieurs modifications peuvent être notifiées à la fois, donc si vous l'activez avec while
, vous pouvez vérifier toutes les modifications.
--Il existe des types de changements tels que "ajouter" et "supprimer"was *** ()
telle que wasAdded ()
ou wasRemoved ()
.
--Il y a les notes suivantes sur l'implémentation de l'auditeurChange
ne doit pas être utilisé dans un autre thread **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
getFrom ()
et getTo ()
pour obtenir l'index qui pointe vers l'endroit où l'élément a été ajouté.
--getFrom ()
est le premier index (y compris cet index) auquel l'élément a été ajouté.
--getTo ()
est le dernier index auquel l'élément a été ajouté (cet index n'est pas inclus)List
contenant uniquement les éléments ajoutés avec getAddedSubList ()
getAddedSize ()
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é
getFrom ()
renvoie le ** index de départ ** de l'élément supprimé dans la Liste
d'origine.getTo ()
renvoie la même valeur quegetFrom ()
(bien que je sois un peu méfiant si c'est OK dans la description Javadoc).Change
, il semble que vous ne puissiez pas lire getFrom () ʻet
getTo () ʻau moment de la suppression, mais qu'en est-il (en fait non confirmé)? ?)List
avec getRemoved ()
--getRemovedSize ()
renvoie le nombre d'éléments suppriméspackage 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é.
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
wasReplaced ()
devient true
quand wasAddedd () && wasRemoved ()
wasReplaced ()
ne peut jamais être true
--Lorsque wasReplaced ()
est true
, wasAddedd ()
et wasRemoved ()
sont toujours true
.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]
SortedList
) liée à la ʻObservableList d'origine en exécutant la méthode
sorted (). --Si vous modifiez la ʻObservalbeList
d'origine, l'état de SortedList
sera également mis à jour dans un état trié.
--SortedList
implémente ʻObservableList--Il existe également une méthode
sorted (Comparator)qui vous permet de passer
Comparator` comme argumentsetComparator (Comparator)
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]
FilteredList
) liée à la ʻObservableList d'origine en exécutant la méthode
filtered (Predicate). --Si vous changez l'original ʻObservalbeList
, l'état de FilteredList
sera également mis à jour dans un état filtré.setPredicate ()
--FilteredList
implémente ʻObservableList`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 de
Mapsurveillable --Utilisez
FXCollections.observableHashMap () pour créer une instance --
Change dans
Mapn'est pas aussi compliqué que
List`
List
, next () ʻest inutile --Il existe deux types de modifications: "ajouté (
wasAdded () )" et "supprimé (
wasRemoved () )". --La valeur ajoutée par
getValueAdded (), Vous pouvez obtenir la valeur supprimée avec
getValueRemoved ()`
--Si vous définissez une valeur différente pour une clé existante, les modifications "ajouter" et "supprimer" se produiront en même temps.Structure des dossiers
`-src/main/
|-java/sample/javafx/
| |-Main.java
| `-MainController.java
|
`-resources/
|-main.fxml
`-my-style.css
main.fxml
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 **
Main.java
Scene scene = new Scene(root);
scene.getStylesheets().add("my-style.css");
Scene.getStylesheets ()
pour obtenir une liste d'URL de feuille de style et ʻajouter () `dessus.[schéma:] [// autorité] [chemin]
--Si [schéma:]
est omis, seul [chemin]
est considéré.
--[chemin]
est traité comme ** par rapport à la racine du chemin de classe **my-style.css
.label {
-fx-background-color: skyblue;
}
Selector {propriété: valeur;}
--Cependant, les noms de propriétés commencent presque toujours par le préfixe -fx
.--Si vous définissez .xxxx
dans HTML CSS, il correspond à la balise avec class =" xxxx "
set.
.xxxx
correspond styleClass =" xxxx "
--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
.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).
Label
a une styleClass
de label
par défaut, qui est spécifiée dans le fichier CSS à l'aide d'un sélecteur utilisant .
(. .label
)
--Cliquez ici pour voir quelle classe par défaut est définie pour quel nœud [https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/doc-files/cssref.html] #nodes)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 **
--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
Label
, mais ʻunderline est la [propriété underline](https://docs.oracle.com) de la classe
Labeled, qui est la classe parente de la classe
Label. /javase/jp/8/javafx/api/javafx/scene/control/Labeled.html#fontProperty) est censé être spécifié. --Cependant, si les propriétés du nœud et les propriétés spécifiées dans CSS correspondent toujours exactement, ce n'est pas le cas. --Par exemple,
border et
background sont divisés en petites propriétés telles que
-fx-border-style et
-fx-background-colorau lieu de
-fx-border et
-fx-background`. A été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 **
--La classe de style de Scene Builder vous permet de définir n'importe quel attribut de styleClass pour un élément
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
styleClassavec
getStyleClass ()depuis le nœud --Tout attribut peut être ajouté à la liste par ʻadd ()
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 **
--Spécifiez file: [path]
pour spécifier un fichier CSS local
Path
ou File
, vous pouvez obtenir l'objet ʻURI avec la méthode
toURI () ,
toUri () et passer la
toString () `.Comment définir -fx-background-image
Structure des dossiers
|-image.jpg
`-src/main/
|-resources/
| |-img/
| | `-image.jpg
| |-my-style.css
| `-main.fxml
:
my-style.css
.my-class {
-fx-background-image: url('/img/image.jpg');
}
--Spécifier avec ʻurl`
schema
est omis, le fichier sera sous le chemin de classe.my-style.css
.my-class {
-fx-background-image: url('file:./image.jpg');
}
--Lors de la spécification d'un fichier local, utilisez file: [path]
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 **
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
Lorsqu'un événement se produit, le traitement est exécuté dans l'ordre suivant
[^ 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".
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));
}
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));
}
setOnMouseClicked (EventHandler)
etc.
--Ce gestionnaire d'événements est également enregistré lorsque le traitement d'événements est enregistré avec l'attribut ʻonMouseClicked` de FXML.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
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.
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 **
La description
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.
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.
Dragboard
obtenu par DragEvent.getDragboard ()
.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 **
La description
MainController.java
@FXML
public void onDragOver(DragEvent e) {
Dragboard dragboard = e.getDragboard();
if (dragboard.hasFiles()) {
e.acceptTransferModes(TransferMode.ANY);
}
e.consume();
}
DragEvent.getDragboard ()
et vérifier si celui qui a été glissé est un fichier avec Dragboard.hasFiles ()
est exécuté uniquement lorsque
hasFiles () est
true`, vous ne pouvez donc supprimer que des fichiers.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();
}
Dragboard.getFiles ()
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 **
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 ()
getString ()
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 **
La description
DRAG_ENTERED
et DRAG_EXITED
sur le nœud à supprimer.
--Mise en œuvre pour ajouter un style au nœud avec DRAG_ENTERED
et supprimer le style avec DRAG_EXITED
―― En faisant cela, vous pouvez exprimer plus clairement l'attente de chute.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 **
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
.
startDragAndDrop ()
du nœud pour commencer à faire glisser
--Dans l'argument, spécifiez le mode de transfert pris en charge en faisant glisser avec TransferMode
.
--Sur le côté du dépôt, le curseur de la souris devient une icône de dépôt uniquement lorsque le mode de transfert qui correspond au mode de transfert défini ici est spécifié avec ʻacceptTransferModes () . --Définissez le contenu de glissement sur
ClipboardContent et définissez ce
ClipboardContentsur
Dragboard. --En plus de
putString (), il y a
putHtml ()et
putFiles ()` etc.ClipboardContent
lui-même est une carte qui hérite de HashMap <DataFormat, Object>
, vous pouvez enregistrer n'importe quelle valeur.référence
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.
Alert alert = new Alert(Alert.AlertType.CONFIRMATION);
alert.showAndWait();
** Résultat d'exécution **
--Spécifiez le type de dialogue dans le constructeur --Type spécifie une constante de type énumération ʻAlertType
--CONFIRMATION
est une boîte de dialogue de confirmationshowAndWait ()
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]
showAndWait ()
est ʻOptional de
ButtonType, donc vous pouvez voir quel bouton a été sélectionné lors de la fermeture. --Retour de
showAndWait () quand une boîte de dialogue (telle qu'une boîte de dialogue avec seulement un bouton "oui") qui n'a qu'un bouton d'annulation [^ 1] ** est fermée par ʻESC
ou le bouton de fermeture de fenêtre. La valeur sera ʻOptional.empty () `resultConverter
est défini, mais cette zone est [Javadoc](https://docs.oracle.com/javase/jp/8/javafx/api/javafx/scene/control/ Voir Dialog.html)[^ 1]: ButtonData
est NON
ou CANCEL_CLOSE
Alert alert = new Alert(Alert.AlertType.CONFIRMATION, "Messeji");
alert.showAndWait();
INFORMATION
CONFIRMATION
WARNING
ERROR
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();
--ButtonType
peut être spécifié après le troisième argument du constructeur
ButtonType
, donc si c'est tout ce dont vous avez besoin, utilisez-le.ButtonType
s.ButtonBar
est utilisée pour le placement des boutons.
--ButtonBar
est une classe pour la disposition des boutons, et peut reproduire la disposition des boutons unique à chaque OS.ButtonBar
.ButtonData
est également défini dans la constante de la classe ButtonType
spécifiée dans le constructeur de ʻAlert`, et les boutons sont organisés en fonction de cette valeur.** 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 |
ButtonData
pour chaque système d'exploitation est dans Javadoc. Sont répertoriés** 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.
ButtonData
.ButtonType.CANCEL
dans le constructeur de ʻAlert, Puisque
ButtonData de
ButtonType.CANCEL est
CANCEL_CLOSE, le bouton d'annulation sera inséré dans la partie qui est
Cdans l'ordre de
ButtonBar`.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 **
Sortie de la console lorsque vous cliquez sur le bouton "Botan"
ButtonType [text=Bouton, buttonData=OTHER]
ButtonType
et en la passant au constructeur de ʻAlert. --La chaîne d'étiquette de bouton peut être spécifiée dans le constructeur
ButtonType. --Si rien n'est spécifié,
ButtonData devient ʻAUTRE
--ButtonType
a également un constructeur qui reçoit ButtonData
, vous pouvez donc le spécifier explicitement., ʻAlt + F4
, le bouton de fermeture de la fenêtre, etc. (pour Windows)showAndWait ()
se comporte un peu compliqué selon le bouton placé.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 **
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 **
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.
ButtonData
est CANCEL_CLOSE`` ButtonType
ButtonData.isCancelButton ()
retourne true
ButtonType
C'est parce que la valeur de retour est déterminée dans l'ordre de priorité de -.ButtonType.NO
, la valeur de retour lorsque la boîte de dialogue est fermée par une méthode autre que le bouton sera ButtonType.NO
.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,
ButtonType
ButtonType
où ButtonData
est CANCEL_CLOSE
ButtonData.isCancelButton ()
retourne true
ButtonType
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();
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();
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.
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 ()
.
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)
Les travailleurs ont un cycle de vie et l'état change comme suit.
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
qui représente l'exécution devient true
RUNNING
Worker
est en coursrunning
qui représente l'exécution devient true
SUCCEEDED
value
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
Task
elle-même est une classe abstraite, alors héritez-en et créez votre propre classe.
--La méthode call ()
est une méthode abstraitecall ()
dans votre propre classe et décrivez le processus qui s'exécute en arrière-plan.Task
implémente l'interface Runnable
, donc vous pouvez la passer à Thread
ou ʻExecutor` pour l'exécuter.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!!
Worker
en appelant la méthode cancel ()
de Worker
.cancel ()
à l'état "fini"
--Lorsque l'annulation est exécutée, ʻisCancelled () de
Task retournera
true`.call ()
.
--Si annulé pendant le blocage par Thread.sleep ()
etc., attrapez ʻInterruptedExceptionet entrez le jugement ʻisCancelled ()
.Task
.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 **
La description
this.progressBar.progressProperty().bind(task.progressProperty());
--Worker
expose une propriété appeléeprogressProperty ()
progressProperty ()
de ProgressBar
, vous pouvez lier progress
de Task
et progress
de ProgressBar
. 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 passe
doubleest également surchargée) --Ce ʻupdateProgress ()
peut être exécuté à partir d'un thread non-UI sans aucun problème.
--Dans lequel runLater ()
est utilisé
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 **
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
value
peut être mise à jour avec la méthode ʻupdateValue ()`.Task
réussit, la valeur de retour de la méthode call ()
est finalement définie sur la propriété value
.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
setOn ****
.
--L'écouteur enregistré est exécuté dans le thread de l'interface utilisateurpackage 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
call ()
, l'exception levée est définie dans la propriété ʻexception`.
――L'exception ne se propage pas en haut de la pile, donc si vous la laissez seule, vous ne remarquerez pas que l'exception a été levée.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;
}
}
}
Task
ait ʻObservableList et expose son état au monde extérieur, vous devez faire attention à utiliser
Platform.runLater ()` pour mettre à jour la liste.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.
Service
est conçu sur l'hypothèse que le traitement des threads sera exécuté plusieurs fois.
--Service
encapsule Task
afin qu'il crée une nouvelle instance de Task
et démarre un thread chaque fois qu'il exécute le traitement du thread.
--Service
est une classe abstraite, et vous pouvez créer et utiliser votre propre classe qui hérite de cette classe.
--Il existe createTask ()
comme méthode abstraite, vous devez donc l'implémenter.Task
.
--Lorsque vous exécutez la méthode start ()
de Service
, le thread est lancé et la Task
générée par la méthodecreateTask ()
est exécutée.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();
}
Service
implémente l'interface Worker
, l'état du Service
change selon les règles définies par Worker
.
―― En d'autres termes, il commence par READY
et devient finalement SUCCEEDED
, FAILED
, CANCELLED
.
--Si vous exécutez start ()
of Service
dans un état autre que READY
, ʻIllegalStateExceptionest levée. --Il existe plusieurs façons de ramener l'état à
READY--Exécute la méthode
reset () à l'état fini (
SUCCEEDED,
FAILED,
CANCELLED) --Pour
SCHEDULED,
RUNNING, annulez avec
cancel ()puis exécutez la méthode
reset (). ――Si vous ne voulez pas revenir sur chaque état, exécutez simplement la méthode
restart (). --Si le statut est
READY`, lancez le processus tel quelREADY
puis réexécuter.restart()Une fois réécrit avec
@FXML
public void start() throws IOException {
System.out.println("restart");
this.service.restart();
}
start ()
, reset ()
et restart ()
doivent toutes être exécutées à partir du thread de l'interface utilisateur.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é ʻexecutorde
Service. --Ensuite,
Service obtiendra le thread pour exécuter
Task à partir de ʻexecutor
.
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
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.
** Résultat d'exécution **
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);
}
}
Bloom
et en la définissant avec setEffect ()
sur le nœud du graphe de scène auquel l'effet est appliqué, l'effet visuel sera appliqué sous ce nœud.** Résultat d'exécution **
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);
}
}
** Résultat d'exécution **
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é
--Spécifiez par la méthode de fréquence (celle dont l'angle est exprimé par
0,0 à
360,0) ――La direction horizontale est «0.0», et on a l'impression qu'elle tourne dans le sens horaire à partir de là. -
―:
0.0 -
\:
45.0 -
|:
90.0 -
/:
135.0 -
―:
180.0--Spécifiez le rayon de flou avec
rayon`** Résultat d'exécution **
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);
}
}
rayon
** Résultat d'exécution **
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
).
Apportez quelques modifications au graphique de la scène pour le rendre plus facile à voir.
** Résultat d'exécution **
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
rayon
** Résultat d'exécution **
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.
** Résultat d'exécution **
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);
}
}
s peuvent être combinés en spécifiant un autre ʻEffect
avecsetInput ()
.setInput ()
est appliqué en premier.référence
――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.
--Dans le fichier multimédia, plusieurs données multimédias sont stockées dans un état combiné.
.mp4
pour MP4, .flv
, .f4v
, etc ... pour FLV).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
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.
référence
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();
}
}
Media
, MediaPlayer
, indépendamment de l'audio / vidéo.Media
représente le média à lire
--MediaPlayer
fournit une API pour manipuler les médias
--Si vous souhaitez afficher la vidéo, utilisez MediaView
(les détails seront décrits plus tard).play ()
de MediaPlayer
Play ()
, pause ()
, stop ()
sont des méthodes de MediaPlayer
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 pausePLAYING Lorsque vous revenez à, il reprendra là où il a été arrêté. |
STOPPED |
État arrêtéPLAYING Jouez 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.
Media
.
--Lorsque l'état de MediaPlayer
auquel l'instance Media
est associée devient READY
, le chargement des méta-informations est définitivement terminé.
--Dans l'état de ʻUNKNOWN, la lecture etc. ne peut pas encore être exécutée, mais des instructions telles que la méthode
play () sont mises en mémoire tampon lorsqu'elles sont appelées dans l'état de ʻUNKNOWN
et sont exécutées lorsque READY
est atteint. (Vous n'êtes pas obligé d'attendre explicitement READY
)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();
}
}
et en faisant
toString ()`.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.
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
cycleDuration
et totalDuration
--cycleDuration
représente le temps de lecture pour une fois
--totalDuration
représente la durée totale de lecture, répétition comprise
--Si le nombre de vues est ʻINDEFINITE,
Duration.INDEFINITE--Cependant, si vous l'obtenez avant qu'il ne devienne «READY», il deviendra «UNKNOWN». --Le temps de lecture est une instance d'une classe appelée
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
Durée
dans une chaîne (telle que 02: 00: 12
), il n'y a rien dans l'API standard qui puisse être formaté directement.
--Je pensais qu'il y avait un bon formateur pour Duration de l'API Date and Time. Peu probable ...
――Cela ne peut pas être aidé, donc si vous essayez d'écrire la version Duration
de JavaFX en référence à l'exemple d'implémentation de StackOverflow dans ↑, cela ressemble à ↓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
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
cycleDuration
renvoie le temps entre startTime
et stopTime
--totalDuration
est cycleDuration * vues
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
qui contient les métadonnées avec
getMetadata () ʻof Media
.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
startTime
, stopTime
et que vous spécifiez seekTime
qui sort de cette plage, vous êtes limité à rester dans startTime
ou stopTime
.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
MediaPlayer
chargé à setMediaPlayer () ʻof
MediaView`fitWidth
et fitHeight
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.
play ()
La différence avec «Media» est la suivante.
--Lecture avec un délai minimum (lecture immédiate)
stop ()
n'ont plus d'effet par la suite.play ()
stop ()
tout en jouant à plusieurs jeux en même temps, tous les sons joués s'arrêteront.Voir Javadoc pour plus de détails.
Créons un simple lecteur de musique tout en utilisant le contenu jusqu'à présent.
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);
}
}
//É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);
}
currentTime
de MediaPlayer
et définissez la valeur au format temporel sur l'étiquette //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
.
//É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
volume
de MediaPlayer
.MediaPlayer.seek ()
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();
}
currentTime
--Calculez le rapport entre l'heure actuelle et le temps total et réglez-le sur le curseur valeur
Slider
pour déterminer si l'utilisateur touche le curseur:
isValueChanging()
--Retourne true
si le curseur est glisséisPressed()
--Retourne true
si le curseur est cliqué 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
//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
status
ne changera pas.setOnEndOfMedia ()
pour changer explicitement status
à la fin de la lectureseek ()
est utilisé pour remettre la position de lecture à startTime
, et stop ()
est utilisé pour changer l'état en STOPPED
.stop ()
est bien, mais pour une raison quelconque getCurrentTime ()
ne renvoie que stopTime
.
--MediaPlayer
a en interne un indicateur pour booléen
, que la lecture soit terminée ou non, mais si cet indicateur est true
,getCurrentTime ()
retournera stopTime
. Commestop ()
après la fin de la lecture, cet indicateur reste comme true
, et je pense que currentTime
ne renvoie que stopTime
même s'il joue après cela.
――Je pense que c'est un bug, mais je ne peux rien dire car je ne connais pas les spécifications.référence
** Résultat d'exécution **
La description
--Utilisez MenuBar
pour créer une barre de menus
Menu
pour ajouter une catégorie (où il est dit" Fichier ") à MenuBar
MenuItem
à Menu
.package sample.javafx;
import javafx.fxml.FXML;
public class MainController {
@FXML
public void open() {
System.out.println(""Ouvrir" est sélectionné");
}
}
** Résultat d'exécution **
SeparatorMenuItem
.** Résultat d'exécution **
Menu
sous Menu
.** Résultat d'exécution **
de
MenuItem`Ctrl
.
Pour Mac, il fait référence à la touche «Meta».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 **
――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
_
) dans Texte
avant l'alphabet auquel vous souhaitez attribuer un mnémonique.** Résultat d'exécution **
(_F)
après le texte du menu, vous pouvez également attribuer des mnémoniques dans le menu japonais (ou plutôt, iTunes ou d'autres applications courantes le font J'y ai fait référence)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 **
graphic
de MenuItem
sur ʻImageView`, vous pouvez afficher n'importe quelle image dans l'élément de menu.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 **
CheckMenuItem
pour définir un élément de menu pour activer et désactiver la vérification.selected
.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 **
RadioMenuItem
pour définir des éléments de menu qui peuvent être sélectionnés exclusivement.
--Définissez l'identifiant (chaîne de caractères arbitraires) du groupe auquel l'élément appartient dans Toggle Group
de RadioMenuItem
.Toggle Group
est défini comme suit sur fxmlToggle 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
ToggleGroup
dans la classe de contrôleur avec l'identificateur deToggle Group
spécifié.Toggle Injection de groupe
@FXML
private ToggleGroup group1;
selectedToggle
de ToggleGroup
, vous pouvez attraper l'événement lorsque l'élément sélectionné change. 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.
à partir de Scene Builder, vous devez donc le définir avec la méthode ʻinitialize ()
etc.** Résultat d'exécution **
ContextMenu
au nœud cible.référence
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();
}
}
true
avec setMaximized ()
Structure des dossiers
`-src/main/
|-java/
| `-sample/javafx/
| |-MainController.java
| `-OtherController.java
|
`-resources/
|-main.fxml
`-other.fxml
other.fxml
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
Class.getResource (String)
comme fichier de ressources.load ()
.show ()
ou showAndWait ()
.
--L'application JavaFX se compose de ** Stage **, ** Scene **, ** Scene Graph **Stage
correspondScene
correspondPane
chargé depuis fxml est le nœud racine de ce graphe de scène.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!!
FXMLLoader.load ()
, vous pouvez obtenir une instance du contrôleur mappée à fxml avec la méthode FXMLLoader.getController ()
.null
est retourné avant load ()
** Comportement lorsqu'aucun propriétaire n'est défini **
--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 **
Stage
dans le contrôleur correspondant, mais obtenez une instance du contrôleur depuis FXMLLoader
et définissez-la via setter etc. C'est plutôt intelligent de faire
--Utilisez la méthode ʻinitOwner () `pour définir le propriétaire
―― Au fait, même si vous définissez le propriétaire, ce n'est pas 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 **
--ʻInitModality () `peut rendre la scène modale
Modality
à ʻinitModality () --
NONE,
WINDOW_MODAL, ʻAPPLICATION_MODAL
sont définis dans Modality
.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
<fx: include>
pour incorporer d'autres fxml** Structure des dossiers **
`-src/main/resources/
|-embedded.fxml
`-main.fxml
** Incorporer fxml (Embedded.fxml) **
** Intégration de la destination fxml (main.fxml) **
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>
/
-La référence dit "Il est considéré par rapport au chemin de classe", ce qui rend difficile à comprendre. .. ..
--S'il ne commence pas par /
, cela semble être un chemin relatif depuis l'emplacement du fichier fxml intégré./
pour le prévisualiser.** Les tentatives d'inclusion dans SceneBuilder échouent **
--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>`.
<fx: include>
à la main, vous pourrez opérer normalement à partir de SceneBuilder.organisation des fichiers
`-src/main/
|-java/
| `-sample/javafx/
| |-Main.java
| |-MainController.java
| `-EmbeddedController.java
`-resources/
|-main.fxml
`-embedded.fxml
embedded.fxml
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
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 **
La description
) associé au FXML (ʻembedded.fxml
) intégré avec <fx: include> ʻest la destination de l'incorporation. Vous pouvez injecter dans le contrôleur (
MainController) de FXML (
main.fxml) avec
@ FXML--Cependant, l'attribut
fx: id doit être défini dans
<fx: include>`.embedded.fxml
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
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 **
La description
main.fxml
...
<BorderPane ... fx:controller="sample.javafx.MainController">
<fx:define>
<fx:include fx:id="embeddedPane" source="embedded.fxml" />
</fx:define>
...
</BorderPane>
) à incorporer en utilisant
<fx: include> dans le FXML (
main.fxml`) de la destination d'intégration.<fx: define>
.
―― De cette façon, les balises intégrées ne seront pas affichées visuellement.
--Définissez l'attribut fx: id
dans <fx: include>
(requis pour DI au contrôleur)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 ()
.
FXMLLoader
à chaque fois.référence
--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
à partir de FXMLLoader
et définissez primaryStage
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 **
Boîte de dialogue affichée
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.
null
, auquel cas la boîte de dialogue de sélection de fichier ne sera pas modale
--La méthode showOpenDialog ()
est bloquée lorsque la boîte de dialogue est affichéeFichier
.
--Si un fichier est sélectionné, un objet File
pointant vers ce fichier est renvoyé.
--null
est renvoyé si la boîte de dialogue est fermée sans sélectionner de fichier FileChooser chooser = new FileChooser();
chooser.setTitle("Juste Sentaku");
File file = chooser.showOpenDialog(this.stage);
** Résultat d'exécution **
setTitile (String)
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.
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 **
getExtensionFilters ()
pour obtenir ʻObservableList` qui définit le filtrage par extension. à cette liste, vous pouvez spécifier le filtrage par extension. --- Passez "description" et "extension definition" au constructeur de ʻExtensionFilter
*. <Extension>
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 **
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.
null
si rien n'est sélectionné (n'est-ce pas une liste vide ...) FileChooser chooser = new FileChooser();
File file = chooser.showSaveDialog(this.stage);
System.out.println("file=" + file);
** Résultat d'exécution **
Sortie de la console
file=C:\Users\Public\hoge
showSaveDialog (Stage)
pour ouvrir une boîte de dialogue pour sélectionner où enregistrer le fichier.File
qui pointe vers le fichier de destination spécifié par l'utilisateur.
--Si la boîte de dialogue est fermée sans rien sélectionner, null
est renvoyé.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 **
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
Apparence
Construction
** Réglage de la taille de chaque élément vertical **
--Utilisez VBox
pour organiser verticalement avec la même largeur
Max Width
) sur MAX_VALUE
.
--Utilisez HBox
si vous voulez vous aligner côte à côte à la même hauteurMax Height
) définie sur MAX_VALUE
.Recommended Posts