JavaFX Event Handling සිංහලෙන් | User Interactions SC Guide

JavaFX Event Handling සිංහලෙන් | User Interactions SC Guide

ආයුබෝවන්, මගේ යාලුවනේ!

කොහොමද ඉතින්? අද අපි කතා කරන්න යන්නේ JavaFX ලෝකයේ තියෙන හරිම වැදගත් සහ රසවත් මාතෘකාවක් ගැන. ඒ තමයි Event Handling. ඔයා JavaFX වලින් GUI application එකක් හදනවා නම්, මේ ගැන හොඳටම දැනගෙන ඉන්න එක අනිවාර්යයි.

හිතන්න, ඔයා ලස්සන application එකක් හැදුවා කියලා. ඒකේ බොත්තම්, ටෙක්ස්ට් බොක්ස්, ලේබල් වගේ දේවල් ගොඩක් තියෙනවා. හැබැයි, ඒවා නිකන් තියෙනවා විතරක් නම් මොකක්ද වැඩේ? අපි බොත්තමක් ක්ලික් කරාම, මොකක් හරි වෙන්න ඕනේ නේද? ටෙක්ස්ට් බොක්ස් එකකට ටයිප් කරද්දි, ඒකේ තියෙන දේ අනුව වැඩක් වෙන්න ඕනේ. මේ හැමදේම කරන්නේ Event Handling කියන සංකල්පය පාවිච්චි කරලා තමයි.

මොකද, application එකක් කියන්නේ නිකන් ලස්සන පින්තූරයක් නෙවෙයි, ඒක පණ තියෙන දෙයක්! User එක්ක interaction කරන, ඒ අයගේ actions වලට ප්‍රතිචාර දක්වන දෙයක්. අන්න ඒ interaction එකට තමයි Event Handling කියන්නේ. ඉතං, අපි පටන් ගමුද?

Event Handling කියන්නේ මොකක්ද?

සරලවම කිව්වොත්, Event එකක් කියන්නේ application එකක් ඇතුළේ සිද්ධ වෙන සිදුවීමක්. මේක user කෙනෙක් කරන ක්‍රියාවක් වෙන්න පුළුවන් (බොත්තමක් ක්ලික් කරන එක, කීබෝඩ් එකෙන් ටයිප් කරන එක, මවුස් එක එහාට මෙහාට කරන එක). එහෙමත් නැත්නම් system එකක් ඇතුළේ වෙන දෙයක් වෙන්න පුළුවන් (ටයිමරයක් ඉවර වෙන එක, ඩේටා ලෝඩ් වෙන එක).

මේ වගේ Event එකක් සිද්ධ වුණාම, ඒක හඳුනාගෙන ඒකට ප්‍රතිචාර දක්වන එක තමයි Event Handling කියන්නේ. මේක හරියට මෙහෙමයි: කවුරු හරි ගේට්ටුව ළඟ සීනුව ගැහුවම, ගෙදර ඉන්න කෙනෙක් ඒ සද්දෙට ඇහුම්කන් දීලා ගිහින් බලනවා වගේ. මෙතනදී,

  • Event Source: සීනුව ගහපු කෙනා (බොත්තම, Text Field එක වගේ user interaction එකක් සිදුවෙන තැන).
  • Event: සීනුව නාද වීම (MouseEvent, KeyEvent, ActionEvent වගේ සිදුවීම).
  • Event Listener / Event Handler: සීනුව සද්දෙට ඇහුම්කන් දෙන කෙනා (මේවා තමයි Event එකක් ඇහුවම ඒකට ප්‍රතිචාර දක්වන කෝඩ් බ්ලොක්ස්).

JavaFX වලදී, මේ හැම සිදුවීමක්ම Event class එකේ subclass එකක් විදිහට නිරූපණය වෙනවා. උදාහරණයක් විදිහට, බොත්තමක් ක්ලික් කරන එක ActionEvent එකක්, කීබෝඩ් එකෙන් අකුරක් ඔබන එක KeyEvent එකක්, මවුස් එක ක්ලික් කරන එක MouseEvent එකක්. මේ වගේ හැම දෙයක්ම වෙන වෙනම හඳුනා ගන්න පුළුවන්.

JavaFX වල Event Handling වැඩ කරන්නේ කොහොමද?

JavaFX වල Event Handling කරන්න මූලික ක්‍රම දෙකක් තියෙනවා:

  1. Using setOnAction() or similar specific methods: මේක තමයි බොහොමයක් වෙලාවට අපි පාවිච්චි කරන්නේ. Button එකකට setOnAction() වගේ methods තියෙනවා. මේවා විශේෂිත Event වර්ගයක් සඳහා නිර්මාණය කරපු ඒවා. මේකට අපි EventHandler interface එක implement කරපු object එකක් දෙන්න පුළුවන්, නැත්නම් Lambda Expression එකක් පාවිච්චි කරන්න පුළුවන්.
  2. Using addEventHandler() and addEventFilter(): මේවා පොදු Methods. ඕනෑම Node එකකට Event Listener එකක් හෝ Filter එකක් එකතු කරන්න මේවා පාවිච්චි කරනවා. මේවා EventHandler interface එකේ instance එකක් සහ EventType එකක් take කරනවා.

මේ හැම Event එකක්ම JavaFX Scene Graph එක හරහා ගමන් කරනවා. මේක Event Dispatch Chain එකක් විදිහට හඳුන්වනවා. Event එකක් සිද්ධ වුණාම, ඒක Capture Phase එක හරහා Scene Graph එකේ මුල ඉඳන් Event Source එකට යනවා. ඊට පස්සේ, Bubbling Phase එක හරහා Event Source එකේ ඉඳන් Scene Graph එකේ මුලට යනවා. මේ ගැන අපි පස්සේ තව ටිකක් කතා කරමු.

වැදගත් දේ තමයි, මේ හැම Event එකක්ම javafx.event.Event package එකෙන් එනවා. අපිට අවශ්‍ය Event type එකට අදාළ subclass එක තෝරගෙන වැඩ කරන්න පුළුවන්.

Click Events හසුරුවමු (Let's Handle Click Events)

අපි සරලම උදාහරණයකින් පටන් ගමු - බොත්තමක් ක්ලික් කිරීම. JavaFX වල Button එකක් නිර්මාණය කරලා, ඒක ක්ලික් කරද්දි මොකක් හරි text එකක් print වෙන විදිහට හදමු.


import javafx.application.Application;
import javafx.event.ActionEvent; // ActionEvent import කරන්න
import javafx.event.EventHandler; // EventHandler import කරන්න
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class ButtonClickExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        // බොත්තමක් හදනවා
        Button button = new Button("මාව ඔබන්න!"); // Button text in Sinhala

        // බොත්තම ක්ලික් කරද්දි සිද්ධ විය යුතු දේ සකසනවා
        // මේක තමයි Event Handling කොටස
        button.setOnAction(new EventHandler<ActionEvent>() {
            @Override
            public void handle(ActionEvent event) {
                System.out.println("බොත්තම ක්ලික් කළා!"); // Console එකේ print කරනවා
            }
        });

        // සරල layout එකක් හදනවා
        StackPane root = new StackPane();
        root.getChildren().add(button);

        // Scene එක හදනවා
        Scene scene = new Scene(root, 300, 200);

        // Stage එකේ title එක set කරනවා
        primaryStage.setTitle("බොත්තම් ක්ලික් කිරීම"); // Stage title in Sinhala

        // Scene එක Stage එකට set කරනවා
        primaryStage.setScene(scene);

        // Stage එක display කරනවා
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

මේ කෝඩ් එකේ button.setOnAction(...) කියන කොටස තමයි Event Handling එකට අදාළ. මෙතනදී අපි EventHandler<ActionEvent> කියන interface එක implement කරනවා. ඒකේ තියෙන handle() method එක තමයි බොත්තම ක්ලික් කරද්දි run වෙන්නේ.

Lambda Expressions පාවිච්චි කරමු!

Java 8 වලින් පස්සේ, අපිට මේ වගේ Single Abstract Method (SAM) Interface එකක් වෙනුවට Lambda Expression එකක් පාවිච්චි කරන්න පුළුවන්. මේකෙන් කෝඩ් එක තවත් සරල වෙනවා. බලන්න මේ විදිහට:


// ... කලින් කෝඩ් එකේ button creation එකට පස්සේ ...

// Lambda Expression එකකින් Event Handling
button.setOnAction(event -> {
    System.out.println("බොත්තම ක්ලික් කළා! (Lambda)");
});

// ... ඉතිරි කෝඩ් එක ඒ විදිහටම ...

මේක EventHandler interface එකේ handle method එකට කෙටි ක්‍රමයක්. event කියන්නේ ActionEvent object එකට. මේක ඇතුලේ Event එකට අදාළ තොරතුරු අඩංගුයි. උදාහරණයක් විදිහට, event.getSource() මඟින් Event එක ඇති වූ Node එක ලබා ගන්න පුළුවන්.

Keyboard Events සහ Mouse Events

Click Events වගේම, Keyboard Events සහ Mouse Events කියන්නෙත් ඉතාම වැදගත් Event වර්ග දෙකක්.

Keyboard Events

යූසර් කෙනෙක් කීබෝඩ් එකෙන් අකුරක් ඔබද්දි හෝ අතහැරියාම (release කරද්දි) මේ Events සිද්ධ වෙනවා. මේවා KeyEvent classes වලින් නිරූපණය වෙනවා. ප්‍රධාන වශයෙන් වර්ග 3ක් තියෙනවා:

  • KeyEvent.KEY_PRESSED: යූසර් කී එකක් එබුවම.
  • KeyEvent.KEY_RELEASED: යූසර් කී එකක් අතහැරියාම.
  • KeyEvent.KEY_TYPED: යූසර් යුනිකෝඩ් චරිතයක් ටයිප් කරද්දි (මේක KEY_PRESSED සහ KEY_RELEASED දෙකේම සංයෝජනයක් වගේ).

උදාහරණයක් විදිහට, TextField එකක යූසර් Enter Key එක එබුවම මොකක් හරි වැඩක් කරන හැටි බලමු.


import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.TextField;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.scene.input.KeyCode; // KeyCode import කරන්න
import javafx.scene.input.KeyEvent; // KeyEvent import කරන්න

public class KeyEventExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        TextField textField = new TextField();
        textField.setPromptText("නමක් ටයිප් කර Enter ඔබන්න..."); // Prompt text in Sinhala

        // KeyEvent එක handle කරනවා
        textField.setOnKeyPressed(event -> {
            if (event.getCode() == KeyCode.ENTER) { // Enter key එකද කියලා බලනවා
                System.out.println("ඔබ ටයිප් කළ නම: " + textField.getText());
                textField.clear(); // TextField එක clear කරනවා
            }
        });

        VBox root = new VBox(10, textField); // Vertical Box layout
        root.setPadding(new javafx.geometry.Insets(20)); // Padding දෙනවා

        Scene scene = new Scene(root, 400, 150);
        primaryStage.setTitle("Keyboard Event උදාහරණය"); // Stage title in Sinhala
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

මෙතන textField.setOnKeyPressed() කියන method එක පාවිච්චි කරලා, KeyEvent එකක් detect කරනවා. event.getCode() මඟින් එබූ Key එකේ KeyCode එක ලබා ගන්න පුළුවන්. KeyCode.ENTER කියන්නේ Enter Key එකට අදාළ KeyCode එක.

Mouse Events

මවුස් එකත් එක්ක කරන හැම interaction එකක්ම Mouse Event එකක්. මේවා MouseEvent classes වලින් නිරූපණය වෙනවා. මේවායින් කිහිපයක්:

  • MouseEvent.MOUSE_CLICKED: මවුස් බොත්තමක් ක්ලික් කරලා release කරද්දි.
  • MouseEvent.MOUSE_PRESSED: මවුස් බොත්තමක් ඔබද්දි.
  • MouseEvent.MOUSE_RELEASED: මවුස් බොත්තමක් අතහැරියාම.
  • MouseEvent.MOUSE_MOVED: මවුස් එක එහාට මෙහාට කරද්දි.
  • MouseEvent.MOUSE_DRAGGED: මවුස් බොත්තමක් ඔබාගෙන එහාට මෙහාට කරද්දි.
  • MouseEvent.MOUSE_ENTERED: මවුස් කර්සරය Node එකක් උඩට ආවම.
  • MouseEvent.MOUSE_EXITED: මවුස් කර්සරය Node එකකින් එළියට ගියාම.

උදාහරණයක් විදිහට, Pane එකක ඕනෑම තැනක ක්ලික් කරද්දි ඒකේ X සහ Y coordinates print වෙන විදිහට හදමු.


import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.layout.Pane;
import javafx.stage.Stage;
import javafx.scene.input.MouseEvent; // MouseEvent import කරන්න

public class MouseEventExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        Pane pane = new Pane();
        pane.setPrefSize(400, 300); // Pane එකට ප්‍රමාණයක් දෙනවා

        // Mouse Click Event එක handle කරනවා
        pane.setOnMouseClicked(event -> {
            System.out.println("මවුස් ක්ලික් කළ ස්ථානය: X=" + event.getX() + ", Y=" + event.getY());
        });

        Scene scene = new Scene(pane);
        primaryStage.setTitle("Mouse Event උදාහරණය"); // Stage title in Sinhala
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

event.getX() සහ event.getY() මඟින් මවුස් ක්ලික් කළ ස්ථානයේ X සහ Y coordinates ලබා ගන්න පුළුවන්. මේවා Node එකට සාපේක්ෂව (relative to the Node) තමයි දැක්වෙන්නේ.

Event Filtering vs. Event Bubbling

මේක ටිකක් advanced concept එකක් වුණත්, JavaFX Event Handling හරියට තේරුම් ගන්න මේක වැදගත්. කලින් කිව්වා වගේ, Event එකක් සිද්ධ වුණාම, ඒක Scene Graph එක හරහා ගමන් කරනවා. මේ ගමනට අදියර දෙකක් තියෙනවා:

  1. Capture Phase (Filtering): Event එක Scene Graph එකේ root එකේ ඉඳන්, Event එක ඇති වුණ Node එක (Event Target) දක්වා පහළට ගමන් කරනවා. මේ අදියරේදී Event එකට අහු වෙන්න ඕනේ නම්, අපි addEventFilter() method එක පාවිච්චි කරන්න ඕනේ. මේකෙන් පුළුවන් Event එක Event Target එකට ළඟා වීමට පෙරම intercept කරලා, අවශ්‍ය නම් Event එක නවත්තලා (consume කරලා) දාන්න.
  2. Bubbling Phase (Handling): Event එක Event Target එකේ ඉඳන්, Scene Graph එකේ root එක දක්වා උඩට ගමන් කරනවා. සාමාන්‍යයෙන් අපි setOnAction() හෝ addEventHandler() වගේ methods පාවිච්චි කරලා Events handle කරන්නේ මේ Bubbling Phase එකේදී.

සරලවම කිව්වොත්,

  • addEventFilter(): Event එක target එකට ළඟා වෙන්න කලින් අල්ලගන්න (Capture Phase). මේකෙන් Event එක consume කරන්න පුළුවන්, එතකොට ඒක Bubbling Phase එකට යන්නේ නෑ.
  • addEventHandler(): Event එක target එකට ළඟා වෙලා, ආයෙත් උඩට යන ගමන් අල්ලගන්න (Bubbling Phase). මේක setOnAction() වගේමයි, හැබැයි ඕනෑම Event type එකකට පොදුවේ පාවිච්චි කරන්න පුළුවන්.

හිතන්න, ඔයාට පුතෙක් ඉන්නවා. එයා කෑම කන්න කැමති නෑ. ඒත් තාත්තා පුතාට කෑම දෙන්න යනවා. අම්මා දොර ළඟ ඉඳන්ම තාත්තාව නවත්තලා, පුතාට කෑම දෙන්න කලින් "එයා දැන් කෑවා" කියලා කියනවා නම්, ඒක addEventFilter() වගේ වැඩක්. කෑම පුතාට ළඟා වෙන්න කලින්ම වැඩේ consume කරනවා. හැබැයි, තාත්තා කෑම දුන්නට පස්සේ පුතා "මම කෑවා" කියලා කියනවා නම්, ඒක addEventHandler() වගේ වැඩක්. සිද්ධිය වුණාට පස්සේ අහනවා.

මේක තවදුරටත් උදාහරණයකින් පැහැදිලි කරමු:


import javafx.application.Application;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.scene.input.MouseEvent;

public class EventFilterExample extends Application {

    @Override
    public void start(Stage primaryStage) {
        Button button = new Button("Click Me!");
        VBox root = new VBox(10, button); // root node

        // Parent (root) node එකට filter එකක් එකතු කරනවා
        // මේක capture phase එකේදී ක්‍රියාත්මක වෙනවා
        root.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
            System.out.println("Filter: Root Node එකෙන් Mouse Click Event එකක් අල්ලගත්තා!");
            // event.consume(); // මේක දැම්මොත් Event එක Bubbling Phase එකට යන්නේ නෑ
        });

        // Parent (root) node එකට handler එකක් එකතු කරනවා
        // මේක bubbling phase එකේදී ක්‍රියාත්මක වෙනවා
        root.addEventHandler(MouseEvent.MOUSE_CLICKED, event -> {
            System.out.println("Handler: Root Node එකෙන් Mouse Click Event එකක් අල්ලගත්තා!");
        });

        // Button (child) node එකට handler එකක් එකතු කරනවා
        // මේක Event target එක
        button.setOnAction(event -> {
            System.out.println("Action: Button එක Click කළා!");
        });

        Scene scene = new Scene(root, 300, 200);
        primaryStage.setTitle("Event Filter vs Handler");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}

මේ කෝඩ් එක run කරලා button එක Click කරලා බලන්න. Output එක මේ වගේ වෙයි (consume නොකළොත්):


Filter: Root Node එකෙන් Mouse Click Event එකක් අල්ලගත්තා!
Action: Button එක Click කළා!
Handler: Root Node එකෙන් Mouse Click Event එකක් අල්ලගත්තා!

ඔයා event.consume() line එක filter එක ඇතුළේ uncomment කරොත්, Bubbling phase එකේ තියෙන Handlers run වෙන්නේ නෑ. මොකද Event එක Filter එකෙන්ම consume වෙනවා. ඒක ඉතාම වැදගත් සංකල්පයක්.

අවසාන වශයෙන්

ඉතින් යාලුවනේ, JavaFX වල Event Handling ගැන මූලික දැනුමක් අද අපි ගත්තා. බොත්තම් ක්ලික් කිරීම්, කීබෝඩ් සහ මවුස් events වගේ දේවල් හසුරුවන හැටි සහ Event Dispatch Chain එකේ Capture සහ Bubbling phases ගැනත් අපි කතා කළා.

මේක JavaFX application එකක් පණ ගන්වන්න අත්‍යවශ්‍යම දෙයක්. මේ concepts හරියට තේරුම් අරන් ප්‍රැක්ටිස් කරන එකෙන් ඔයාට තව තවත් දියුණු වෙන්න පුළුවන්. අනිවාර්යයෙන්ම මේ කෝඩ් උදාහරණ ටික ඔයාගේ පරිගණකයේ run කරලා බලන්න. එතකොට තව හොඳින් තේරුම් ගන්න පුළුවන්.

ඔබට මොනවා හරි ප්‍රශ්න තියෙනවා නම්, comment section එකේ අහන්න! පුළුවන් හැම වෙලාවෙම උදව් කරන්න මම සූදානම්. ඊළඟ ලිපියකින් හමුවෙමු, හැමෝටම ජය!