Wednesday, 18 July 2012

Gradients In JavaFX 2



The content of this blog is moved and published here.

Please wait...The page will automatically redirect.










Tuesday, 12 June 2012

Scroller control in JavaFX 2



The content of this blog is moved and published here.












Wednesday, 18 January 2012

Sliding in JavaFX (It’s all about clipping)


JavaFX animation package provides many amazing animation/transition effects. Let’s look into another effect, “Sliding” effect of node (relative to the parent node).  The term ‘relative’ refers to, like the node should hide relatively under the parent node by sliding. If you are familiar with “Titled Pane”, I am speaking about the same effect how the content of the pane is shown and hide. I am trying to achieve this with some simple logic and using some properties of a node.

Let’s look into an example, how we can hide/show our custom nodes by implementing this sliding effect. In brief, it is all about clipping our node for some duration of time. There are some amazing blogs/articles available in internet explaining the “clip” functionality of a node. I am going to use this clip feature, to achieve this effect.

Below are the screenshots of the example to which the sliding effect is implemented. On click of “Slide Down”, the top pane (blue color) content is shown and on click of “Slide Up” the top pane content is hidden.


Before going into the actual java code, let me explain the logic pictorially.  In the below image, the red color dotted box is the node that is to be shown/hidden by sliding.  The grey shaded region is the Rectangle that is used to clip the node (Means the visible part of node after clipping). And the black border box is the parent node.

 
To get this effect we need to handle three parameters (A, B & C as shown in picture)
                A – Start Point of the Node
                B – Start point of the clip rectangle
                C – Height of the clip rectangle.

Considering the above scenario the code to slide up (hide) the node(topPane) is as below.

Rectangle2D boxBounds = new Rectangle2D(100, 100, 250, 250);
Rectangle clipRect = new Rectangle();
clipRect.setWidth(boxBounds.getWidth());

StackPane topPane = new StackPane();
topPane.setPrefSize( boxBounds.getWidth(),boxBounds.getHeight());  topPane.setClip(clipRect);
                
/* Animation for scroll up. */
Timeline timelineUp = new Timeline();
timelineUp.setCycleCount(1); 
timelineUp.setAutoReverse(true);

final KeyValue kvUp1 = new KeyValue(clipRect.heightProperty(), 0);
final KeyValue kvUp2 = new KeyValue(clipRect.translateYProperty(), boxBounds.getHeight());
final KeyValue kvUp3 = new KeyValue(topPane.translateYProperty(), -boxBounds.getHeight());
final KeyFrame kfUp = new KeyFrame(Duration.millis(200), kvUp1, kvUp2, kvUp3);

timelineUp.getKeyFrames().add(kfUp);


In the above code, for the given amount of duration,
  • Clip rectangle heightProperty is decreased to 0,
  • translateYProperty of clip Rectangle is moved down to amount of height of the node and
  • translateYProperty of node is moved up to the amount of height of the node.

Similarly the code to slide down (show) the node (topPane) is as below

/* Animation for scroll down. */
Timeline timelineDown = new Timeline();
timelineDown.setCycleCount(1);
timelineDown.setAutoReverse(true);

final KeyValue kvDwn1 = new KeyValue(clipRect.heightProperty(), boxBounds.getHeight());
final KeyValue kvDwn2 = new KeyValue(clipRect.translateYProperty(), 0);
final KeyValue kvDwn3 = new KeyValue(topPane.translateYProperty(), 0);
final KeyFrame kfDwn = new KeyFrame(Duration.millis(200), kvDwn1, kvDwn2, kvDwn3);

timelineDown.getKeyFrames().add(kfDwn);

In the above code, for the given amount of duration,
  • Clip rectangle heightProperty is to the height of the node, 
  • translateYProperty of clip Rectangle is moved up to 0 and 
  • translateYProperty of node is moved down to 0. 

Combining all the logic together, below is the final SSCCE (Short, Self Contained, Correct Example) code of the above example.
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.geometry.Rectangle2D;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.paint.CycleMethod;
import javafx.scene.paint.LinearGradient;
import javafx.scene.paint.Stop;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.RectangleBuilder;
import javafx.scene.text.Font;
import javafx.scene.text.TextBuilder;
import javafx.stage.Stage;
import javafx.util.Duration;

public class SlideEffectDemo extends Application {

 private Rectangle2D boxBounds = new Rectangle2D(100, 100, 250, 200);
 
 private StackPane bottomPane;
 private StackPane topPane;
 private Rectangle clipRect;
 private Timeline timelineUp;
 private Timeline timelineDown;
 
 public static void main(String[] args) {
  Application.launch(args);
 }
 
 @Override
 public void start(Stage stage) throws Exception {
  VBox root = new VBox();
  root.setAlignment(Pos.CENTER);
  root.autosize();
  Scene scene = new Scene(root);
  stage.setTitle("Slide Effect Demo");
  stage.setWidth(350);
     stage.setHeight(300);
     stage.setScene(scene);
     stage.show();
     
  configureBox(root);
 }

 private void configureBox(VBox root) {
  StackPane container = new StackPane();
  container.setPrefHeight(250);
  container.setPrefSize(boxBounds.getWidth(), boxBounds.getHeight());
  container.setStyle("-fx-border-width:1px;-fx-border-style:solid;-fx-border-color:#999999;");
  
  // BOTTOM PANE 
  Stop[] stops = new Stop[] { new Stop(0, Color.web("#F89C8C")), new Stop(1, Color.web("#BE250A"))};
  LinearGradient lg = new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, stops);
  bottomPane = new StackPane();
  bottomPane.getChildren().addAll(RectangleBuilder.create().width(boxBounds.getWidth()).height(boxBounds.getHeight()).fill(lg).build(), 
     TextBuilder.create().text("Click the above \"Slide Down\" button to see the top pane content...").wrappingWidth(200).font(Font.font("Arial", 22)).build());
  
  // TOP PANE 
  Stop[] stops2 = new Stop[] { new Stop(0, Color.web("#FFFFFF")), new Stop(1, Color.web("#50AABC"))};
  LinearGradient lg2 = new LinearGradient(0, 0, 0, 1, true, CycleMethod.NO_CYCLE, stops2);
  StackPane sp1 = new StackPane();
  sp1.getChildren().add(TextBuilder.create().text("Click the below \"Slide Up\" button to see the bottom pane content...").wrappingWidth(200).font(Font.font("Arial", 22)).build());
  
  topPane = new StackPane();
  topPane.getChildren().addAll(RectangleBuilder.create().width(boxBounds.getWidth()).height(boxBounds.getHeight()).fill(lg2).build(), sp1);
  container.getChildren().addAll(bottomPane,topPane);
  
  setAnimation();
  
  Group gp = new Group();
  gp.getChildren().add(container);
  root.getChildren().addAll(getActionPane(),gp);
 }

 private void setAnimation(){
  // Initially hiding the Top Pane
  clipRect = new Rectangle();
        clipRect.setWidth(boxBounds.getWidth());
  clipRect.setHeight(0);
  clipRect.translateYProperty().set(boxBounds.getHeight());
  topPane.setClip(clipRect);
  topPane.translateYProperty().set(-boxBounds.getHeight());
  
  // Animation for bouncing effect.
  final Timeline timelineBounce = new Timeline();
  timelineBounce.setCycleCount(2);
  timelineBounce.setAutoReverse(true);
  final KeyValue kv1 = new KeyValue(clipRect.heightProperty(), (boxBounds.getHeight()-15));
  final KeyValue kv2 = new KeyValue(clipRect.translateYProperty(), 15);
  final KeyValue kv3 = new KeyValue(topPane.translateYProperty(), -15);
  final KeyFrame kf1 = new KeyFrame(Duration.millis(100), kv1, kv2, kv3);
  timelineBounce.getKeyFrames().add(kf1);
  
  // Event handler to call bouncing effect after the scroll down is finished.
  EventHandler onFinished = new EventHandler() {
            public void handle(ActionEvent t) {
             timelineBounce.play();
            }
        };
        
        timelineDown = new Timeline();
        timelineUp = new Timeline();
        
        // Animation for scroll down.
  timelineDown.setCycleCount(1);
  timelineDown.setAutoReverse(true);
  final KeyValue kvDwn1 = new KeyValue(clipRect.heightProperty(), boxBounds.getHeight());
  final KeyValue kvDwn2 = new KeyValue(clipRect.translateYProperty(), 0);
  final KeyValue kvDwn3 = new KeyValue(topPane.translateYProperty(), 0);
  final KeyFrame kfDwn = new KeyFrame(Duration.millis(200), onFinished, kvDwn1, kvDwn2, kvDwn3);
  timelineDown.getKeyFrames().add(kfDwn);
  
  // Animation for scroll up.
  timelineUp.setCycleCount(1); 
  timelineUp.setAutoReverse(true);
  final KeyValue kvUp1 = new KeyValue(clipRect.heightProperty(), 0);
  final KeyValue kvUp2 = new KeyValue(clipRect.translateYProperty(), boxBounds.getHeight());
  final KeyValue kvUp3 = new KeyValue(topPane.translateYProperty(), -boxBounds.getHeight());
  final KeyFrame kfUp = new KeyFrame(Duration.millis(200), kvUp1, kvUp2, kvUp3);
  timelineUp.getKeyFrames().add(kfUp);
 }
 
 private HBox getActionPane(){
  HBox hb = new HBox();
  hb.setAlignment(Pos.CENTER);
  hb.setSpacing(10);
  hb.setPrefHeight(40);
  Button upBtn = new Button("Slide Up");
  upBtn.setOnAction(new EventHandler() {
   @Override
   public void handle(ActionEvent arg0) {
    timelineUp.play();
   }
  });
  Button downBtn = new Button("Slide Down");
  downBtn.setOnAction(new EventHandler() {
   @Override
   public void handle(ActionEvent arg0) {
    timelineDown.play();
   }
  });
  hb.getChildren().addAll(downBtn,upBtn);
  return hb;
 }
}
If you observe in the above code, an extra timelineBounce is added to show the bounce effect when the top pane slides down.

A more realistic example of this slide effect can be found here.
Below are the screenshots of this example. The search form slides downwards and upwards on click of the “Search” label.


I hope you can relate the similar logic to slide the node towards LEFT and RIGHT, by using the widthProperty and translateXProperty of the node.

Happy Coding!!

Thursday, 5 January 2012

Percent Width for TableColumn in JavaFX 2.x TableView

The moment I started working with TableView in JavaFX 2.x, the first question raised in my mind is “Why there is no feature of setting the column widths in percentage as we do in HTML?” I am not sure what could be the reasons for not setting this feature. But if my application demands, I have to implement this by somehow ;). Ofcourse there are a couple of issues logged in JIRA  related to this functionality, but till it get resolved here is the way (workaround) how I tried to get it.

And another thing is with the display of “Extra Column” in TableView. I still remember, in the discussion forum, one guy has referred this term as “Ghost Column” :). Below is the screen shot of a simple TableView, with some setting of prefWidth(here 150px) to TableColumns and resize the screen.



Ok, let me get into some action...  The target is, on resizing the window the table columns should keep their relative sizes with respect to container/window.

For achieving this I am taking the help of GridPane for which percentage widths can be set to its columns through ColumnConstraints.

As a first step, a CustomTableColumn class is created that extends TableColumn, to hold the custom percentWidth property for the column.
The code for the CustomTableColumn is as below:

The idea is that, we will create a container, probably a StackPane (CustomTableView) which holds a GridPane and a TableView.  The number of columns in the GridPane will be synchronized with the number of columns in the TableView.

 StackPane is used as the content in each column of the grid. (StackPane because, it fits automatically to its parent size)

Now the core logic is,
Step 1 : We will be creating ColumnConstraints with the percentWidth that is specified for each column(CustomTableColumn) of the TableView. And set these ColumnConstraints to the grid.
Step 2 : The next step is binding each StackPane’s  widthProperty to its corresponding table column’s prefWidthProperty

Considering the above logic, the CustomTableView code is as below:


Combining all the above code, the below is the final SSCCE(Short, Self Contained, Correct Example) code:

On resizing the window, the output is as below. Not only the columns have maintained their relative sizes , but the extra column is no more visible. :)


I hope the above logic will serve the need till the auto sizing feature of TableView is implemented and released.

Happy Coding!! :)

Note: The above logic/code may be or can be improvised. Let me also know the changes for better implementation. :)