Showing posts with label data binding. Show all posts
Showing posts with label data binding. Show all posts

Tuesday, August 18, 2015

Tutorial Data Binding di JavaFX

Dalam tutorial kali ini saya akan memberikan  contoh bagaimana menggunakan konsep data binding di JavaFX. Sebenarnya ini terinspirasi dari sebuah kursus yang saya ikuti kemarin mengenai penggunaaan WPF (Windows Presentation Foundation) yang konsepnya juga hampir mirip dengan JavaFX.

Defenisi data binding sendiri adalah bagaimana perubahan yang dialami oleh sebuah element UI (misalnya button) langsung direfleksikan pada elemen UI yang lain (misalnya panel atau window). Jadi ketika kita merubah lebar button, maka lebar panel secara serta merta ikutan berubah. Dalam konsep  UI jadul, misalnya yang dijumpai pada Jawa Swing, hal ini amat rumit untuk diaplikasikan. Bisa sih, namun kode yang kita buat agak kotor. Kita mungkin bisa mendeteksi event click mouse yang dilakukan pada sebuah button atau event mouse drag. Namun bagaimana kalau sudah hal-hal semisal perubahan property lebar atau tinggi button tersebut, tentu cukup sulit untuk mendeteksinya. Salah satu cara kotor untuk menempuhnya antara lain dengan membuat sebuah thread yang secara periodik mengecek perubahan pada ukuran button tersebut untuk kemudian merefleksikannya pada panel.

Pada WPF, teknik data binding biasanya dilakukan secara langsung pada XAML. Namun kemudian saya menyadari bahwa cara ini kurang tepat. Hal ini karena  antara property-property yang di-binding biasanya tidak proporsional nilainya. Misalnya jika kita mem-binding lebar button dengan lebar panel (atau window), tentu itu kurang tepat karena secara umum lebar button jauh lebih kecil dari lebar panel. Jadi kita harus melakukan operasi aritmatika di situ. Misalnya dengan mengeset lebar button setengah atau seperempat dari lebar window.

Masalahnya adalah kita tidak bisa menyertakan operasi aritmatika ke dalam file XAML (atau sebut saja XML). Jadi mau tidak mau kita harus letakkan pengesetan tersebut pada code behind-nya. Dan ini sudah diantisipasi oleh JavaFX.

Jadi dalam JavaFX tersebut sebuah UI element (misalnya window) properti propertynya sudah mengantisipasi adanya event yang terjadi. Dalam Java Swing, sebuah window (atau dalam terminologinya disebut sebagai JFrame) hanya memiliki width atau height saja. Dalam JavaFX, sebuah window (atau dalam terminologinya disebut sebagai Stage) width dan height nya itu sudah berubah menjadi widthProperty dan heightProperty yang bisa diberikan listener jika terjadi sebuah event. Perhatikan video berikut.



Dalam video di atas, jelas bahwa lebar button tidak mungkin di binding secara langsung dengan lebar window, akan tetapi harus dikalikan sebuah rasio tertentu.

Video berikut ini sebuah contoh bagaimana di JavaFX terdapat objek khusus yakni objek text yang tidak dijumpai pada WPF.

package fjr.test.binding;

import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;

public class TestBindingButton extends Application{

 Stage prStage; 
 
 Button testButton; 
 
 @Override
 public void start( Stage primaryStage) throws Exception {
  
  AnchorPane pane = new AnchorPane();
  
  Scene sc  = new Scene(pane, 500, 400) ; 
  
  primaryStage.setScene(sc); 
  
  primaryStage.show();
  
  this.prStage = primaryStage; 
  
  getChildren(pane);
  
  primaryStage.widthProperty().addListener(new InvalidationListener() {
   
   @Override
   public void invalidated(Observable arg0) {
    testButton.setPrefWidth(prStage.getWidth()/3.0) ;
    testButton.setPrefHeight(prStage.getHeight()/10.0);
   }
  });
   
 }
 
 
 private void getChildren(Pane p){
  
  VBox box1 = new VBox(); 
  box1.setSpacing(5);
  box1.setTranslateX(10);
  box1.setTranslateY(10);
  
  
  final Button b1 = new Button("COBA"); 
  b1.setPrefWidth(prStage.getWidth()/3.0) ;
  b1.setPrefHeight(prStage.getHeight()/10.0);  
  box1.getChildren().add(b1); 
  
  
  final Button b2 = new Button("ACUAN"); 
  b2.setPrefWidth(prStage.getWidth()/3.0) ;
  b2.setPrefHeight(prStage.getHeight()/10.0);  
  box1.getChildren().add(b2); 
  
  this.testButton = b1; 
  
  p.getChildren().addAll(box1); 
 }
 
 public static void main(String[] args){
  launch(args);
 }
}
package fjr.test.binding;

//import java.awt.Button;
import java.util.Random;

import javafx.animation.Interpolator;
import javafx.animation.KeyFrame;
import javafx.animation.KeyValue;
import javafx.animation.Timeline;
import javafx.application.Application;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Orientation;
import javafx.geometry.VPos;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.input.KeyEvent;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.AnchorPane;
import javafx.scene.layout.Pane;
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.text.Font;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.util.Duration;


public class TestBindingFont extends Application{
 
    private static final Random RANDOM = new Random();
    private static final Interpolator INTERPOLATOR = Interpolator.SPLINE(0.295,0.800,0.305,1.000);
    Pane p; 
    
    Font font = new Font (Font.getDefault().getFamily(), 60 ) ; 

 @Override
 public void start(Stage primaryStage) throws Exception {
  // TODO Auto-generated method stub
  
  final AnchorPane pane = new AnchorPane(); 
  Scene sc = new Scene(pane, 500,500 ); 
  
  primaryStage.setScene(sc);
  primaryStage.show(); 
  
  pane.setFocusTraversable(true); 
  pane.setOnMousePressed(new EventHandler() {
   @Override
   public void handle(MouseEvent me) {
    pane.requestFocus();
    me.consume();
   }
  });
  
  pane.setOnKeyPressed(new EventHandler() {
   @Override
   public void handle(KeyEvent ke) {
    createLetter(ke.getText()); 
    ke.consume();
   }
  });
  
  p = pane; 
  
  final Slider s = new Slider(); 
  s.setTranslateX(10); 
  s.setTranslateY(10); 
  s.setPrefWidth(150); 
  s.setOrientation(Orientation.HORIZONTAL);
  s.setMin(1); 
  s.setMax(10); 
  s.valueProperty().addListener(new InvalidationListener() {   
   @Override
   public void invalidated(Observable arg0) {
    double m = s.getValue(); 
    Font f2  = new Font(Font.getDefault().getFamily(), 60 * m); 
    font = f2; 
    for( Node  t : p.getChildren()){
     if(t instanceof Text){
      Text tt = (Text) t; 
      tt.setFont(f2);
     }
     
    }
   }
  });
  
  VBox box = new VBox(); 
  box.setSpacing(5); 
  
  pane.getChildren().add(box);
  box.getChildren().add(s); 
  
  Text t  = new Text("Ketikan sesuatu di Keyboard!!"); 
  t.setTranslateX(10);
  t.setTranslateY(20); 
  Font f = Font.font(Font.getDefault().getFamily(), FontWeight.BOLD , FontPosture.ITALIC , 20); 
  
  t.setFont(f); 
  
  box.getChildren().add(t);  
  
  t.setFill( new LinearGradient(0f,1f,1f,0f,true, CycleMethod.NO_CYCLE, 
      new Stop(0,Color.web("#f8bd55")),
                        new Stop(0.14f,Color.web("#c0fe56")),
                        new Stop(0.28f,Color.web("#5dfbc1")),
                        new Stop(0.43f,Color.web("#64c2f8")),
                        new Stop(0.57f,Color.web("#be4af7")),
                        new Stop(0.71f,Color.web("#ed5fc2")),
                        new Stop(0.85f,Color.web("#ef504c")),
                        new Stop(1,Color.web("#f2660f"))));

 }
 
  private void createLetter(String c) {

         final Text letter = new Text(c);
         letter.setFill(Color.BLACK);
         letter.setFont(font);
         letter.setTextOrigin(VPos.TOP);
         letter.setTranslateX((p.getWidth() - letter.getBoundsInLocal().getWidth()) / 2);
         letter.setTranslateY((p.getHeight() - letter.getBoundsInLocal().getHeight()) / 2);

         p.getChildren().add(letter);

         final Timeline timeline = new Timeline();
         timeline.getKeyFrames().add(
                 new KeyFrame(Duration.seconds(6), new EventHandler() {
                     @Override public void handle(ActionEvent event) {
                         p.getChildren().remove(letter);
                     }
                 },
                 new KeyValue(letter.translateXProperty(), getRandom(0.0f, p.getWidth() - 
                   letter.getBoundsInLocal().getWidth()),INTERPOLATOR),
                 new KeyValue(letter.translateYProperty(), getRandom(0.0f, p.getHeight() - 
                   letter.getBoundsInLocal().getHeight()),INTERPOLATOR),
                 new KeyValue(letter.opacityProperty(), 0f)
         ));
         timeline.play();
     }
 
  private static float getRandom(double min, double max) {
         return (float)(RANDOM.nextFloat() * (max - min) + min);
     }
  
 public static void main(String[] args){
  launch(args);
 }

}