Sunday, November 8, 2015

create a custom control in javafx 8

in this tutorial we are going to modify a well known javafx sample : the lcd clock and turn it in a reusable control. so we need to create a custom control that behave that way. the final result will look like this :

custom javafx 8 control


  1. architecture of controls
  2. creating a custom control.
  3. adding events handling to a control.
  4. stylling a control.
  5. source code.
  6. further reading.

architecture of controls

javafx 8 controls hierarchy


all the controls extend the abstarct class Control which in turn extends the class Region. The Region class is a resizable Parent node which can be styled from CSS and also layout the children. the important properties and methods of the Region Class that you must take into account are the following :



Properties of the Region Class
name access type description
width read-only Double the width of the node set by the parent container
height read-only Double the height of the node set by the parent container
minWidth read-write Double the overriden min max, preferred min, preffered and max for width and height of the Region
minHeight read-write Double
prefWidth read-write Double
prefHeight read-write Double
maxWidth read-write Double
maxHeight read-write Double
padding read-write Insets padding of the region



Functions of the Region Class
Name Description
computeMaxHeight(double width)
Computes the maximum height of this region.
computeMaxWidth(double width) Computes the maximum width of this region.
computeMinHeight(double width) Computes the minimum height of this region.
computeMinWidth(double width) Computes the minimum width of this region.

prefWidth and preHeight are by default set to zero. it's up to you to give then an appropiate value. you must take into account the css box model when calculating them :


  • padding is the space between the content and the content’s border.
  • border is the line that’s drawn around each edge of the box.
  • margin is what separates one node from another.
  • the Inset is the sum of the padding and the border.

mvc pattern in javafx 8

Controls follow the classic MVC design pattern. as you can see every Control (the model) has a reference to a single Skin. so there is a one to one relationship between them, The Control Class holds all the properties needed to represent his state and its the responsability of the Skin class to set the visual wich include calculating the min and max dimensions and handle all the interaction with the user.

creating a custom control.

so to implement a custom control we need two classes at leat one which extends the Control Class and one extends the skin Class. each of them have a methode that allow to retrieve the other.
The skin of a control is defined by the Skin<C extends Skinnable> interface. this interface ha three methods :
Name Description
void dispose() Called by a Skinnable when the Skin is replaced on the Skinnable.
 Node getNode() Gets the Node which represents this Skin.
C getSkinnable Gets the Skinnable to which this Skin is assigned.

custom control implementation will extend the Control class, and its skin implementation will extend the SkinBase class :
 in our example the two classes are :

//the control :

public class LcdClock extends Control {

//....

@Override

protected Skin&lt;?&gt; createDefaultSkin() {

return new LcdClockSkin(this);

}

}




//the skin

public  class LcdClockSkin extends SkinBase&lt;LcdClock&gt; {

//....

public LcdClockSkin(LcdClock control) {

super(control);

//....

}

the LcdClockSkin manage the properties of LcdClock and define its size by redefining these methods :
@Override

protected double computePrefHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {

return 100;

}

@Override

protected double computePrefWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {

return 300;

}

@Override

protected double computeMinHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {

return 30;

}

@Override

protected double computeMinWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {

return 100;

}

@Override

protected double computeMaxHeight(double width, double topInset, double rightInset, double bottomInset, double leftInset) {

return computePrefHeight(width, topInset, rightInset, bottomInset, leftInset);

}

@Override

protected double computeMaxWidth(double height, double topInset, double rightInset, double bottomInset, double leftInset) {

return computePrefWidth(height, topInset, rightInset, bottomInset, leftInset);

}
the drawing of the clock is done by overrinding this method :
@Override

protected void layoutChildren(double contentX, double contentY, double contentWidth, double contentHeight) {

if (invalidate){

updateClock(contentWidth, contentHeight) ;

invalidate=false;

}

layoutInArea(this.getNode(), contentX, contentY, contentWidth, contentHeight, -1, HPos.CENTER, VPos.CENTER);

}

adding event handling :

for demo purpose we add an onclick event to our control which print a simple message on the console :
first of all we define an ordinary property : onActionPropriety. of the type EventHandler<ActionEvent>.

private final ObjectProperty<EventHandler<ActionEvent>> onAction = new ObjectPropertyBase<EventHandler<ActionEvent>>() {

@Override

public Object getBean() {

return LcdClock.this;

}

@Override

public String getName() {

return "onAction";

}

@Override

protected void invalidated() {

setEventHandler(ActionEvent.ACTION, get());

}

};

public EventHandler<ActionEvent> getOnAction() {

return onActionProperty().get();

}

public void setOnAction(EventHandler<ActionEvent> value) {

onAction.set(value);

}

public ObjectProperty<EventHandler<ActionEvent>> onActionProperty() {

return onAction;

}
after that we add the following code to the constructor of the skin Class :
getSkinnable().setOnMousePressed((e) -> {

getSkinnable().fireEvent(new ActionEvent());

});

skining the control :

Making a property styleable, then, consist of:
  1. defining the javafx.beans.property as a StyleableProperty
  2. creating a corresponding CssMetaData
  3. ensuring the CssMetaData is returned in the List<CssMetaData>
Pseudo-class state support consists of notifying CSS of a pseudo-class state change via the invalidated() method of a property.
in our case we are make only one property styleable. let's say it's the backgroundColor property. to achieve this result the property must extend one of the StyleableProperty classe. here the StyleableObjectProperty :

private ObjectProperty<Color>   backgroundColor = new StyleableObjectProperty<Color>(Color.BLACK) {

@Override

public Object getBean() {

return LcdClock.this;

}

@Override

public String getName() {

return "backgroundColor";

}

@Override

public CssMetaData getCssMetaData() {

return StyleableProperties.LCD_CLOCK_METADATA;

}

@Override

public void invalidated() {

updateBackgroundColor();

}

};
In java doc we find the following definition of CssMetaData :

"A CssMetaData instance provides information about the CSS style and provides the hooks that allow CSS to set a property value. It encapsulates the CSS property name, the type into which the CSS value is converted, and the default value of the property.
CssMetaData is the bridge between a value that can be represented syntactically in a .css file, and a StyleableProperty. There is a one-to-one correspondence between a CssMetaData and a StyleableProperty. Typically, the CssMetaData of a will include the CssMetaData of its ancestors. During CSS processing, the CSS engine iterates over the Node's CssMetaData, looks up the parsed value of each, converts the parsed value, and sets the value on the StyleableProperty.
The method Node.getCssMetaData() is called to obtain the List<CssMetaData>. This method is called frequently and it is prudent to return a static list rather than creating the list on each call. By convention, node classes that have CssMetaData will implement a static method getClassCssMetaData() and it is customary to have getCssMetaData() simply return getClassCssMetaData(). The purpose of getClassCssMetaData() is to allow sub-classes to easily include the CssMetaData of some ancestor. "

private static final CssMetaData<LcdClock, Color> LCD_CLOCK_METADATA = new CssMetaData<LcdClock, Color>("-fx-lcd-bk", StyleConverter.getColorConverter(), Color.BLACK) {

@Override

public boolean isSettable(LcdClock styleable) {

return styleable.backgroundColor == null || !styleable.backgroundColor.isBound();

}

@Override

public StyleableProperty<Color> getStyleableProperty(LcdClock styleable) {

return (StyleableProperty<Color>) (WritableValue<Color>) styleable.backgroundColor;

}

};

finnally getClassCssMetaData() and getCssMetaData() have to be created :

static {

List<CssMetaData<? extends Styleable, ?>> temp

= new ArrayList<>(Control.getClassCssMetaData());

Collections.addAll(temp, LCD_CLOCK_METADATA);

cssMetaDataList = Collections.unmodifiableList(temp);

}

private static final List<CssMetaData<? extends Styleable, ?>> cssMetaDataList;

@Override

protected List<CssMetaData<? extends Styleable, ?>> getControlCssMetaData() {

return getClassCssMetaData();

}

public static List<CssMetaData<? extends Styleable, ?>> getClassCssMetaData() {

return StyleableProperties.cssMetaDataList;

} 

source code

you can find the source code for netbeans at https://github.com/mooninvader/lcdClock

further reading


Friday, September 18, 2015

tetris game with javafx

TETRIS GAME WITH JAVAFX



a simple tetris game written in javafx under netbeans.

you can find the source code of the project at the following link :






Saturday, August 15, 2015

parsing an arithmetic expression using the shunting-yard algorithm and drawing with javafx


this time I am demonstrating the use of the shunting-yard algorithm for parsing an arithmetic expression. the parser is written in groovy. you can use the following functions :


  • cos
  • sin 
  • tan
  • log
  • log10
  • sqrt
  • abs
  • pow
  • max
  • min 


and the following  operators :

  • +
  • -
  • *
  • /
  • ^ (power not xor).

the parser is from me but for the graphical part, i found an intersting post on stackoverflow :

http://stackoverflow.com/questions/24005247/draw-cartesian-plane-graphi-with-canvas-in-javafx

so i use the awsome piece of code given by jewelsea : http://stackoverflow.com/users/1155209/jewelsea

the code is very concise and easy to ready you extend or fix it with no pain. perhaps it is a foundation for a new open source project what do you think?

any way here is some snapshots :







i also user the library exp4j http://www.objecthunter.net/exp4j/ to test my code.

as usual you can find the code source at github : https://github.com/mooninvader/FunctionPlotter



Monday, July 13, 2015

using ireport in javafx applications



one of reporting solutions in java is to use ireports from JasperSoft. here we choose to generate a xhtml report and send it to a web view component. 
you must now that ireport wont start under java8 you must have jdk 7 or under installed .
go to directory of ireport installation and edit the file ireport.conf which is in the  etc folder. uncomment the line jdkhome and make it point to the older jdk.


first we need of course to create a report. this one is simple it's a result of the simple query "select * from emp"  with the schema scott in oracle database.  make a connection and use the wizard for this simple report : 






next we take the resulting scott.jrxml into our new netbeans project :

 to setup a ireport project we must include the following jars :
  • commons-logging.
  • commons-digesters
  • commons-collections.
  • commons-beanutils.
  • groovy-all
  • jasperreports-5.6.0
  •  jasperreports-fonts-5.6.0
  •  jasperreports-htmlcomponent-5.0.

Our goal is to generate a xhtml report and injecting into the Webview component.

to generate the report we use ReportPrinter class :
 



package ireportfx;
import java.io.InputStream;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import net.sf.jasperreports.engine.JRException;
import net.sf.jasperreports.engine.JRExporter;
import net.sf.jasperreports.engine.JRExporterParameter;
import net.sf.jasperreports.engine.JasperCompileManager;
import net.sf.jasperreports.engine.JasperFillManager;
import net.sf.jasperreports.engine.JasperPrint;
import net.sf.jasperreports.engine.JasperReport;
import net.sf.jasperreports.engine.export.JRXhtmlExporter;
        
/**
 *
 * @author hakim
 */
public class ReportPrinter {

    Connection conn = null;

    public ReportPrinter(Connection conn) {
        this.conn = conn;
    }


    private String jasperReport(String name, 
            ResultSet data, Map params) {

        JasperPrint jasperPrint = null;
        try {
            JasperReport jr=JasperCompileManager.compileReport(ReportPrinter.class.getResourceAsStream(name));
            InputStream stream = ReportPrinter.class.getResourceAsStream(name);
            jasperPrint = JasperFillManager.fillReport(
                    jr, params, conn);
        } catch (RuntimeException e) {
            throw e;
        } catch (Exception ex) {
            java.util.logging.Logger.getLogger(ReportPrinter.class.getName()).log(Level.SEVERE, null, ex);
        }

        JRExporter exporter = null;
        StringWriter out = new StringWriter();
        PrintWriter printWriter = new PrintWriter(out);

        try {
            exporter = new JRXhtmlExporter();
            exporter.setParameter(JRExporterParameter.JASPER_PRINT,
                    jasperPrint);
            exporter.setParameter(JRExporterParameter.OUTPUT_WRITER,
                    printWriter);

        } catch (RuntimeException e) {
            throw e;
        }
        try {
            exporter.exportReport();
        } catch (JRException ex) {
            Logger.getLogger(ReportPrinter.class.getName()).log(Level.SEVERE, null, ex);
        }

        try {
            if (conn != null) {
                conn.close();
            }
        } catch (SQLException ex) {
            java.util.logging.Logger.getLogger(ReportPrinter.class.getName()).log(Level.SEVERE, null, ex);
        }

        return out.toString();
    }

    public String generateReport(String ReportName, Map params) {
        return jasperReport(ReportName, null, params);
    }
    
}


and this is the simple controller that fill the report :
 
package ireportfx;

import java.net.URL;
import java.sql.Connection;
import java.sql.DriverManager;
import java.util.ResourceBundle;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.web.WebView;

/**
 *
 * @author hakim
 */
public class FXMLDocumentController implements Initializable {

    @FXML
    WebView webView;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        webView.getEngine().loadContent(generateReport());
    }


    Connection getConnection()  {
        String url = "jdbc:oracle:thin:@localhost:1521:orcl";
        Connection con=null;
        try {
            con = DriverManager.getConnection(url,"scott","tiger");
        } catch (Exception except) {
            except.printStackTrace();

        }
        return con;
    }

    private String generateReport() {
          ReportPrinter rp=new ReportPrinter(getConnection())  ;
          String sp=rp.generateReport("scott.jrxml",  null);
          System.out.println(sp);
          return sp;
    }

}

the result is in the screenshot below :


as usual you can find the full source code at git hub :

https://github.com/mooninvader/ireportfx/tree/master/src/ireportfx


Tuesday, June 30, 2015


Puddles with javafx


 programmingpraxis published once in 2013 a riddle for quantity of water than remain after the rain:


here an implementation with javafx. the solution is made with groovy witch make it really easier to understand.




as usual you can find the source code at github :

https://github.com/mooninvader/puddlesfx 



 

Monday, June 22, 2015

IMAGE CROPPER IN JAVAFX


this time this a demonstration of an application that create small pieces from a bigger image and save them on disk in jpeg format :


the transparent rectangle is simply a text field :


     public TextField createRectangle(double x, double y, double width, double height) {
        TextField rec = new TextField();
        rec.setPrefHeight(1);
        rec.setPrefWidth(1);
        container.getChildren().add(rec);
        rec.setOpacity(0.7f);
        rec.setAlignment(Pos.BOTTOM_CENTER);
        rec.setTranslateX(x);
        rec.setTranslateY(y);
        rec.setFont(Font.font("System", FontWeight.BOLD, 14));
        rec.setPromptText("give a file name");
        rec.setCursor(Cursor.OPEN_HAND);      
        currentRect = rec;

        rec.addEventHandler(MouseEvent.MOUSE_PRESSED, (MouseEvent e) -> {
            if (currentMode == Mode.SELECT) {
                markRectangleAsCurrent((TextField) e.getSource());
                dragAnchor = new Point2D(e.getSceneX(), e.getSceneY());
                initX = ((TextField) e.getSource()).getTranslateX();
                initY = ((TextField) e.getSource()).getTranslateY();
            }
        });

        rec.addEventHandler(MouseEvent.MOUSE_DRAGGED, (MouseEvent e) -> {
            ((TextField) e.getSource()).setTranslateX(initX + e.getSceneX() - dragAnchor.getX());
            ((TextField) e.getSource()).setTranslateY(initY + e.getSceneY() - dragAnchor.getY());
            surroundRectangleWithCorners(((TextField) e.getSource()));
        });

        rec.addEventHandler(MouseEvent.MOUSE_ENTERED, (MouseEvent e) -> {            
            this.currentMode=Mode.SELECT;
        });

        rec.addEventHandler(MouseEvent.MOUSE_EXITED, (MouseEvent e) -> {
            this.currentMode=Mode.CREATE;
        });

        rec.addEventHandler(KeyEvent.KEY_PRESSED, (KeyEvent e) -> {

            if (e.getCode() == KeyCode.DELETE && currentRect == ((TextField) e.getSource())) {
                container.getChildren().remove(currentRect);
                setCurrentRectangleToNull();
            }           
        });
        return rec;
    }
and the red dot are instance of Rectangle:



the code can be found on github :

https://github.com/mooninvader/imageCropperFx 

Tuesday, June 16, 2015

MINESWEEPER WITH JAVAFX

A simple minesweeper game made with netbeans 8 and javafx.





you can find the code at github. here the link :