I have a StackPane with the size of (15px width, 400px height). I want to but a "Vertical ProgressBar" to that StackPane. What I was doing is to rotate the progressbar by 90 degree. However, the progressBar cannot fit in the stackpane with that rotation. It just shows as a small squared progressbar at the center of StackPane.
How can I fixed that?
Sample vertical progress bar.
class UpwardProgress {
private ProgressBar progressBar = new ProgressBar();
private Group progressHolder = new Group(progressBar);
public UpwardProgress(double width, double height) {
progressBar.setMinSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
progressBar.setPrefSize(height, width);
progressBar.setMaxSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
progressBar.getTransforms().setAll(
new Translate(0, height),
new Rotate(-90, 0, 0)
);
}
public ProgressBar getProgressBar() {
return progressBar;
}
public Group getProgressHolder() {
return progressHolder;
}
}
Used in a sample app.
import javafx.animation.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.scene.*;
import javafx.scene.canvas.*;
import javafx.scene.control.*;
import javafx.scene.image.PixelWriter;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.transform.*;
import javafx.stage.Stage;
import javafx.util.Duration;
import java.util.Random;
class UpwardProgress {
private ProgressBar progressBar = new ProgressBar();
private Group progressHolder = new Group(progressBar);
public UpwardProgress(double width, double height) {
progressBar.setMinSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
progressBar.setPrefSize(height, width);
progressBar.setMaxSize(StackPane.USE_PREF_SIZE, StackPane.USE_PREF_SIZE);
progressBar.getTransforms().setAll(
new Translate(0, height),
new Rotate(-90, 0, 0)
);
}
public ProgressBar getProgressBar() {
return progressBar;
}
public Group getProgressHolder() {
return progressHolder;
}
}
public class StarCounter extends Application {
public static final Color INDIA_INK = Color.rgb(35, 39, 50);
private static final int CANVAS_SIZE = 400;
private static final int N_STARS = 1_000;
private final Canvas canvas = new Canvas(CANVAS_SIZE, CANVAS_SIZE);
private final Random random = new Random(42);
private final IntegerProperty visibleStars = new SimpleIntegerProperty(0);
private Timeline timeline;
#Override
public void start(final Stage stage) {
Group root = initProgress();
clearCanvas();
visibleStars.addListener((observable, oldValue, newValue) -> {
if (newValue.intValue() > oldValue.intValue()) {
addStars(newValue.intValue() - oldValue.intValue());
}
});
stage.setScene(
new Scene(
new HBox(canvas, root),
INDIA_INK
)
);
stage.show();
runSimulation();
stage.getScene().setOnMouseClicked(event -> {
resetSimulation();
runSimulation();
});
}
private Group initProgress() {
UpwardProgress upwardProgress = new UpwardProgress(15, 400);
ProgressIndicator bar = upwardProgress.getProgressBar();
bar.setStyle("-fx-base: skyblue; -fx-accent: gold;");
bar.progressProperty().bind(visibleStars.divide(N_STARS * 1.0));
return upwardProgress.getProgressHolder();
}
private void resetSimulation() {
clearCanvas();
if (timeline != null) {
timeline.stop();
timeline = null;
}
}
private void runSimulation() {
timeline = new Timeline(
new KeyFrame(
Duration.seconds(0),
new KeyValue(visibleStars, 0)
),
new KeyFrame(
Duration.seconds(10),
new KeyValue(visibleStars, N_STARS)
)
);
timeline.play();
}
private void clearCanvas() {
canvas.getGraphicsContext2D().setFill(INDIA_INK);
canvas.getGraphicsContext2D().fillRect(0, 0, CANVAS_SIZE, CANVAS_SIZE);
}
private void addStars(int nStarsToAdd) {
GraphicsContext context = canvas.getGraphicsContext2D();
PixelWriter writer = context.getPixelWriter();
for (int i = 0; i < nStarsToAdd; i++) {
writer.setColor(random.nextInt(CANVAS_SIZE), random.nextInt(CANVAS_SIZE), Color.GOLD);
}
}
public static void main(String[] args) {
launch(args);
}
}
There's another alternative to this, which is, to make your own custom vertical progress-bar. Sounds too much but isn't. The advantage of this approach is that this is more consistent in UI and more dynamically approachable. I used the above answer by #jewel but struggled with UI consistency and dynamic behavior of the progress-bar.
The approach being use a vbox for the progress-bar and inside it another vbox for bar.
<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.scene.layout.VBox?>
<VBox xmlns="http://javafx.com/javafx/8.0.65" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.device.ui.VerticalProgressBarController">
<children>
<VBox fx:id="progress_bar" alignment="BOTTOM_CENTER" prefHeight="450.0" prefWidth="20.0" style="-fx-border-color: black; -fx-border-radius: 2 2 2 2;">
<children>
<VBox fx:id="bar" prefHeight="0.0" prefWidth="20.0" style="-fx-border-radius: 2 2 2 2;"/>
</children>
</VBox>
</children>
</VBox>
One can adjust the prefHeight of progress-bar and bar dynamically in controller or statically in .fxml file. Since here, only bar was the one I needed to adjust dynamically, so have set its prefHeight as 0 and adjust it appropriately in corresponding controller.
public class VerticalProgressBarController implements Initializable {
#FXML
VBox progress_bar;
#FXML
VBox bar;
private double progress_bar,fixed_capacity;
public void initialize(URL location, ResourceBundle resources) {
progressBarHeight = progress_bar.getPrefHeight();
bar.setMaxHeight(progressBarHeight);
// initial bar color
setGreenBar();
// set the max capacity of the progress bar
fixed_capacity = 100;
// pass in the proportion; here wanted to show 15 on a scale of 100
updateProgressBar(15 / fixed_capacity);
}
public void setGreenBar(){
bar.setStyle("-fx-background-color: green");
}
public void setYellowBar(){
bar.setStyle("-fx-background-color: yellow");
}
public void setRedBar(){
bar.setStyle("-fx-background-color: red");
}
public void updateProgressBar(double progress){
bar.setPrefHeight(progressBarHeight * progress);
if(progress <= .60){
setGreenBar();
} else if(progress > .60 &&
progress <= .75){
setYellowBar();
}else {
setRedBar();
}
}
Related
I have a simple JavaFX app that has two little circles that are supposed to change their location every 0.5 s. Later on this is supposed to become a planets simulation. At the moment the location of my space objects changes in a separate thread which is launched when the button "Start simulation" is pressed. Simultaneously I want my circles (representing the planets) to be drawn again and again always using the current location stored in the spaceObject objects. When I limit the re-drawing to three times (instead of an unlimited amount via a while ( true ) { which is what I actually want) I see that the GUI is not updating while the loop is running. But after the loop is finished the circles move to the new location while the calculations thread in the background is still running. Why is my GUI thread blocked for the time of the while ( i < 3 ) { and how can I simultaneously update my GUI with the current location of the circles? Here is my code:
Main.java
package plantenbahnen;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;
public class Main extends Application {
#Override
public void start(Stage stage) throws Exception {
Parent root = FXMLLoader.load(getClass().getResource("FXMLDocument.fxml"));
Scene scene = new Scene(root);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Controller.java
package plantenbahnen;
import java.net.URL;
import java.util.ArrayList;
import java.util.ResourceBundle;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.layout.Pane;
public class Controller implements Initializable {
#FXML private Pane paneDraw;
#FXML private Pane paneControls;
private ArrayList<SpaceObject> universe = new ArrayList<>();
private Thread calcThread;
#Override
public void initialize(URL url, ResourceBundle rb) {
SpaceObject sun = new SpaceObject("sun", 600, 600);
universe.add(sun);
SpaceObject earth = new SpaceObject("earth", 450, 450);
universe.add(earth);
MyCalculations myCalc = new MyCalculations(universe);
calcThread = new Thread(myCalc);
Draw.drawPlanets(universe, paneDraw);
}
#FXML private void buttonStartSimulation(ActionEvent event) throws InterruptedException {
calcThread.start();
Platform.runLater(new Runnable() {
#Override public void run() {
int i = 0;
//while ( true ) { // this line is what I want
while ( i < 3 ) {
Draw.drawPlanets(universe, paneDraw);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e);
}
i++;
}
}
});
}
}
MyCalculations.java
package plantenbahnen;
import java.util.ArrayList;
public class MyCalculations implements Runnable {
ArrayList<SpaceObject> universe;
public MyCalculations (ArrayList<SpaceObject> universe) {
this.universe = universe;
}
#Override
public void run(){
double toAdd = 100.0;
while ( true ) {
for (SpaceObject so: universe) {
so.setx(so.getx() + toAdd);
so.sety(so.gety() + toAdd);
}
if ( toAdd > 0.0 ) {
toAdd = -300.0;
} else {
toAdd = 300.0;
}
}
}
}
Draw.java
package plantenbahnen;
import java.util.ArrayList;
import javafx.scene.Node;
import javafx.scene.layout.Pane;
public class Draw {
public static void drawPlanets(ArrayList<SpaceObject> universe, Pane pane) {
for (Node child: pane.getChildren()) {
System.out.println(child);
}
// Clear objects first
for (SpaceObject so: universe) {
if ( pane.getChildren().contains(so) ) {
pane.getChildren().remove(so);
System.out.println("Removing ... " + so.getName());
}
}
double paneHalfWidth = pane.getPrefWidth() / 2.0;
double paneHalfHeight = pane.getPrefHeight() / 2.0;
double scaleFactor = 0.1;
for (SpaceObject so: universe) {
so.setCenterX(so.getx() * scaleFactor + paneHalfWidth);
so.setCenterY(so.gety() * scaleFactor + paneHalfHeight);
System.out.println("x=" + so.getCenterX() + " y=" + so.getCenterY());
so.setRadius(2);
//so.setColour(Color.BLACK);
pane.getChildren().add(so);
}
}
}
SpaceObject.java
package plantenbahnen;
import javafx.scene.shape.Circle;
public class SpaceObject extends Circle {
private double x,y;
private String name;
SpaceObject(String name, double x, double y) {
this.x = x;
this.y = y;
this.name = name;
}
public double getx(){
return this.x;
}
public void setx(double value){
this.x=value;
}
public double gety(){
return this.y;
}
public void sety(double value){
this.y=value;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
FXMLDocument.fxml
<?xml version="1.0" encoding="UTF-8"?>
<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<AnchorPane fx:id="AnchorPane" prefHeight="700.0" prefWidth="800.0" xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="plantenbahnen.Controller">
<children>
<Pane fx:id="paneDraw" prefHeight="700.0" prefWidth="800.0">
<children>
<Pane fx:id="paneControls" prefHeight="66.0" prefWidth="174.0">
<children>
<Button fx:id="buttonStartSimulation" layoutX="26.0" layoutY="21.0" mnemonicParsing="false" onAction="#buttonStartSimulation" text="Start simulation" />
</children>
</Pane>
</children></Pane>
</children>
</AnchorPane>
Thanks a lot in advance for your help.
Try something like this:
#FXML private void buttonStartSimulation(ActionEvent event) throws InterruptedException {
calcThread.start();
Thread updaterThread = new Thread( () -> {
#Override public void run () {
int i = 0;
while ( true ) { // this line is what I want
Platform.runLater( () -> Draw.drawPlanets(universe, paneDraw) );
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
System.out.println(e);
}
i++;
}
}
}
updaterThread.setDaemon ( true );
updaterThread.start();
}
You want to make sure all of your calls to Platform.runLater() are short, have no sleeps involved, return quickly, and do minimal calculations -- all of these calls have to be done "in-between" other updates to the UI, like resizing windows, managing button presses, etc.
By the way -- I'm not sure if you need a "calcThread" and an "updaterThread". I suspect they should be one thread. But this is a good proof of concept.
Thanks everybody for your help. I ended up using Timeline. All I had to change is the Controller and it looks like this (only the relevant code is shown):
public class Controller implements Initializable {
// ...
private Thread calcThread;
Timeline timeline = new Timeline(new KeyFrame(Duration.seconds(0.5), new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event) {
Draw.drawPlanets(universe, paneDraw);
}
}));
#Override
public void initialize(URL url, ResourceBundle rb) {
// ...
MyCalculations myCalc = new MyCalculations(universe);
calcThread = new Thread(myCalc);
// ...
}
#FXML private void buttonStartSimulation(ActionEvent event) throws InterruptedException {
calcThread.start();
timeline.setCycleCount(Timeline.INDEFINITE);
timeline.play();
}
}
I've been trying to create a program that displays a ball in the center of a window, with 4 buttons titled Up, Down, Left and Right at the bottom. When you press the buttons the ball moves in the corresponding direction. I've got that part figured out, but I also need to figure out how to make it so if making the ball go in a certain direction obscures it from view, it stops it from going in that direction. I need to find a way to detect the boundaries of the window, and stopping the ball from being able to go outside of the boundary. Here's what I have so far:
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Circle;
import javafx.stage.Stage;
public class Ball extends Application {
private BallControl circle = new BallControl();
#Override
public void start(Stage primaryStage) {
HBox pane = new HBox();
pane.setSpacing(10);
pane.setAlignment(Pos.CENTER);
Button btUp = new Button("UP");
Button btDown = new Button("DOWN");
Button btLeft = new Button("LEFT");
Button btRight = new Button("RIGHT");
pane.getChildren().add(btLeft);
pane.getChildren().add(btRight);
pane.getChildren().add(btUp);
pane.getChildren().add(btDown);
BorderPane borderPane = new BorderPane();
borderPane.setCenter(circle);
borderPane.setBottom(pane);
BorderPane.setAlignment(pane, Pos.CENTER);
btUp.setOnAction(new UpHandler());
btDown.setOnAction(new DownHandler());
btLeft.setOnAction(new LeftHandler());
btRight.setOnAction(new RightHandler());
Scene scene = new Scene(borderPane, 250, 250);
primaryStage.setTitle("Ball"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show();
}
class UpHandler implements EventHandler<ActionEvent> {
public void handle(ActionEvent e) {
circle.up();
System.out.println("Up Button Pressed");
}
}
class DownHandler implements EventHandler<ActionEvent> {
public void handle(ActionEvent e) {
circle.down();
System.out.println("Down Button Pressed");
}
}
class LeftHandler implements EventHandler<ActionEvent> {
public void handle(ActionEvent e) {
circle.left();
System.out.println("Left Button Pressed");
}
}
class RightHandler implements EventHandler<ActionEvent> {
public void handle(ActionEvent e) {
circle.right();
System.out.println("Right Button Pressed");
}
}
public static void main(String[] args) {
launch(args);
}
}
class BallControl extends Pane {
public final double radius = 20;
private double x = radius, y = radius;
private double dx = 1, dy = 1;
private Circle circle = new Circle();
public BallControl() {
getChildren().add(circle);
circle.setCenterX(125.0f);
circle.setCenterY(115.0f);
circle.setRadius(25.0f);
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
}
protected void moveBall() {
// Check boundaries
if (x < radius || x > getWidth() - radius) {
dx = 0; // Change ball move direction
}
if (y < radius || y > getHeight() - radius) {
dy = 0; // Change ball move direction
}
// Adjust ball position
x += dx;
y += dy;
circle.setCenterX(x);
circle.setCenterY(y);
}
public void up() {
circle.setCenterY(circle.getCenterY() - 10);
}
public void down() {
circle.setCenterY(circle.getCenterY() + 10);
}
public void left() {
circle.setCenterX(circle.getCenterX() - 10);
}
public void right() {
circle.setCenterX(circle.getCenterX() + 10);
}
}
Here is the part that I was hoping would make the program check for boundaries, but it doesn't seem to work:
protected void moveBall() {
// Check boundaries
if (x < radius || x > getWidth() - radius) {
dx = 0; // Change ball move direction
}
if (y < radius || y > getHeight() - radius) {
dy = 0; // Change ball move direction
}
// Adjust ball position
x += dx;
y += dy;
circle.setCenterX(x);
circle.setCenterY(y);
Someone else already asked a very similar question How to make the ball bounce off the walls in JavaFX?
Nevertheless, all you were missing was a check method before making the next move. I added moveAcceptable() and deleted BallControl for simplicity.
public class Ball extends Application
{
Circle circle = new Circle();
Pane bc = new Pane();
public BorderPane borderPane;
#Override
public void start(Stage primaryStage)
{
bc.getChildren().add(circle);
circle.setCenterX(125.0f);
circle.setCenterY(115.0f);
circle.setRadius(25.0f);
circle.setStroke(Color.BLACK);
circle.setFill(Color.WHITE);
HBox pane = new HBox();
pane.setSpacing(10);
pane.setAlignment(Pos.CENTER);
Button btUp = new Button("UP");
Button btDown = new Button("DOWN");
Button btLeft = new Button("LEFT");
Button btRight = new Button("RIGHT");
pane.getChildren().add(btLeft);
pane.getChildren().add(btRight);
pane.getChildren().add(btUp);
pane.getChildren().add(btDown);
borderPane = new BorderPane();
borderPane.setCenter(bc);
borderPane.setBottom(pane);
BorderPane.setAlignment(pane, Pos.CENTER);
btUp.setOnAction(new UpHandler());
btDown.setOnAction(new DownHandler());
btLeft.setOnAction(new LeftHandler());
btRight.setOnAction(new RightHandler());
Scene scene = new Scene(borderPane, 250, 250);
primaryStage.setTitle("Ball"); // Set the stage title
primaryStage.setScene(scene); // Place the scene in the stage
primaryStage.show();
}
class UpHandler implements EventHandler<ActionEvent>
{
public void handle(ActionEvent e)
{
if (moveAcceptable("up"))
circle.setCenterY(circle.getCenterY() - 10);
System.out.println("Up Button Pressed");
}
}
class DownHandler implements EventHandler<ActionEvent>
{
public void handle(ActionEvent e)
{
if (moveAcceptable("Down"))
circle.setCenterY(circle.getCenterY() + 10);
System.out.println("Down Button Pressed");
}
}
class LeftHandler implements EventHandler<ActionEvent>
{
public void handle(ActionEvent e)
{
if (moveAcceptable("Left"))
circle.setCenterX(circle.getCenterX() - 10);
System.out.println("Left Button Pressed");
}
}
class RightHandler implements EventHandler<ActionEvent>
{
public void handle(ActionEvent e)
{
if (moveAcceptable("Right"))
circle.setCenterX(circle.getCenterX() + 10);
System.out.println("Right Button Pressed");
}
}
public boolean moveAcceptable(String direction)
{
final Bounds bounds = borderPane.getLayoutBounds();
if (direction.equalsIgnoreCase("up"))
{
return (circle.getCenterY() > (bounds.getMinY() + circle.getRadius()));
} else if (direction.equalsIgnoreCase("down"))
{
return circle.getCenterY() < (bounds.getMaxY() - circle.getRadius());
} else if (direction.equalsIgnoreCase("right"))
{
return circle.getCenterX() < (bounds.getMaxX() - circle.getRadius());
} else //left
{
return circle.getCenterX() > (bounds.getMinX() + circle.getRadius());
}
}
public static void main(String[] args)
{
launch(args);
}
}
I have some signal processing data which gets fed at roughly at 50Hz. I need to update a rectangle's opacity based on the signal value in real time. I am trying to develop the UI in JavaFX 8.
For time being I am simulating the signal value using random number generator in JavaFX service in my code.
I am using Platform.runLater to update the UI, however this doesn't update values in real time, I read through similar problems encountered by others and the normal suggestion is that not to call Platform.runLater often but to batch the updates.
In my case if I batch my updates, the frequency at which the opacity changes will not be equal to the signal frequency.
Any thoughts on how to achieve this?
public class FlickerController
{
#FXML
private Rectangle leftBox;
#FXML
private Rectangle rightBox;
#FXML
private ColorPicker leftPrimary;
#FXML
private ColorPicker leftSecondary;
#FXML
private ColorPicker rightPrimary;
#FXML
private ColorPicker rightSecondary;
#FXML
private Slider leftFrequency;
#FXML
private Slider rightFrequency;
#FXML
private Button startButton;
#FXML
private Label leftfreqlabel;
#FXML
private Label rightfreqlabel;
#FXML
private Label rightBrightness;
#FXML
private Label leftBrightness;
private boolean running = false;
DoubleProperty leftopacity = new SimpleDoubleProperty(1);
DoubleProperty rightopacity = new SimpleDoubleProperty(1);
private FlickerThread ftLeft;
private FlickerThread ftRight;
public void initialize()
{
leftopacity.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
{
Platform.runLater(new Runnable()
{
#Override
public void run()
{
double brightness = leftopacity.doubleValue();
leftBrightness.setText(""+brightness);
leftBox.opacityProperty().set(brightness);
}
});
}
});
rightopacity.addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
{
Platform.runLater(new Runnable()
{
#Override
public void run()
{
double brightness = rightopacity.doubleValue();
rightBrightness.setText(""+brightness);
rightBox.opacityProperty().set(brightness);
}
});
}
});
startButton.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent event)
{
if(running)
{
synchronized(this)
{
running=false;
}
startButton.setText("Start");
}
else
{
running=true;
ftLeft = new FlickerThread((int)leftFrequency.getValue(),leftopacity);
ftRight = new FlickerThread((int)rightFrequency.getValue(), rightopacity);
try
{
ftLeft.start();
ftRight.start();
}
catch(Throwable t)
{
t.printStackTrace();
}
startButton.setText("Stop");
}
}
});
leftFrequency.valueProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
{
leftfreqlabel.setText(newValue.intValue()+"");
}
});
rightFrequency.valueProperty().addListener(new ChangeListener<Number>() {
#Override
public void changed(ObservableValue<? extends Number> observable,
Number oldValue, Number newValue)
{
rightfreqlabel.setText(newValue.intValue()+"");
}
});
}
class FlickerThread extends Service<Void>
{
private long sleeptime;
DoubleProperty localval = new SimpleDoubleProperty(1) ;
public FlickerThread(int freq, DoubleProperty valtoBind)
{
this.sleeptime = (1/freq)*1000;
valtoBind.bind(localval);
}
#Override
protected Task <Void>createTask()
{
return new Task<Void>() {
#Override
protected Void call() throws Exception
{
while(running)
{
double val = Math.random();
System.out.println(val);
localval.setValue(val);
Thread.sleep(sleeptime);
}
return null;
}
};
}
}
}
class FlickerThread extends Thread
{
private long sleeptime;
final AtomicReference<Double> counter = new AtomicReference<>(new Double(-1.0));
private Label label;
private Rectangle myrect;
public FlickerThread(int freq, Label label,Rectangle rect)
{
this.sleeptime = (long) ((1.0/freq)*1000.0);
System.out.println("Sleep time is "+sleeptime);
this.label = label;
this.myrect = rect;
}
#Override
public void run() {
double count = 1.0 ;
while (running) {
count = Math.random();
if (counter.getAndSet(count) == -1) {
updateUI(counter, label,myrect);
try
{
Thread.sleep(sleeptime);
} catch (InterruptedException e)
{
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
}
private void updateUI(final AtomicReference<Double> counter,
final Label label, final Rectangle myrect) {
Platform.runLater(new Runnable() {
#Override
public void run() {
double val = counter.getAndSet(-1.0);
final String msg = String.format("Brt: %,f", val);
label.setText(msg);
myrect.opacityProperty().set(val);
}
});
}
You have a calculation error in your code.
Consider:
1/100*1000=0
But:
1.0/100*1000=10.0
i.e. you need to use floating point arithmetic, not integer arithmetic.
There are numerous other issues with your code as pointed out in my previous comment, so this answer is more of a code review and suggested approach than anything else.
You can batch updates to runLater as in James's answer to Throttling javafx gui updates. But for an update rate of 100 hertz max, it isn't going to make a lot of difference performance-wise as JavaFX generally operates on a 60 hertz pulse cycle, unless you really overload it (which you aren't really doing in your example). So the savings you get by throttling updates will be pretty minimal.
Here is a sample you can try out (it uses James's input throttling technique):
import javafx.application.*;
import javafx.beans.property.DoubleProperty;
import javafx.concurrent.Task;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.layout.*;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;
import java.util.Random;
import java.util.concurrent.atomic.AtomicLong;
public class InputApp extends Application {
private final ToggleButton controlButton = new ToggleButton("Start");
private final Rectangle box = new Rectangle(100, 100, Color.BLUE);
private final Label brightness = new Label();
private final Label frequencyLabel = new Label();
private final Slider frequency = new Slider(1, 100, 10);
private InputTask task;
#Override
public void start(Stage stage) throws Exception {
// initialize bindings.
brightness.textProperty().bind(
box.opacityProperty().asString("%.2f")
);
frequencyLabel.textProperty().bind(
frequency.valueProperty().asString("%.0f")
);
frequency.valueChangingProperty().addListener((observable, oldValue, newValue) -> {
if (controlButton.isSelected()) {
controlButton.fire();
}
});
// start and stop the input task.
controlButton.selectedProperty().addListener((observable, wasSelected, isSelected) -> {
if (isSelected) {
task = new InputTask(
(int) frequency.getValue(),
box.opacityProperty()
);
Thread inputThread = new Thread(task, "input-task");
inputThread.setDaemon(true);
inputThread.start();
controlButton.setText("Stop");
} else {
if (task != null) {
task.cancel();
}
controlButton.setText("Start");
}
});
// create the layout
VBox layout = new VBox(
10,
frequency,
new HBox(5, new Label("Frequency: " ), frequencyLabel, new Label("Hz"),
controlButton,
box,
new HBox(5, new Label("Brightness: " ), brightness)
);
layout.setPadding(new Insets(10));
// display the scene
stage.setScene(new Scene(layout));
stage.show();
}
// simulates accepting random input from an input feed at a given frequency.
class InputTask extends Task<Void> {
private final DoubleProperty changeableProperty;
private final long sleeptime;
final AtomicLong counter = new AtomicLong(-1);
final Random random = new Random(42);
public InputTask(int inputFrequency, DoubleProperty changeableProperty) {
this.changeableProperty = changeableProperty;
this.sleeptime = (long) ((1.0 / inputFrequency) * 1_000);
}
#Override
protected Void call() throws InterruptedException {
long count = 0 ;
while (!Thread.interrupted()) {
count++;
double newValue = random.nextDouble(); // input simulation
if (counter.getAndSet(count) == -1) {
Platform.runLater(() -> {
changeableProperty.setValue(newValue);
counter.getAndSet(-1);
});
}
Thread.sleep(sleeptime);
}
return null;
}
}
public static void main(String[] args) {
System.out.println(1.0/100*1000);
}
}
I'm still new with programming android. I have a problem with my game project. The problem is I have a problem with moveYModifier for sprite to jump when I touch the screen. Other problem is I got an error when I try to make a touch event method. Can somebody help me, so I can understand how to write correctly the method. I do not use engine extension because I think it's enough by using moveYModifier. I already search the answer but I still confuse about it. Thanks.
here my code :
import org.anddev.andengine.engine.Engine;
import org.anddev.andengine.engine.camera.Camera;
import org.anddev.andengine.engine.options.EngineOptions;
import org.anddev.andengine.engine.options.EngineOptions.ScreenOrientation;
import org.anddev.andengine.engine.options.resolutionpolicy.RatioResolutionPolicy;
import org.anddev.andengine.entity.modifier.MoveYModifier;
import org.anddev.andengine.entity.modifier.SequenceEntityModifier;
import org.anddev.andengine.entity.scene.Scene;
import org.anddev.andengine.entity.scene.Scene.IOnSceneTouchListener;
import org.anddev.andengine.entity.scene.background.AutoParallaxBackground;
import org.anddev.andengine.entity.scene.background.ParallaxBackground.ParallaxEntity;
import org.anddev.andengine.entity.sprite.AnimatedSprite;
import org.anddev.andengine.entity.sprite.Sprite;
import org.anddev.andengine.entity.util.FPSLogger;
import org.anddev.andengine.input.touch.TouchEvent;
import org.anddev.andengine.opengl.texture.TextureOptions;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlas;
import org.anddev.andengine.opengl.texture.atlas.bitmap.BitmapTextureAtlasTextureRegionFactory;
import org.anddev.andengine.opengl.texture.region.TextureRegion;
import org.anddev.andengine.opengl.texture.region.TiledTextureRegion;
import org.anddev.andengine.ui.activity.BaseGameActivity;
public class KetigaMainActivity extends BaseGameActivity implements IOnSceneTouchListener{
private int CAMERA_WIDTH = 800;
private int CAMERA_HEIGHT = 480;
private BitmapTextureAtlas bitmapTextureAtlas;
private TiledTextureRegion playerTextureRegion;
private BitmapTextureAtlas autoParallaxBackgroundTexture;
private TextureRegion parallaxLayerBack;
private TextureRegion parallaxLayerMid;
private TextureRegion parallaxLayerFront;
private TextureRegion parallaxLayerBackMid;
private int jumpHeight = 100;
private int jumpDuration = 2;
private int playerX = CAMERA_WIDTH/2;
private int playerY = CAMERA_HEIGHT - playerTextureRegion.getTileHeight() - (parallaxLayerFront.getHeight()/3);
#Override
public Engine onLoadEngine() {
final Camera camera = new Camera(0, 0, CAMERA_WIDTH, CAMERA_HEIGHT);
return new Engine(new EngineOptions(true, ScreenOrientation.LANDSCAPE, new RatioResolutionPolicy(CAMERA_WIDTH, CAMERA_HEIGHT), camera));
}
#Override
public void onLoadResources(){
BitmapTextureAtlasTextureRegionFactory.setAssetBasePath("gfx/");
this.bitmapTextureAtlas = new BitmapTextureAtlas(512, 256, TextureOptions.BILINEAR_PREMULTIPLYALPHA);
this.playerTextureRegion = BitmapTextureAtlasTextureRegionFactory.createTiledFromAsset(this.bitmapTextureAtlas, this, "ulat10.png",0,0,4,2);
this.autoParallaxBackgroundTexture = new BitmapTextureAtlas(1024, 1024, TextureOptions.DEFAULT);
this.parallaxLayerFront = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.autoParallaxBackgroundTexture, this, "tanah6.png",0,810);
this.parallaxLayerBack = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.autoParallaxBackgroundTexture, this, "background1.png",0,0);
this.parallaxLayerBackMid = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.autoParallaxBackgroundTexture, this, "gunung3.png",0,490);
this.parallaxLayerMid = BitmapTextureAtlasTextureRegionFactory.createFromAsset(this.autoParallaxBackgroundTexture, this, "awan5.png",0,700);
this.mEngine.getTextureManager().loadTextures(this.bitmapTextureAtlas, this.autoParallaxBackgroundTexture);
}
#Override
public Scene onLoadScene(){
new Scene();
this.mEngine.registerUpdateHandler(new FPSLogger());
final Scene scene = new Scene();
final AutoParallaxBackground autoParallaxBackground = new AutoParallaxBackground(0, 0, 0, 5);
autoParallaxBackground.attachParallaxEntity(new ParallaxEntity(0.0f, new Sprite(0, CAMERA_HEIGHT - this.parallaxLayerBack.getHeight(), this.parallaxLayerBack)));
autoParallaxBackground.attachParallaxEntity(new ParallaxEntity(-1/4.0f, new Sprite(0, CAMERA_HEIGHT - this.parallaxLayerBackMid.getHeight() - (parallaxLayerFront.getHeight()/6), this.parallaxLayerBackMid)));
autoParallaxBackground.attachParallaxEntity(new ParallaxEntity(-1/2.0f, new Sprite(0, 0,this.parallaxLayerMid)));
autoParallaxBackground.attachParallaxEntity(new ParallaxEntity(-3.0f, new Sprite(0, CAMERA_HEIGHT - this.parallaxLayerFront.getHeight(), this.parallaxLayerFront)));
scene.setBackground(autoParallaxBackground);
AnimatedSprite player = new AnimatedSprite(playerX, playerY, this.playerTextureRegion);
player.setScaleCenterY(this.playerTextureRegion.getTileHeight());
player.animate(new long[]{100, 100, 100},0 ,2, true);
scene.setOnSceneTouchListener(this);
scene.attachChild(player);
return scene;
}
#Override
public void onLoadComplete(){
}
#Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent) {
if (pSceneTouchEvent.isActionDown()) {
jump(); // this where I got an error
}
return false;
}
public boolean jump(AnimatedSprite player){
final MoveYModifier moveUpModifier = new MoveYModifier(jumpDuration /2, playerY, playerY + jumpHeight);
final MoveYModifier moveDownModivier = new MoveYModifier(jumpDuration /2, playerY + jumpHeight, playerY );
final SequenceEntityModifier modifier = new SequenceEntityModifier(moveUpModifier, moveDownModivier);
player.registerEntityModifier(new SequenceEntityModifier (modifier));
return true;
}
}
#Override
public boolean onSceneTouchEvent(Scene pScene, TouchEvent pSceneTouchEvent)
{
if (pSceneTouchEvent.isActionDown())
{
jump(player);
}
return false;
}
You can use the JumpModifier instead
public boolean jump(AnimatedSprite player){
//your innitial y-position
final float innitialYPosition = 120;
//-140 means jump upward, positive move downward
JumpModifier jumpModifier = new JumpModifier(1, fromX, toX, fromY, toY, -140, new IEntityModifier.IEntityModifierListener() {
#Override
public void onModifierStarted(IModifier<IEntity> pModifier, IEntity pItem) {
animatedSprite.stopAnimation(0);
}
#Override
public void onModifierFinished(IModifier<IEntity> pModifier, IEntity pItem) {
animatedSprite.animate(50);
animatedSprite.setY(innitialYPosition);
}
});
animatedSprite.registerEntityModifier(jumpModifier);
}
I found this example of Internal Frames
http://docs.oracle.com/javase/tutorial/uiswing/components/internalframe.html
Is it possible to make the same internal Frames in JavaFX?
With JFXtras there is a Window control, where you can add content and handle the internal window behavior.
First you will need to put in your classpath the jfxtras library. They have some instructions where you can get the library. If you are using maven, just need to add:
<dependency>
<groupId>org.jfxtras</groupId>
<artifactId>jfxtras-labs</artifactId>
<version>2.2-r5</version>
</dependency>
Or download the library and put it into your project classpath, whatever.
Now I put a sample of the demo of the Window with a little difference, allowing generation of several windows.
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.stage.Stage;
import jfxtras.labs.scene.control.window.CloseIcon;
import jfxtras.labs.scene.control.window.MinimizeIcon;
import jfxtras.labs.scene.control.window.Window;
public class WindowTests extends Application {
private static int counter = 1;
private void init(Stage primaryStage) {
final Group root = new Group();
Button button = new Button("Add more windows");
root.getChildren().addAll(button);
primaryStage.setResizable(false);
primaryStage.setScene(new Scene(root, 600, 500));
button.setOnAction(new EventHandler<ActionEvent>() {
#Override
public void handle(ActionEvent arg0) {
// create a window with title "My Window"
Window w = new Window("My Window#"+counter);
// set the window position to 10,10 (coordinates inside canvas)
w.setLayoutX(10);
w.setLayoutY(10);
// define the initial window size
w.setPrefSize(300, 200);
// either to the left
w.getLeftIcons().add(new CloseIcon(w));
// .. or to the right
w.getRightIcons().add(new MinimizeIcon(w));
// add some content
w.getContentPane().getChildren().add(new Label("Content... \nof the window#"+counter++));
// add the window to the canvas
root.getChildren().add(w);
}
});
}
public double getSampleWidth() {return 600;}
public double getSampleHeight() {return 500;}
#Override
public void start(Stage primaryStage) throws Exception {
init(primaryStage);
primaryStage.show();
}
public static void main(String[] args) {launch(args);}
}
In the original demo, the event code was in the init method, and no button was included. I add the button to create dynamically windows and adding them to the screen.
Here is a snapshot of the result of the application:
I totally recommend you try the demo of jfxtras. They have really great stuff. Hope it helps.
You can implement simple internal window themselves. Main idea, that InternalWindow class just skeleton, that has internal frame like functionality. You can apply any content to it.
1) Declare class
public class InternalWindow extends Region
2) You should be able to set content in window
public void setRoot(Node node) {
getChildren().add(node);
}
3) You should be able to bring window to front if many window exist
public void makeFocusable() {
this.setOnMouseClicked(mouseEvent -> {
toFront();
});
}
4) Now we need dragging functionality
//just for encapsulation
private static class Delta {
double x, y;
}
//we can select nodes that react drag event
public void makeDragable(Node what) {
final Delta dragDelta = new Delta();
what.setOnMousePressed(mouseEvent -> {
dragDelta.x = getLayoutX() - mouseEvent.getScreenX();
dragDelta.y = getLayoutY() - mouseEvent.getScreenY();
//also bring to front when moving
toFront();
});
what.setOnMouseDragged(mouseEvent -> {
setLayoutX(mouseEvent.getScreenX() + dragDelta.x);
setLayoutY(mouseEvent.getScreenY() + dragDelta.y);
});
}
5) Also we want able to resize window (I show only simple right-bottom resizing)
//current state
private boolean RESIZE_BOTTOM;
private boolean RESIZE_RIGHT;
public void makeResizable(double mouseBorderWidth) {
this.setOnMouseMoved(mouseEvent -> {
//local window's coordiantes
double mouseX = mouseEvent.getX();
double mouseY = mouseEvent.getY();
//window size
double width = this.boundsInLocalProperty().get().getWidth();
double height = this.boundsInLocalProperty().get().getHeight();
//if we on the edge, change state and cursor
if (Math.abs(mouseX - width) < mouseBorderWidth
&& Math.abs(mouseY - height) < mouseBorderWidth) {
RESIZE_RIGHT = true;
RESIZE_BOTTOM = true;
this.setCursor(Cursor.NW_RESIZE);
} else {
RESIZE_BOTTOM = false;
RESIZE_RIGHT = false;
this.setCursor(Cursor.DEFAULT);
}
});
this.setOnMouseDragged(mouseEvent -> {
//resize root
Region region = (Region) getChildren().get(0);
//resize logic depends on state
if (RESIZE_BOTTOM && RESIZE_RIGHT) {
region.setPrefSize(mouseEvent.getX(), mouseEvent.getY());
} else if (RESIZE_RIGHT) {
region.setPrefWidth(mouseEvent.getX());
} else if (RESIZE_BOTTOM) {
region.setPrefHeight(mouseEvent.getY());
}
});
}
6) Usage. First we construct all layout. Then apply it to InternalWindow.
private InternalWindow constructWindow() {
// content
ImageView imageView = new ImageView("https://upload.wikimedia.org/wikipedia/commons/thumb/a/a9/Cheetah4.jpg/250px-Cheetah4.jpg");
// title bar
BorderPane titleBar = new BorderPane();
titleBar.setStyle("-fx-background-color: green; -fx-padding: 3");
Label label = new Label("header");
titleBar.setLeft(label);
Button closeButton = new Button("x");
titleBar.setRight(closeButton);
// title bat + content
BorderPane windowPane = new BorderPane();
windowPane.setStyle("-fx-border-width: 1; -fx-border-color: black");
windowPane.setTop(titleBar);
windowPane.setCenter(imageView);
//apply layout to InternalWindow
InternalWindow interalWindow = new InternalWindow();
interalWindow.setRoot(windowPane);
//drag only by title
interalWindow.makeDragable(titleBar);
interalWindow.makeDragable(label);
interalWindow.makeResizable(20);
interalWindow.makeFocusable();
return interalWindow;
}
7) And how add window to layout
#Override
public void start(Stage primaryStage) throws Exception {
Pane root = new Pane();
root.getChildren().add(constructWindow());
root.getChildren().add(constructWindow());
primaryStage.setScene(new Scene(root, 300, 275));
primaryStage.show();
}
Result
Full code: gist
Upd about close button:
You can add method to InternalWindow
public void setCloseButton(Button btn) {
btn.setOnAction(event -> ((Pane) getParent()).getChildren().remove(this));
}
And when construct:
interalWindow.setCloseButton(closeButton);