December 18, 2013

Day 11 Bind (Part 2)

ပြီးခဲ့တဲ့ ဘလောဂ်မှာတုန်းက Bind Function ကို သုံးပြီးတော့ Property တွေကို Bind ပုံတွေကို လေ့လာခဲ့ပါတယ်။ Bind Function ဟာ Property တွေကို Synchronize လုပ်ရုံတင်မကပါဘူး။ Property တွေကို တွက်ချက်ပြီး ရလာတဲ့ရလဒ်ကို အခြားသော Property တစ်ခုနဲ့လည်း Bind ထားနိုင်ပါတယ်။ ဒီတစ်ခေါက်ကတော့ Property တွေကို တွက်ချက်ပြီး Bind တဲ့ ပုံကို ဖော်ပြသွားပါမယ်။

High-Level Binding API


Java FX Application တွေမှာ Binding Function တွေကို အလွယ်တကူ အသုံးပြုနိုင်ရန် စီစဉ်ထားတာကတော့ High-Level Binding API ပဲ ဖြစ်ပါတယ်။ High-Level Binding API ထဲမှာ Fluent API နဲ့ Binding Class ဆိုပြီး နှစ်ပိုင်း ပါဝင်ပါတယ်။ နှစ်မျိုးစလုံးဟာ Property တွေကို Bind လုပ်တဲ့နေရာမှာ အသုံးဝင်ပါတယ်။


Fluent API


ပုံမှန်အားဖြင့် Binding ကို Property Variable နှစ်ခုကို Bind လုပ်တဲ့အခါမှာ အသုံးပြုပါတယ်။ Fluent API ကို အသုံးပြုတဲ့အခါမှာ Variable နှစ်ခုကို ပေါင်းပြီး အခြားသော Variable မှာ Bind လုပ်နိုင်ပါတယ်။
public class FluentSample {

    public static void main(String[] args) {
        IntegerProperty var_1 = new SimpleIntegerProperty(0);
        IntegerProperty var_2 = new SimpleIntegerProperty(0);
        
        NumberBinding sum = var_1.add(var_2);
        
        var_1.set(10);
        var_2.set(5);
        
        System.out.println(sum.getValue()); // value is 15
        
        var_1.set(13);
        
        System.out.println(sum.getValue()); // value is 18
    }
}
အထက်က ကုဒ်တွေထဲမှာ IntegerPrpoerty ဖြစ်တဲ့ var_1 နဲ့ var_2 ကို ပေါင်းပြီး Binding လုပ်ပါတယ်။ ရလာတဲ့ ရလဒ်ကတော့ NumberBind ဖြစ်တဲ့ sum ပါပဲ။ အဲ့ဒီအတွက် var_1 နဲ့ var_2 ကို တန်ဖိုးပြောင်းတဲ့အခါမှာ sum ဟာလည်း အလိုအလျှောက် လိုက်ပြောင်းပေးနိုင်ခြင်း ဖြစ်ပါတယ်။


Bindings Class


အထက်မှာ Fluent API ကို သုံးပြီး Binding ကို ဖော်ပြခဲ့ပါတယ်။ အဲ့ဒီလို Binding မျိုးကို Binding Class ကို အသုံးပြုပြီးလည်း ရေးသားနိုင်ပါတယ်။
public class Average {

    public static void main(String[] args) {
        DoubleProperty var_1 = new SimpleDoubleProperty();
        DoubleProperty var_2 = new SimpleDoubleProperty();
        
        NumberBinding average = Bindings.add(var_1, var_2).divide(2);
        
        var_1.set(45);
        var_2.set(10);
        
        System.out.println(average.doubleValue());
    }
}
အထက်ပါ နမှုနာထဲမှာတော့ DoubleProperty Variable နှစ်ခုကို Bindings Class ကို သုံးပြီး ပေါင်းကာ၊ ပြန်ပြီး 2 နဲ့ စားထားပါတယ်။ အဲ့ဒီအတွက် var_1 နဲ့ var_2 ရဲ့ တန်ဖိုးကိုပြောင်းပြီးတဲ့အခါမှာ average ရဲ့ တန်ဖိုးဟာလည်း အလိုလို ပြောင်းသွားတာကို တွေ့ရပါတယ်။ အဲ့ဒီအတွက် Fluent API ကိုပဲသုံးသုံး၊ Bindings Class ကိုပဲသုံးသုံး Property တွေရဲ့ ပေါင်းနှုတ်မြှောက်စားကို အလွယ်တကူ ရေးသားနိုင်မှာဖြစ်ပါတယ်။


Both Fluent And Bindings with UI Components


ကျွှန်တော်တို့ ကျောင်းသားတစ်ယောက်ရဲ့ အမှတ်စာရင်းသွင်းတာကို Binding Function ကို သုံးပြီး ရေးသားကြည့်ပါမယ်။ အမှတ်စာရင်းတွေကို ပြောင်းလိုက်တာနဲ့ အမှတ်ပေါင်းရယ်၊ ပျမ်းမျှအမှတ်ရယ်၊ အဲ့ဒီအမှတ်တွေရဲ့ Chart ဟာ အလိုလိုပြောင်းသွားတဲ့ အပလီကို ရေးကြည့်ပါမယ်။
public class StudentMark extends Application implements Initializable {

    @FXML
    private ComboBox<Integer> myanmar;
    @FXML
    private ComboBox<Integer> english;
    @FXML
    private ComboBox<Integer> maths;
    @FXML
    private ComboBox<Integer> physis;
    @FXML
    private ComboBox<Integer> chemistory;
    @FXML
    private ComboBox<Integer> biology;
    @FXML
    private Label total;
    @FXML
    private Label average;
    @FXML
    private BarChart<String, Integer> chart;

ဦးစွာ အသုံးပြုမယ့် UI Component တွေကို Declare လုပ်ပါတယ်။ FXML မှာ ရေးထားတဲ့ Component တွေထဲက Dynamically အသုံးပြုချင်တဲ့ Component တွေကို Controller ထဲမှာ ရေးသားပါတယ်။
    private void initCombo(ComboBox<Integer> comb) {
        List<Integer> list = new ArrayList<>();
        for (int i = 0; i <= 100; i++) {
            list.add(i);
        }

        comb.getItems().clear();
        comb.getItems().addAll(list);
        comb.getSelectionModel().select(40);
    }

ပြီးတော့ ComboBox တွေကို Data ဖြည့်ဖို့ method ပါပဲ။ ComboBox ၆ ခုလုံး အတူတူ အသုံးပြုမှာဖြစ်တဲ့အတွက် method တစ်ခုကို ရေးပြီး အသုံးပြုတာဖြစ်ပါတယ်။
    private Series<String, Integer> getSeries(String name,
            ComboBox<Integer> comb) {
        XYChart.Series<String, Integer> series = new XYChart.Series<>();
        series.setName(name);
        Data<String, Integer> data = new Data<String, Integer>("", 0);
        data.YValueProperty().bind(comb.valueProperty());
        series.getData().add(data);
        return series;
    }

နောက် method တစ်ခုကတော့ BarChat ရဲ့ Data အဖြစ်အသုံးပြုမယ့် Data တွေကို ယူတဲ့ Method ပါပဲ။ သူ့ဆီမှာတော့ Argument အနေနဲ့ Data အမည် အဖြစ်အသုံးပြုမယ့် String variable တစ်ခုရယ်၊ Binding လုပ်ဖို့အတွက် ComboBox တစ်ခုပါပဲ။

BarChart မှာ အသုံးပြုတာကတော့ XYChart.Series Object ပါပဲ။ အဲ့ဒီထဲမှာ XYChart.Data Object ကို တစ်ခုထက်မက ထည့်သွင်း အသုံးပြုနိုင်ပါတယ်။ ဒီနမှုနာမှာကတော့ တစ်ခုတည်းကိုသာ အသုံးပြုထားပါတယ်။

ဒီထဲမှာ ထူးခြားတာကတော့ XYChart.Data ရဲ့ valueProperty ကို ComboBox ရဲ့ valueProperty နဲ့ Bind လုပ်ထားတာပါပဲ။ အဲ့ဒီလို လုပ်ထားတဲ့အတွက် ComboBox ကို ပြောင်းလိုက်တာနဲ့ BarChart ရဲ့ Data လည်း လိုက်ပြောင်းမှာ ဖြစ်ပါတယ်။
    @SuppressWarnings("unchecked")
    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        // set data to combo
        this.initCombo(myanmar);
        this.initCombo(english);
        this.initCombo(maths);
        this.initCombo(physis);
        this.initCombo(chemistory);
        this.initCombo(biology);

        // set series to chart
        chart.getData().addAll(this.getSeries("Myanmar", this.myanmar),
                this.getSeries("English", this.english),
                this.getSeries("Maths", this.maths),
                this.getSeries("Physics", this.physis),
                this.getSeries("Chemistory", this.chemistory),
                this.getSeries("Biology", this.biology));

        // binding to total
        NumberBinding totalBind = getProperty(myanmar).add(getProperty(english))
                .add(getProperty(maths)).add(getProperty(physis))
                .add(getProperty(chemistory)).add(getProperty(biology));
        
        totalBind.addListener(new ChangeListener<Number>() {

            @Override
            public void changed(ObservableValue<? extends Number> arg0, Number oldItem,
                    Number newItem) {
                total.setText(String.valueOf(newItem));
            }
        });
        
        total.setText(String.valueOf(totalBind.getValue()));

        // binding to average
        NumberBinding averageBind = Bindings.divide(totalBind, 6);
        averageBind.addListener(new ChangeListener<Number>() {

            @Override
            public void changed(ObservableValue<? extends Number> arg0,
                    Number arg1, Number arg2) {
                average.setText(String.valueOf(arg2.doubleValue()));
            }
        });
        
        average.setText(String.valueOf(averageBind.doubleValue()));
    }

အထက်က ရေးသားထားတဲ့ Method တွေကို အသုံးပြုမှာကတော့ initialize method ပါပဲ။ ပထမဦးဆုံး ComboBox တွေကို Data တွေကို ဖြည့်ပါတယ်။ အဲ့ဒီနောက်မှာတော့ BarChart ရဲ့ Data တွေကို အထက်မှာရေသားထားတဲ့ getSeries ကို ခေါ်ပြီး အသီးသီး ဖြည့်စွက်ပါတယ်။ getSeries ထဲမှာ ComboBox ရဲ့ တန်ဖိုးနဲ့ Series ရဲ့ တန်ဖိုးကို Bind ထားတဲ့အတွက် ComboBox ကို ပြောင်းတာနဲ့ BarChart ရဲ့ တန်ဖိုးလည်း လိုက်ပြောင်းမှာ ဖြစ်ပါတယ်။

ဆက်ပြီးရေမှာကတော့ Total အတွက် တန်ဖိုးဖြစ်ပါတယ်။ getProperty method ကိုခေါ်ပြီး ရလာတဲ့ DoubleProperty တွေကို အသီးသီး ပေါင်းခိုင်းပြီး၊ NumberBinding ကို ရယူပါတယ်။ အဲ့ဒီ variable မှာ ChangeListner ကို ဖြည့်ပြီး၊ အဲ့ဒီထဲက change method မှာတော့ total Lable ရဲ့ text အနေနဲ့ တန်ဖိုးအသစ်ကို သွားဖြည့်ခိုင်းပါတယ်။ ဒီလိုရေးထားတာနဲ့ ComboBox ရဲ့ တန်ဖိုးတစ်ခုခု ပြောင်းတိုင်း အဲ့ဒီ Variable ရဲ့ တန်ဖိုးလည်း ပြောင်းလည်းမှာဖြစ်ပြီး၊ အဲ့ဒီတန်ဖိုး ပြောင်းတိုင်းလည်း total မှာ သွားပြီး ဖော်ပြပေးမှာဖြစ်ပါတယ်။ တကယ်လို့များ change ထဲမှာ Database ထဲသွားပြီး save လုပ်တဲ့ လော့ဂျစ်ကို ရေးထားရင် Database ထဲကို သွားပြီး သိမ်းပေးမှာဖြစ်ပါတယ်။

နောက်ဆုံးရေးထားတာကတော့ Average ရှာတဲ့ လော့ဂျစ်ပါ။ NumberBinding variable တစ်ခုကို Bindings ကို သုံးပြီး၊ အထက်က Total ကို 6 နဲ့ စားပါတယ်။ ပြီးတော့ အထက်မှာလိုပဲ ChangeListener တစ်ခုကို ရေးသားပါတယ်။ ဒီလိုရေးတာနဲ့ Total တန်ဖိုးပြောင်းတိုင်း Average ကို ပြန်တွက်ပြီး ဖော်ပြပေးပါလိမ့်မယ်။


ကွန်ဘိုမှာရှိတဲ့ တန်ဖိုးတွေကို လိုက်ပြောင်းကြည့်တဲ့အခါ အောက်ပါအတိုင်း လိုက်ပြီး ပြောင်းလည်းတာကို တွေ့ရပါတယ်။


Swing နဲ့ ဒီလို အပလီမျိုးကို ရေးမယ်ဆိုရင် Source Code တွေ အများကြီး ဖောင်းပွကုန်ပါလိမ့်မယ်။ အဲ့ဒီအပြင် Event တွေကို လိုက်ဖမ်းပြီး ရေးနေရပါမယ်။ ကွန်ဘိုတွေကိုပြောင်း နောက်ဆုံးမှ ခလုပ်နှိပ် အဲ့ဒီလို ရေးဖြစ်မယ် ထင်ပါတယ်။ JavaFX ရဲ့ Bind ကို သုံးထားတဲ့အတွက် Combo ကို တစ်ခါပြောင်းလိုက်တိုင်း Application မှာလည်း ချက်ချင်း လိုက်ပြီး ပြောင်းပေးတာကို တွေ့ရပါတယ်။

ဒီကနေ့တော့ High-Level Binding API အကြောင်းကို ဖော်ပြခဲ့ပါတယ်။ အလွယ်တကူသုံးလိုတဲ့ အခါမှာ အဆင်ပြေပါတယ်။ ဒီပေမယ့် ပိုမိုရှုပ်ထွေးတဲ့ Business Logic တွေကို ရေးချင်ရင်တော့ Binding API ကို Customize လုပ်ဖို့လိုအပ်လာပါလိမ့်မယ်။ အဲ့ဒီလို Customize လုပ်ဖို့အတွက်လည်း Low-Level Binding API ကို ပြင်ဆင်ထားပါတယ်။ နောက်တစ်ခေါက်မှပဲ Low-Level Binding API အကြောင်းကို မိတ်ဆက်ပါတော့မယ်။

ဆက်ပါဦးမည်
မင်းလွင်

No comments:

Post a Comment