I've been playing with animations while getting acquainted with JavaFX 2.0, and I wrote a small test program that was intended to rotate a rectangle along its X and Y axes. Here is the test program:
import javafx.animation.Animation;
import javafx.animation.FadeTransition;
import javafx.animation.ParallelTransition;
import javafx.animation.RotateTransition;
import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.RectangleBuilder;
import javafx.scene.transform.Rotate;
import javafx.stage.Stage;
import javafx.util.Duration;
public class ParallelTransitionTest extends Application
{
public static void main( String[] args )
{
launch( args );
}
#Override
public void start( Stage primaryStage ) throws Exception
{
init( primaryStage );
primaryStage.show();
}
private void init( Stage primaryStage )
{
primaryStage.setTitle( "Parallel Transition" );
primaryStage.setResizable( true );
// Create the scene
BorderPane root = new BorderPane();
Scene scene = new Scene( root, 800, 600, true );
scene.setFill( Color.BLACK );
primaryStage.setScene( scene );
Rectangle rect = RectangleBuilder.create()
.width( 100 ).height( 100 )
.x( 350 ).y( 250 )
.fill( Color.BLUE )
.build();
RotateTransition rotationY = new RotateTransition();
rotationY.setAxis( Rotate.Y_AXIS );
rotationY.setDuration( Duration.seconds( 5 ) );
rotationY.setByAngle( 360 );
rotationY.setNode( rect );
rotationY.setAutoReverse( true );
rotationY.setCycleCount( Animation.INDEFINITE );
RotateTransition rotationX = new RotateTransition();
rotationX.setAxis( Rotate.X_AXIS );
rotationX.setDuration( Duration.seconds( 5 ) );
rotationX.setByAngle( 360 );
rotationX.setNode( rect );
rotationX.setAutoReverse( true );
rotationX.setCycleCount( Animation.INDEFINITE );
FadeTransition fade = new FadeTransition();
fade.setDuration( Duration.seconds( 5 ) );
fade.setToValue( 0.2 );
fade.setNode( rect );
fade.setAutoReverse( true );
fade.setCycleCount( Animation.INDEFINITE );
ParallelTransition transition = new ParallelTransition( rect,
rotationX, rotationY, fade );
transition.setAutoReverse( true );
transition.play();
root.getChildren().add( rect );
}
}
Unfortunately, the rotation is only happening for one of the axes. My assumption is that both RotationTransitions are running, but that one is overwriting the rotation applied by the other. Is this the intended behavior of RotationTransition?
Also, if the following three lines are commented:
rotationY.setNode( rect );
...
rotationX.setNode( rect );
...
fade.setNode( rect );
I get a NullPointerException. The docs suggest that you shouldn't need to set the nodes on transitions included in a ParallelTransition. Is this a bug?
You should create another node add your rect to that node, and then:
RotateTransition rotationY = new RotateTransition();
rotationY.setAxis( Rotate.Y_AXIS );
rotationY.setDuration( Duration.seconds( 5 ) );
rotationY.setByAngle( 360 );
rotationY.setNode( rect );
rotationY.setAutoReverse( true );
rotationY.setCycleCount( Animation.INDEFINITE );
RotateTransition rotationX = new RotateTransition();
rotationX.setAxis( Rotate.X_AXIS );
rotationX.setDuration( Duration.seconds( 5 ) );
rotationX.setByAngle( 360 );
rotationX.setNode( NEWNODE );
rotationX.setAutoReverse( true );
rotationX.setCycleCount( Animation.INDEFINITE );
Related
I am trying to write a custom resize policy that acts like TableView.CONSTRAINED_RESIZE_POLICY in that it set the total width of all visible to columns to the total available width in the table, so that the horizontal scroll bar never appears.
I am using the line double widthAvailable = table.getWidth() - getScrollbarWidth(table); to try do to this (see full code below).
Unfortunately, this calculation seems to return 4 too many. My guess is that there is some other thing I am also supposed to be subtracting from the table width that I am missing, which happens to be 4 pixels wide in the default JavaFX theme.
Here is a complete program demonstrating the problem:
package net.joshuad.hypnos.test;
import java.util.List;
import java.util.Locale;
import java.util.Set;
import javafx.application.Application;
import javafx.beans.property.SimpleObjectProperty;
import javafx.collections.FXCollections;
import javafx.geometry.Orientation;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.ScrollBar;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.Callback;
public class CustomResizeExample extends Application {
#SuppressWarnings("unchecked")
private Parent getContent () {
TableView <Locale> table = new TableView <>( FXCollections.observableArrayList( Locale.getAvailableLocales() ) );
TableColumn <Locale, String> countryCode = new TableColumn <>( "CountryCode" );
countryCode.setCellValueFactory( new PropertyValueFactory <>( "country" ) );
TableColumn <Locale, String> language = new TableColumn <>( "Language" );
language.setCellValueFactory( new PropertyValueFactory <>( "language" ) );
table.getColumns().addAll( countryCode, language );
TableColumn <Locale, Locale> local = new TableColumn <>( "Locale" );
local.setCellValueFactory( c -> new SimpleObjectProperty <>( c.getValue() ) );
table.getColumns().addAll( local );
table.setColumnResizePolicy( new CustomResizePolicy() );
BorderPane pane = new BorderPane( table );
return pane;
}
#Override
public void start ( Stage stage ) throws Exception {
stage.setScene( new Scene( getContent(), 800, 400 ) );
stage.show();
}
public static void main ( String[] args ) {
launch ( args );
}
}
#SuppressWarnings ( "rawtypes" )
class CustomResizePolicy implements Callback <TableView.ResizeFeatures, Boolean> {
#SuppressWarnings("unchecked")
#Override
public Boolean call ( TableView.ResizeFeatures feature ) {
TableView table = feature.getTable();
List <TableColumn> columns = table.getVisibleLeafColumns();
double widthAvailable = table.getWidth() - getScrollbarWidth ( table );
double forEachColumn = widthAvailable / columns.size();
for ( TableColumn column : columns ) {
column.setMinWidth( forEachColumn );
column.setMaxWidth( forEachColumn );
}
return true;
}
private double getScrollbarWidth ( TableView table ) {
double scrollBarWidth = 0;
Set <Node> nodes = table.lookupAll( ".scroll-bar" );
for ( final Node node : nodes ) {
if ( node instanceof ScrollBar ) {
ScrollBar sb = (ScrollBar) node;
if ( sb.getOrientation() == Orientation.VERTICAL ) {
if ( sb.isVisible() ) {
scrollBarWidth = sb.getWidth();
}
}
}
}
return scrollBarWidth;
}
}
See how the table is a little wider than it ought to be? I would like to be able to set the columns' width such that no horizontal scrollbar appears.
Scenic View is a useful tool to get acquainted with. It gives you many details about the scene graph. The space you are looking for is clipped-container:
Note that setting a CustomResizePolicy the way you did effectively does not allow columns to be resized as they are always allocated an equal share of available space.
Here is the calculation you are probably looking for:
Region region = null;
Set<Node> nodes = table.lookupAll(".clipped-container");
for (Node node : nodes) {
if (node instanceof Region) {
region = (Region) node;
}
}
for (TableColumn<Locale, ?> column : table.getColumns()) {
column.setPrefWidth(region.getWidth() / table.getColumns().size());
}
I don't completely understand why CONSTRAINED_RESIZE_POLICY is not what you want, as it divides the space equally and does not allow resizing to exceed the allocated space (thus a horizontal scrollbar does not appear), but if you're making some special resize policy the above should help.
I have access to a database that returns the temperature of a location and time of that location every 5 seconds.
I have an idea of plotting the time on the x-axis.
And probably by using the java swing timer I would be able to add data into the graph every 5 seconds.
However, I do not know how to implement that because I thought of adding a timer in createDataset( ) but since it returns a dataset, I won't be able to achieve it.
Any idea how I would be able to add data into the graph every 5 seconds?
Here is my code:
import java.awt.Color;
import java.awt.BasicStroke;
import org.jfree.chart.ChartPanel;
import org.jfree.chart.JFreeChart;
import org.jfree.data.xy.XYDataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.ui.ApplicationFrame;
import org.jfree.ui.RefineryUtilities;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.ChartFactory;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
public class XYLineChart_AWT extends ApplicationFrame {
public XYLineChart_AWT( String applicationTitle, String chartTitle ) {
super(applicationTitle);
JFreeChart xylineChart = ChartFactory.createXYLineChart(
chartTitle ,
"Time" ,
"Temperature" ,
createDataset() ,
PlotOrientation.VERTICAL ,
true , true , false);
ChartPanel chartPanel = new ChartPanel( xylineChart );
chartPanel.setPreferredSize( new java.awt.Dimension( 560 , 367 ) );
final XYPlot plot = xylineChart.getXYPlot( );
XYLineAndShapeRenderer renderer = new XYLineAndShapeRenderer( );
renderer.setSeriesPaint( 0 , Color.RED );
renderer.setSeriesStroke( 0 , new BasicStroke( 4.0f ) );
plot.setRenderer( renderer );
setContentPane( chartPanel );
}
private XYDataset createDataset( ) {
final XYSeries temp = new XYSeries( "Temperature" );
//time = getTime(); //returns a float time in seconds.milliseconds
//temperature = getTemp(); //returns a number temperature
//I want to add data into temp every 5 seconds but i don't know how to do it
temp.add( 1.0 , 1.0 );
temp.add( 2.0 , 4.0 );
temp.add( 3.0 , 3.0 );
final XYSeriesCollection dataset = new XYSeriesCollection( );
dataset.addSeries( temp );
return dataset;
}
public static void main( String[ ] args ) {
XYLineChart_AWT chart = new XYLineChart_AWT("Temp",
"Temperature of some location");
chart.pack( );
RefineryUtilities.centerFrameOnScreen( chart );
chart.setVisible( true );
}
}
Rather than putting a timer in your createDataset() method you can instead spawn a new thread from your main method that modifies your JFreeChart dataset every 5 seconds.
For example you could do it something like this:
public static void main( String[ ] args ) {
XYLineChart_AWT chart = new XYLineChart_AWT("Temp",
"Temperature of some location");
chart.pack( );
RefineryUtilities.centerFrameOnScreen( chart );
chart.setVisible( true );
//now make your timer
int delay = 5000; //milliseconds
ActionListener timerAction = new ActionListener() {
public void actionPerformed(ActionEvent evt) {
//some code here to get and modify your dataset so it can be updated
// ----
// ----
//now apply your new dataset to your JFreeChart
xylineChart.getXYPlot().setDataset(myNewDataset);
}
};
new Timer(delay, timerAction).start();
}
Remember to add some code to remove old entries in your dataset so that the chart remains readable and all the values on the Time axis remain the same distance apart between different datasets, for example make sure there are no more than 24 items (2 minutes of data) plotted at a time.
I'd like to have a progress bar that updates inside an onAction block. For some reason in the following code, the progress bar doesn't update until it exits the onAction block. I get no progress for nine seconds and then 90%. The System.out prints just fine.
package net.snortum.play;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.ProgressBar;
import javafx.scene.layout.GridPane;
import javafx.scene.layout.HBox;
import javafx.scene.text.Text;
import javafx.stage.Stage;
public class ProgressBarTester extends Application {
public static void main( String[] args ) {
launch( ProgressBarTester.class, args );
}
#Override
public void start( final Stage stage ) throws Exception {
final GridPane grid = new GridPane();
grid.setAlignment( Pos.CENTER );
grid.setHgap( 10 );
grid.setVgap( 10 );
grid.setPadding( new Insets( 25, 25, 25, 25 ) );
final Text title = new Text( "Test Progress Bar" );
int col = 0, row = 0, colSpan = 2, rowSpan = 1;
grid.add( title, col, row, colSpan, rowSpan );
col = 0;
row++;
grid.add( new Label( "Progress:" ), col, row );
col = 1;
final ProgressBar progress = new ProgressBar( 0.0 );
grid.add( progress, col, row );
final HBox hbox = new HBox();
hbox.setAlignment( Pos.BASELINE_RIGHT );
final Button submit = new Button( "Go" );
hbox.getChildren().add( submit );
col = 1;
row++;
grid.add( hbox, col, row );
submit.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle( ActionEvent event ) {
double size = 10.0;
for (double i = 0.0; i < size; i++){
progress.setProgress( i / size );
System.out.printf("Complete: %02.2f%n", i * 10);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
} );
final Scene scene = new Scene( grid );
stage.setScene( scene );
stage.setTitle( "Test Progress Bar" );
stage.show();
}
}
Long form of tomsomtom's precise answer:
If you use the JavaFX Thread to do this kind of long term work, you stop the UI from updating etc. So you have to run your worker in a sparate thread. If you run in a separate Thread, all GUI operatione like progress.setProgress() have to be passed back to the JavaFX Thread using runLater() as JavaFX is not multithreaded.
Try this in your Action:
submit.setOnAction( new EventHandler<ActionEvent>() {
#Override
public void handle( ActionEvent event ) {
double size = 10.0;
new Thread(){
public void run() {
for (double i = 0.0; i < size; i++){
final double step = i;
Platform.runLater(() -> progress.setProgress( step / size ));
System.out.printf("Complete: %02.2f%n", i * 10);
try {
Thread.sleep(1000);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
}
}
}.start();
}
} );
You are blocking the javafx event thread you need to do the work in another thread and sync back only the setProgress call with Platform.runLater
I would like to resize the below created shape. but cannot get it.
The project is to create a transparent rectangle to show only a part of the desktop, and hide the rest. The transparent zone is the result of a substraction, and I need to make it resizable by the user.
I tryed several ways, such as adapting from this : https://gist.github.com/jewelsea/1441960
But couldn't get it.
Here is my code :
#Override
public void start(Stage stage) {
Group group = new Group();
Rectangle rect = new Rectangle(0, 0, 350, 300);
Rectangle clip = new Rectangle(20, 20, 200, 200);
clip.setArcHeight(15);
clip.setArcWidth(15);
Shape shape = Shape.subtract(rect, clip);
shape.setFill(Color.GRAY);
group.getChildren().add(shape);
Scene scene = new Scene(group);
scene.setFill(Color.TRANSPARENT);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(scene);
stage.show();
}
Any link or help would be appreciated.
If you create a Shape by Shape.subtract(...), you don't have any mechanism to change the properties of it afterwards (in the sense of changing the bounds of the shapes that were used to create it). You would have to remove the shape from its parent, recompute the rect and clip, recompute the shape, and add the new shape back into the scene.
It might be better to use a Path here so that you can manipulate the coordinates without creating a new shape every time. Traverse one way (say clockwise) around the outside (filled portion), and then the other way (anti-clockwise) around the inner (transparent portion). The resulting shape will be the same as a subtraction of the inner portion from the outer portion. The initial setup will potentially require considerably more code, but you can then manipulate the coordinates as you need to.
I'm not sure exactly what functionality you were looking for, but the following allows you to drag the inner portion around by clicking and dragging on it, and allows you to move the whole window by clicking and dragging on the outer portion. It should be enough for you to figure out what you need. I didn't include the nice rounded corners you had in your example, but you can fairly easily implement those using ArcTo path elements.
import javafx.application.Application;
import javafx.application.Platform;
import javafx.beans.binding.DoubleBinding;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.beans.value.ObservableDoubleValue;
import javafx.geometry.Point2D;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.scene.paint.Color;
import javafx.scene.shape.ClosePath;
import javafx.scene.shape.LineTo;
import javafx.scene.shape.MoveTo;
import javafx.scene.shape.Path;
import javafx.scene.shape.PathElement;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
public class TransparentRectangle extends Application {
#Override
public void start(Stage stage) {
Pane root = new Pane();
PathElement start = new MoveTo(0, 0);
PathElement outerTopRight = createBoundLineTo(root.widthProperty(), 0);
PathElement outerBottomRight = createBoundLineTo(root.widthProperty(), root.heightProperty());
PathElement outerBottomLeft = createBoundLineTo(0, root.heightProperty());
PathElement outerTopLeft = new LineTo(0, 0);
DoubleProperty innerLeft = new SimpleDoubleProperty(20);
DoubleProperty innerTop = new SimpleDoubleProperty(20);
DoubleBinding innerRight = innerLeft.add(180);
DoubleBinding innerBottom = innerTop.add(180);
PathElement innerTopLeft = createBoundLineTo(innerLeft, innerTop);
PathElement innerTopRight = createBoundLineTo(innerRight, innerTop);
PathElement innerBottomRight = createBoundLineTo(innerRight, innerBottom);
PathElement innerBottomLeft = createBoundLineTo(innerLeft, innerBottom);
Path path = new Path(
start, outerTopRight,
outerBottomRight, outerBottomLeft,
outerTopLeft,
innerTopLeft, innerBottomLeft,
innerBottomRight, innerTopRight,
innerTopLeft, new ClosePath()
);
path.setFill(Color.GRAY);
path.setStroke(Color.TRANSPARENT);
root.getChildren().add(path);
class Wrapper<T> { T value ; }
Wrapper<Point2D> mouseLocation = new Wrapper<>();
// Drag on gray portion of path - move entire window:
path.setOnDragDetected(event -> {
mouseLocation.value = new Point2D(event.getScreenX(), event.getScreenY());
});
path.setOnMouseDragged(event -> {
if (mouseLocation.value != null) {
stage.setX(stage.getX() + event.getScreenX() - mouseLocation.value.getX());
stage.setY(stage.getY() + event.getScreenY() - mouseLocation.value.getY());
mouseLocation.value = new Point2D(event.getScreenX(), event.getScreenY());
}
});
path.setOnMouseReleased(event -> mouseLocation.value = null);
// Drag on scene (i.e not on path, i.e. on transparent part) - move transparent part
root.setOnDragDetected(event -> {
mouseLocation.value = new Point2D(event.getScreenX(), event.getScreenY());
});
root.setOnMouseDragged(event -> {
if (mouseLocation.value != null) {
innerLeft.set(innerLeft.get() + event.getScreenX() - mouseLocation.value.getX());
innerTop.set(innerTop.get() + event.getScreenY() - mouseLocation.value.getY());
mouseLocation.value = new Point2D(event.getScreenX(), event.getScreenY());
}
});
root.setOnMouseReleased(event -> mouseLocation.value = null);
// No close button on a transparent window, so exit on double click:
root.setOnMouseClicked(event -> {
if (event.getClickCount() == 2) Platform.exit();
event.consume();
});
Scene scene = new Scene(root, 800, 600);
scene.setFill(Color.TRANSPARENT);
stage.initStyle(StageStyle.TRANSPARENT);
stage.setScene(scene);
stage.show();
}
private PathElement createBoundLineTo(ObservableDoubleValue x, ObservableDoubleValue y) {
LineTo lineTo = new LineTo();
lineTo.xProperty().bind(x);
lineTo.yProperty().bind(y);
return lineTo ;
}
private PathElement createBoundLineTo(double fixedX, ObservableDoubleValue y) {
LineTo lineTo = new LineTo();
lineTo.setX(fixedX);
lineTo.yProperty().bind(y);
return lineTo ;
}
private PathElement createBoundLineTo(ObservableDoubleValue x, double fixedY) {
LineTo lineTo = new LineTo();
lineTo.setY(fixedY);
lineTo.xProperty().bind(x);
return lineTo ;
}
public static void main(String[] args) {
launch(args);
}
}
I did some code but still im on the road to complete the whole chart.but at this moment I just want to show a CIRCLE in the emulator,just to make sure that im on the right path.but none of them is appear.the requestWindowFeature( Window.FEATURE_NO_TITLE ); is working but the rest is not.Help me.
package com.Sabry.yesbmi;
import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
public class Chart extends Activity {
Paint paint;
#Override
public void onCreate( Bundle savedInstanceState ) {
super.onCreate( savedInstanceState );
requestWindowFeature( Window.FEATURE_NO_TITLE );
setContentView( R.layout.chart );
initializeView();
}
private void initializeView() {
paint = new Paint();
paint.setColor( Color.BLACK );
paint.setStrokeWidth( 2 );
paint.setTextSize( 20 );
paint.setStyle(Paint.Style.STROKE);
setContentView( new Panel( this ) );
}
class Panel extends View {
public Panel( Context context ) {
super( context );
}
}
public void onDraw( Canvas canvas ) {
int originX = 10, originY = 800;
canvas.drawColor( Color.WHITE ); // Background color
canvas.drawCircle( 300, 80, 20, paint );
}
}
I edited your question and formatted the code. It is now very obvious that you have onDraw() outside the Panel class. As you have it, it is a method of your Activity.
Here, I've removed the extra braces.
class Panel extends View {
public Panel( Conext context ) {
super( context );
}
public void onDraw( Canvas canvas ) {
int originX = 10, originY = 800;
canvas.drawColor( Color.WHITE ); // Background color
canvas.drawCircle( 300, 80, 20, paint );
}
}
It is not required, but is good practice, to decorate overridden methods with the #Override annotation. If you had done this, the compiler would have given you an error since Acitivty does not have an onDraw method.