I have a JavaFX list using flowless project. But I get a strange border (I have not enabled any border and forced the border to have width=0px) that is present and has a gradient effect:
When I set the cell background-inset to -1, the border is gone, so I believe this issue is related to the background. Although this is not a solution because if I enable the border (which I want to), the gradient effect is still present.
Does anyone knows how I can get rid of this border?
I use JavaFX 8 with latest 201 release.
EDIT: I made the following example show casing the issue:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextFlow;
import javafx.stage.Stage;
import org.fxmisc.flowless.Cell;
import org.fxmisc.flowless.VirtualFlow;
import org.fxmisc.flowless.VirtualizedScrollPane;
public class Main extends Application {
private ObservableList<Line> logLines;
private VirtualFlow<Line, Cell<Line, LineCell>> listView;
private VirtualizedScrollPane<VirtualFlow> listScrollPane;
public static void main(String[] args) {
launch(args);
}
public void start(Stage primaryStage) {
logLines = FXCollections.observableArrayList();
for(int i=1; i < 50; i++) {
logLines.add(new Line("Line "+i));
}
listView = VirtualFlow.createVertical(logLines, (line) -> Cell.wrapNode(new LineCell(line)));
listScrollPane = new VirtualizedScrollPane<>(listView);
Scene scene = new Scene(listScrollPane, 200, 600, Color.BLACK);
primaryStage.setScene(scene);
primaryStage.show();
}
private class Line {
private String text;
public Line(String text) {
this.text = text;
}
public String getText() {
return text;
}
}
private class LineCell extends TextFlow {
public LineCell(Line line) {
super();
Text t = new Text(line.getText());
t.setFill(Color.BLACK);
super.setStyle("-fx-background-color: green;");
super.getChildren().add(t);
}
}
}
Thanks!
I have found a solution to this issue. Instead of LineCell extending TextFlow, it now extends StackPane and I add the TextFlow to the Stackpane.
private class LineCell extends StackPane {
public LineCell(Line line) {
super();
Text t = new Text(line.getText());
t.setFill(Color.BLACK);
TextFlow tf = new TextFlow(t);
super.setStyle("-fx-background-color: green;");
super.getChildren().add(tf);
}
}
Related
I want to ask if it is possible to make a chip in JFXChipView editable once it has been set.
You can create your own JFXChip and implement a behavior to enable editing. First, you need to have an editable label. I looked up online and I found this post: JavaFX custom control - editable label. Then, you can extend JFXChip to use that EditableLabel:
import com.jfoenix.controls.JFXButton;
import com.jfoenix.controls.JFXChip;
import com.jfoenix.controls.JFXChipView;
import com.jfoenix.svg.SVGGlyph;
import javafx.beans.binding.Bindings;
import javafx.beans.property.Property;
import javafx.scene.layout.HBox;
public class EditableChip<T> extends JFXChip<Property<T>> {
protected final HBox root;
public EditableChip(JFXChipView<Property<T>> view, Property<T> item) {
super(view, item);
JFXButton closeButton = new JFXButton(null, new SVGGlyph());
closeButton.getStyleClass().add("close-button");
closeButton.setOnAction(event -> {
view.getChips().remove(item);
event.consume();
});
// Create the label with an initial value from the item
String initialValue = view.getConverter().toString(item);
EditableLabel label = new EditableLabel(initialValue);
label.setMaxWidth(100);
// Bind the item to the text in the label
item.bind(Bindings.createObjectBinding(() -> view.getConverter().fromString(label.getText()).getValue(), label.textProperty()));
root = new HBox(label, closeButton);
getChildren().setAll(root);
}
}
Note: I am using Property<T> instead of using the desired class T because JFXChipView stores the item the first time you add it. And in that case, you're going to get the values as you entered them the first time when calling JFXChipView#getChips().
Sample application:
import com.jfoenix.controls.JFXChipView;
import javafx.application.Application;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.StringConverter;
public class EditableChipViewApp extends Application {
#Override
public void start(Stage primaryStage) {
JFXChipView<Property<String>> chipView = new JFXChipView<>();
chipView.setChipFactory(EditableChip::new);
chipView.setConverter(new StringConverter<Property<String>>() {
#Override
public String toString(Property<String> object) {
return object == null ? null : object.getValue();
}
#Override
public Property<String> fromString(String string) {
return new SimpleStringProperty(string);
}
});
VBox container = new VBox(chipView);
Scene scene = new Scene(container, 800, 600);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Result:
This is how you get the actual values of the chips:
List<String> chipsValues = chipView.getChips().stream().map(Property::getValue).collect(Collectors.toList());
I am trying to add GUIs, created from individual files and add them into my main code.
While it seems to be working, kind of, however, it is missing some elements. For example, in my GridPane, there are a label and a text, both of which are missing. Likewise, for my treeview, there is a treeitem within, however, that is missing as well.
What I am trying to attempt is to reduce the amount of code in the main field and as well as to call relevant events between the Guis, eg. if I select something in the TreeView, that selected TreeItem information will be populated in the GridPane.
Client.java
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;
public class Client extends Application
{
private treeviewGui tvGui;
private gridpaneGui inputFieldsGui;
public void init()
{
tvGui = new treeviewGui();
inputFieldsGui = new gridpaneGui();
}
#Override
public void start(Stage topView)
{
topView.setTitle("Test Application");
HBox mainLayout = new HBox(10);
mainLayout.getChildren().addAll(tvGui, inputFieldsGui);
Scene scene = new Scene(mainLayout);
topView.centerOnScreen();
topView.setScene(scene);
topView.show();
}
public static void main(String[] argv)
{
launch(argv);
}
}
treeviewGui.java
import javafx.scene.control.*;
public class treeviewGui extends TreeView
{
private TreeView treeview;
public treeviewGui()
{
treeview = new TreeView();
preload();
}
private void preload()
{
TreeItem<String> newTI = new TreeItem<>("blah");
treeview.setRoot(newTI);
}
}
gridPane.java
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.geometry.Pos;
import javafx.scene.text.Text;
public class gridpaneGui extends GridPane
{
private GridPane gridPane;
public Text fnameTxt;
public gridpaneGui()
{
gridPane = new GridPane();
gridPane.setAlignment(Pos.CENTER);
gridPane.setHgap(5);
gridPane.setVgap(5);
// First Name
Label fnameLbl = new Label("First Name");
fnameTxt = new Text("-");
gridPane.addRow(0, fnameLbl, fnameTxt);
}
public void setFname(String nameStr)
{
fnameTxt.setText(nameStr);
}
}
i'm working on a project and i'd like to find a way to change the background color of some elements in a listView. i've find a way to add css style class to the listView in general but not to specific elements .
Also , i've heard about cell factory but I dont know if cell factory can adapt during the programme or just set up things at the begging
(i have a listView of an object that I call player , and I want that , when the player in the listView get enough points , his name becomes red)
is there a way to do something like this ?
ListView<Players> listview = ...;
for(Player p : listView){
p.addListener(//change color to red)
}
Thanks
I would use ObservableList and addListener to the given List.
Sample code:
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;
public class App extends Application {
private StackPane main;
private ListView<Player> players;
#Override
public void start(Stage stage) {
main = new StackPane();
var scene = new Scene(main, 640, 480);
players = new ListView<Player>();
ObservableList<Player> playerObjs = FXCollections.observableArrayList (
new Player("A", 50),
new Player("B", 30),
new Player("C", 60),
new Player("D", 5),
new Player("E", 0)
);
players.setItems(playerObjs);
playerObjs.addListener(new ListChangeListener<Player>() {
#Override
public void onChanged(Change<? extends Player> change) {
updateView();
}
});
main.getChildren().add(players);
stage.setScene(scene);
stage.show();
}
public void updateView() {
for(int i = 0; i < players.getItems().size(); i++) {
if(players.getItems().get(i).getHp() < 10) {
players.getItems().get(i).setBackground(...);
}
}
}
public static void main(String[] args) {
launch();
}
}
Now, everytime the list changes, it calls updateView(), which if some condition holds, will set the given item Background to some value.
Let me know if that helped.
I'm using JavaFX to create a Java application which is able to apply a TranslateTransition to a generic node and recall it continuously.
I retrieved a simple right arrow from this url https://www.google.it/search?q=arrow.png&espv=2&source=lnms&tbm=isch&sa=X&ved=0ahUKEwiGheeJvYrTAhWMB5oKHU3-DxgQ_AUIBigB&biw=1600&bih=764#imgrc=rH0TbMkQY2kUaM:
and used it to create the node to translate.
This is my AnimatedNode class:
package application.model.utils.addon;
import javafx.animation.TranslateTransition;
import javafx.scene.Node;
import javafx.util.Duration;
public class AnimatedNode {
private Node node;
private double positionY;
private TranslateTransition translateTransition;
private boolean animated;
private int reverse = 1;
public AnimatedNode(Node node, double animationTime) {
setPositionY(0.0);
setNode(node);
setTranslateTransition(animationTime);
}
public void play() {
if(translateTransition != null && !isAnimated()) {
setAnimated(true);
new Thread() {
#Override
public void run() {
while(isAnimated()) {
translateTransition.setToY(positionY + 50 * reverse);
translateTransition.play();
reverse = -reverse;
setPositionY(translateTransition.getToY());
}
}
}.start();
}
}
public void stop() {
setAnimated(false);
}
public Node getNode() {
return node;
}
private void setNode(Node node) {
this.node = node;
}
public TranslateTransition getTranslateTransition() {
return translateTransition;
}
private void setTranslateTransition(double animationTime) {
translateTransition = new TranslateTransition();
if(node != null) {
translateTransition.setDuration(Duration.seconds(animationTime));
translateTransition.setNode(node);
}
}
public double getPositionY() {
return positionY;
}
private void setPositionY(double positionY) {
this.positionY = positionY;
}
public boolean isAnimated() {
return animated;
}
private void setAnimated(boolean animated) {
this.animated = animated;
}
}
and this is the Application class
package test;
import application.model.utils.addon.AnimatedNode;
import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
public class Test extends Application {
private final String TITLE = "Test application";
private final double WIDTH = 600;
private final double HEIGHT = 400;
private final String ARROW_PATH = "file:resources/png/arrow.png";
private BorderPane rootPane;
#Override
public void start(Stage primaryStage) {
primaryStage.setTitle(TITLE);
rootPane = new BorderPane();
rootPane.setPrefSize(WIDTH, HEIGHT);
Image image = new Image(ARROW_PATH);
ImageView imageView = new ImageView(image);
imageView.setFitWidth(WIDTH);
imageView.setFitHeight(HEIGHT);
imageView.setPreserveRatio(true);
AnimatedNode animatedNode = new AnimatedNode(imageView, 0.7);
Pane pane = new Pane();
pane.getChildren().add(animatedNode.getNode());
pane.setOnMouseClicked(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent arg0) {
if(arg0.getButton().equals(MouseButton.PRIMARY))
animatedNode.play();
if(arg0.getButton().equals(MouseButton.SECONDARY))
animatedNode.stop();
}
});
rootPane.setCenter(pane);
Scene scene = new Scene(rootPane, WIDTH, HEIGHT);
primaryStage.setScene(scene);
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
The node is added to a generic pane; the pane has a MouseListener. I can start the TranslateTransition by using the primary button of the mouse and stop it with the secondary one.
I used a Thread in the play() method of AnimatedNode but I still have a continuous delay in the transition.
Is this the best way to perform the transition? Can I improve my code?
Thanks a lot for your support.
Sample
This is a simplified example which demonstrates a continuous animation started and stopped by left and right mouse clicks.
import javafx.animation.*;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.image.*;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.util.Duration;
public class BouncingCat extends Application {
private static final double WIDTH = 100;
private static final double HEIGHT = 100;
private final String ARROW_PATH =
"http://icons.iconarchive.com/icons/iconka/meow-2/64/cat-rascal-icon.png";
// image source: http://www.iconka.com
#Override
public void start(Stage stage) {
Image image = new Image(ARROW_PATH);
ImageView imageView = new ImageView(image);
TranslateTransition animation = new TranslateTransition(
Duration.seconds(0.7), imageView
);
animation.setCycleCount(Animation.INDEFINITE);
animation.setFromY(0);
animation.setToY(50);
animation.setAutoReverse(true);
Pane pane = new Pane(imageView);
Scene scene = new Scene(pane, WIDTH, HEIGHT);
scene.setOnMouseClicked(e -> {
switch (e.getButton()) {
case PRIMARY:
animation.play();
break;
case SECONDARY:
animation.pause();
break;
}
});
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Advice
You don't need a Thread when you have a Transition. JavaFX will render updated transition frames automatically each pulse.
I don't advise keeping track of properties in a class, when those same values are already represented in the underlying tools you use.
For example:
replace int reverse = 1; with transition.setAutoReverse(true) or transition.setRate(1) (or -1).
replace animated with transition.getStatus().
instead of double positionY, set the toY of the transition.
I wouldn't advise calling your class AnimatedNode unless it extended node, otherwise it is confusing, instead call it something like AnimationControl.
import javafx.animation.TranslateTransition;
import javafx.scene.Node;
import javafx.util.Duration;
public class AnimationControl {
private final TranslateTransition translateTransition;
public AnimationControl(Duration duration, Node node) {
translateTransition = new TranslateTransition(duration, node);
}
public TranslateTransition getTranslateTransition() {
return translateTransition;
}
}
You only need to encapsulate the node and the transition in the AnimationControl and not other fields unless you need further functionality not apparent in your question and not already provided by Node or Transition. If you have that extra functionality then you can enhance the AnimationControl class above to add it.
Exposing the node and the translate transition is enough, as if the user wants to manage the animation, such as starting and stopping it, then the user can just get it from the AnimationControl class. Depending on your use case, the entire AnimationControl class might be unnecessary as you might not need the encapsulation it provides and might instead prefer to just work directly with the node and the transition (as demoed in the sample).
I'm trying to reproduce a Pagination Sample from oracle samples, but when I imported the project something strange happened that I can not build and run the project:
The complete code is:
import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.*;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.VBox;
import javafx.util.Callback;
public class PaginationSample extends Application {
private final Pagination pagination;
private Image[] images = new Image[7];
private void init(Stage primaryStage) {
Group root = new Group();
primaryStage.setScene(new Scene(root));
VBox outerBox = new VBox();
outerBox.setAlignment(Pos.CENTER);
//Images for our pages
for (int i = 0; i < 7; i++) {
images[i] = new Image(PaginationSample.class.getResource("animal" + (i + 1) + ".jpg").toExternalForm(), false);
}
pagination = PaginationBuilder.create().pageCount(7).pageFactory(new Callback<Integer, Node>() {
#Override public Node call(Integer pageIndex) {
return createAnimalPage(pageIndex);
}
}).build();
//Style can be numeric page indicators or bullet indicators
Button styleButton = ButtonBuilder.create().text("Toggle pagination style").onAction(new EventHandler<ActionEvent>() {
public void handle(ActionEvent me) {
if (!pagination.getStyleClass().contains(Pagination.STYLE_CLASS_BULLET)) {
pagination.getStyleClass().add(Pagination.STYLE_CLASS_BULLET);
} else {
pagination.getStyleClass().remove(Pagination.STYLE_CLASS_BULLET);
}
}
}).build();
outerBox.getChildren().addAll(pagination, styleButton);
root.getChildren().add(outerBox);
}
//Creates the page content
private VBox createAnimalPage(int pageIndex) {
VBox box = new VBox();
ImageView iv = new ImageView(images[pageIndex]);
box.setAlignment(Pos.CENTER);
Label desc = new Label("PAGE " + (pageIndex + 1));
box.getChildren().addAll(iv, desc);
return box;
}
#Override public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) { launch(args); }
}
But netbeans show me the error "cannot assign a value to final variable pagination" for the following line:
pagination = PaginationBuilder.create().pageCount(7).pageFactory(new Callback<Integer, Node>() {
Someone can explain me what is going wrong??
A final field can be initialized only once. So the best place to initialize it is when its declared
private final Pagination pagination = new Pagination(...);
or it can be done in the constructor, since the constructor is assured to be called once per instance
private final Pagination pagination;
public PaginationSample() {
pagination = new Pagination(...);
}
final field cannot be initialized in a method because a method can be called multiple times once an instance of that class gets created