Showing posts with label JavaFX Days. Show all posts
Showing posts with label JavaFX Days. Show all posts

April 20, 2015

Day 17 Transition (Part 2)

ပြီးခဲ့တဲ့ ဘလော့ဖြင့်  Java FX ကို သုံးပြီး Transition Animation ကို ရေးသားပုံ အကြောင်းကို ဖော်ပြခဲ့ ပါတယ်။ နမူနာ အနေနဲ့လဲ FadeTransition, RotateTransition, ScaleTransition, SequentialTransition နဲ့ ParallelTransition တို့ကို ရေးသားပြသခဲ့ပါတယ်။

ဒီကနေ့လည်း ဆက်လက်ပြီး Transition ရဲ့ နမူနာတစ်ချို့ကို ရေးသားပြသသွားပါမယ်။ ဒီတစ်ခေါက်တော့ Object တစ်ခုကို တစ်နေရာမှ တစ်နေရာကို ပြောင်းရွှေ့ကြည့်ပါမယ်။ Java FX မှာ Transition ကို သုံးပြီး တစ်နေရာက တစ်နေရာကို ပြောင်းရွှေ့လိုလျင် TranslateTransition နဲ့ PathTransition တို့ကို အသုံးပြုနိုင်မှာ ဖြစ်တယ်။ TranslateTransition ကတော့ Scene ပေါ်က x, y, z တို့ကို သတ်မှတ်ပြီး ပြောင်ရွှေ့လိုတဲ့ နေရာကို ပြောင်းရွှေ့ပေးနိုင်မှာ ဖြစ်ပါတယ်။ PathTransition ကတော့ Path လမ်းကြောင်းတစ်ခုကို သတ်မှတ်ပြီးတော့ အဲ့ဒီ Path လမ်းကြောင်းပေါ်မှာ ရွေ့လျားစေနိုင်မှာ ဖြစ်ပါတယ်။


နမူနာအနေနဲ့ Object တစ်ခုကို ကလစ်နှိပ်လိုက်လျင် ရွေ့လျားစေမဲ့ Animation တစ်ခုကို ရေးသားကြည့်ပါမယ်။


Using TranslateTransition


    private void setStage(Stage stage) {
        
        // to right
        TranslateTransition right = new TranslateTransition
                (Duration.millis(1000), obj);
        right.toXProperty().bind(stage.widthProperty().add(-130));
        
        // to down
        TranslateTransition down = new TranslateTransition
                (Duration.millis(1000), obj);
        down.toYProperty().bind(stage.heightProperty().add(-120));
        
        // to left
        TranslateTransition left = new TranslateTransition
                (Duration.millis(1000), obj);
        left.toXProperty().set(0);
        
        // to top
        TranslateTransition top = new TranslateTransition
                (Duration.millis(1000), obj);
        top.toYProperty().set(0);
        
        stop.setOnAction(a -> stop.getScene().getWindow().hide());
        
        SequentialTransition trans = new SequentialTransition(right, rotate(), 
                down, rotate(), left, rotate(), top, rotate());
        trans.setCycleCount(Timeline.INDEFINITE);
        
        obj.setOnMouseClicked(a -> {
            if(trans.getStatus().equals(Status.RUNNING)) {
                trans.pause();
            } else {
                trans.play();
            }
        });
    }
အထက်ပါနမူနာထဲမှာ လက်ရှိနေရာကနေ ညာဘက်ကို ရွှေ့လိုတဲ့အခါ right ၊ အောက်ကို ရွှေ့ချင်တဲ့အခါ down ၊ ဘယ်ဘက်ကို ရွှေ့လိုတဲ့အခါ left နှင့် အပေါ်ကိုရွှေ့လိုတဲအခါ top object တို့ကို အသုံးပြုမှာ ဖြစ်ပါတယ်။ အဆိုပါ Object တွေအားလုံးဟာ TranslateTransition Object တွေဖြစ်ကြပါတယ်။

TransitionTransition ရဲ့ Object တွေကို Instantiate လုပ်တဲ့နေရာမှာ Argument နှစ်ခုပါတဲ့ Constructor ကို အသုံးပြုထားပါတယ်။ ပထမ Argument ကတော့ Animation Cycle တစ်ခုကို လုပ်ဆောင်တဲ့ အခါမှာကြာမယ့် Duration Object ကို အသုံးပြုရပြီး၊ ဒုတိယ Argument အနေနဲ့ Animation ကို လုပ်ဆောင်စေမယ့် Node Object ကို အသုံးပြုရပါမယ်။ ပြီးတဲ့ အခါမှာ Transition Object အသီးသီးရဲ့ ရွှေ့လျားစေလိုတဲ toX နဲ့ toY ရဲ့ တန်ဖိုးအသီးသီးကို သတ်မှတ်ပေးလိုက်ရုံပဲဖြစ်ပါတယ်။

နမူနာထဲမှာ left object ရဲ့ toXPrpperty ကို stage object ရဲ့ widhtProperty ကို ၁၃၀ နှတ်ပြီး bind လုပ်ထားပါတယ်။ ဒီလို Binding ကို အသုံးပြုခြင်းအားဖြင့် Stage ရဲ့ Size ကို ပြောင်းပြီးတဲ့ နောက်မှာလည်း Dynamically ရွေ့လျားမှု့ကို ပြောင်းပေးနိုင်မှာ ဖြစ်ပါတယ်။

လက်တွေ့ရေးသားပုံ အဆင့်ဆင့်ကို အောက်ပါ ဗွီဒီယိုဖိုင်လ် မှာ လေ့လာနိုင်ပါတယ်။





Using PathTransition


PathTransition ကို အသုံးပြုတဲ့နေရာမှာ Path Object ဟာ အဓိကအချက်ဖြစ်ပါတယ်။ Path Object အတွင်း PathElement Object များကို ဖြည့်စွက်ခြင်းအားဖြင့် လိုအပ်သော Path Shape Object တစ်ခုကို တည်ဆောက်နိုင်မှာဖြစ်ပါတယ်။ အသုံးပြုနိုင်သော PathElement Class ဟာ Abstract Class ဖြစ်ပြီ အသုံးပြုနိုင်မယ့် Concrete Class တွေကတော့ အောက်ပါအတိုင်းဖြစ် ပါတယ်။

Path Element Description
MoveTo Path Object အတွင်းသို့ စတင်ဝင်ရောက်မယ့် အမှတ်ကို သတ်မှတ်ပေးနိုင်ပါတယ်
HLineTo Path အတွင်းရှိလက်ရှိနေရာမှ ရေပြင်ညီ မျဥ်းအတိုင်း အကွာအဝေးတန်ဖိုးကို သတ်မှတ်ပေးနိုင်ပါတယ်
VLineTo Path အတွင်းရှိလက်ရှိနေရာမှ ဒေါင်လိုက် မျဥ်းအတိုင်း အကွာအဝေးတန်ဖိုးကို သတ်မှတ်ပေးနိုင်ပါတယ်
LineTo Path အတွင်းရှိလက်ရှိနေရာမှ x နှင့် y တန်ဖိုးကို သတ်မှတ်၍ မျဥ်းတစ်ကြောင်းကို ရေးဆွဲ ပေးနိုင်ပါမယ်
ArcTo Path အတွင်းရှိလက်ရှိနေရာမှ x နှင့် y တန်ဖိုးကို သတ်မှတ်၍ မျဥ်းကောက်တစ်ခုကို ရေးဆွဲ ပေးနိုင်ပါမယ်
CubicCurveTo Path အတွင်းရှိလက်ရှိနေရာမှ x နှင့် y တန်ဖိုးကို သတ်မှတ်၍ Cubic Curve မျဥ်းတစ်ကြောင်းကို ရေးဆွဲ ပေးနိုင်ပါမယ်
QuadCurveTo Path အတွင်းရှိလက်ရှိနေရာမှ x နှင့် y တန်ဖိုးကို သတ်မှတ်၍ Quadratic Curve မျဥ်းတစ်ကြောင်းကို ရေးဆွဲ ပေးနိုင်ပါမယ်
ClosePath Path အတွင်းရှိလက်ရှိနေရာမှ Path ကို စတင်ခဲ့သော နေရာသို့ မျဥ်းတစ်ကြောင်းကို ရေးဆွဲ ပေးနိုင်ပါမယ်
Path Object ကို တည်ဆောက်တဲ့ နေရာမှာ MoveTo Object ကို စတင်ဖြည့်စွက်၍ လိုအပ်သလို PathElement Object များကို ဖြည့်စွက်ရေးသား ရပါမည်။ Path Object တစ်ခုကို ပရိုဂရမ်အတွင်းရေးသားနိုင်သလို SceneBuilder ဖြင့်လည်း FXML ဖြင့် ရေးသားနိုင်မှာဖြစ်ပါတယ်။ ဤနမူနာထဲမှာတော့ SceneBuilder ကို အသုံးပြုပြီးဖော်ပြသွားပါမယ်။
public class Move implements Initializable{
    
    @FXML
    private Polygon node;
    @FXML
    private Path path;
    
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        PathTransition pathTrans = new PathTransition(Duration.millis(4000), 
                path, node);
        pathTrans.setCycleCount(Timeline.INDEFINITE);
        
        SequentialTransition seq = new SequentialTransition(getRot(), 
                getRot(), getRot(), getRot());
        seq.setCycleCount(Timeline.INDEFINITE);
        
        ParallelTransition paral = new ParallelTransition(pathTrans, seq);
        
        node.setOnMouseClicked(a -> {
            if(paral.getStatus().equals(Status.RUNNING)) {
                paral.pause();
            } else {
                paral.play();
            }
        });
    }
    
    private SequentialTransition getRot() {
        RotateTransition rot = new RotateTransition(Duration.millis(50), node);
        rot.setByAngle(90);
        return new SequentialTransition(new PauseTransition
                (Duration.millis(950)), rot);
    }
}
အထက်ပါ နမူနာထဲမှာကြည့်မယ် ဆိုရင် FXML ဖိုင်လ်ထဲကနေ Inject လုပ်ပြီးသုံးမယ့် Polygon နဲ့ Path Object နှစ်ခုကို @FXML Annotation ကို သုံးပြီး ရေးထားပါတယ်။ အဆိုပါ Object များကို သုံးပြီး initialize method ထဲမှာ PathTransition Object ကို တည်ဆောက်နေပါတယ်။

Object ကို တည်ဆောက်ပြီး အခြားသော Transition များနှင့်တွဲဖက် အသုံးပြုပုံကတော့ အခြားသော Transition Animation များ နှင့် အတူတူပဲ ဖြစ်ပါတယ်။ အသေးစိတ်ရေးသားပုံကို တော့ အောက်ပါ ဗွီဒီယိုဖိုလ်မှာ လေ့လာနိုင်မှာဖြစ်ပါတယ်။



ကျွန်တော်တို့ အရင်တစ်ကြိမ်ရေးသားခဲ့သော Day 16 Transition (Part 1) အပါအဝင် Transition Animation အကြောင်းအား နှစ်ကြိမ်ဆက်တိုက် ရေးသား ဖော်ပြခဲပါတယ်။ Java FX ရဲ့ Transition ကို အသုံးပြုပြီး အသုံးများသော Animation များကို အလွယ်တကူ အသုံးပြုနိုင်မှာ ဖြစ်ပါတယ်။

အထက်ပါ ဗွီဒီယိုဖိုင်လ် ထဲမှာသုံးထားတဲ့ Slide Show လေးဟာလဲ TranslateTransition ကို အသုံးပြုပြီးရေးသားထားတာဖြစ်ပါတယ်။ နောက် အခေါက်များမှပဲ ရေးသားပုံအား ဆက်လက်ဖေါ်ပြသွားပါမည်။


လေးစားစွားဖြင့်
မင်းလွင်

April 19, 2015

Day 16 Transition (Part 1)

Java FX အကြောင်းအား ရေးသားခဲ့သည်မှာ ၁၆ခန်းမြောက်ကို ရောက်ရှိခဲ့ပြီ ဖြစ်သည်။ ယခုတစ်ခေါက်မှာတော့ Animation အကြောင်းကို မိတ်ဆက်သွားပါမည်။ သို့ရာတွင် တစ်ခု အမှတ်မမှား စေရန်မှာ Animation များ Game များအား ရေးသားရန်မဟုတ်။ Business Application များအား ရေးသားရာတွင် ပိုမို Interactive ဖြစ်သော Application များအား လွယ်ကူစွာ ရေးသားနိုင်ရန် ရည်ရွယ်ပါသည်။

JavaFX ကို အသုံးပြုပြီး Animation ကို ရေးသားတဲ့အခါမှာ Transition နဲ့ Time Line Animation ဆိုပြီး အသုံးပြုနိုင်ပါတယ်။ ကျွန်တော်တို့ ဒီအခန်းမှာ အလွယ်တကူနဲ့ ရေးသားနိုင်တဲ့ Transition Animation ကို ရေးသားကြည့်ပါမယ်။


Animation ကို ရေးတဲ့အခါမှာ လှုပ်ရှားမှု့ကို ပြသရန်လို အပ်တဲ့အတွက် ကျွန်တော်တို့ Video Tutorial အဖြစ်ဖေါ်ပြသွားပါမယ်။


Transition

Animation ဆိုတာဟာ Object တစ်ခုရဲ့ Properties တွေကို Time Line တစ်ခု အပေါ်မူတခု အပေါ်မူတည်ပြီး ပြောင်းလဲပေးနေခြင်းဖြစ်တယ်။ ဥပမာအားဖြင့် လေးထောင့်ပုံလေးတစ်ခုရဲ့ အရောင်ကို အပြည့်မြင်နေရာကနေ အချိန်တစ်ခု အတွင်း တဖြည်းဖြည်း မှေးမှိန်ပျောက်ကွယ်စေတာတို့၊ သူ့ရဲ့ တည်နေရာကို တနေရာကနေ တနေရာကို ရွှေ့ပစ်တာတို့ ဒါတွေကို ဖြစ်စေတာဟာ Animation ဖြစ်ပါတယ်။

JavaFX ကို သုံးပြီး Animation ကို ရေးသားရာတွင် အသုံးများသော လုပ်ဆောင်မှု့များကို အလွယ်တကူ အသုံးပြုနိုင်စေရန် Transition အနေနဲ့ ပံ့ပိုးပေးထားပါတယ်။ လက်ရှိ အသုံးပြုနိုင်သော Transition တွေကတော့ အောက်ပါအတိုင်းဖြစ်ပါတယ်။

Transition Description
FillTransition Object တစ်ခုရဲ့ အရောင်ကို ပြောင်းလဲစေလိုတဲ့ အခါမှာ အသုံးပြုနိုင်ပါတယ်
ScaleTransition Object တစ်ခုရဲ့ အရွယ်အစားကို ပြောင်းလိုတဲ အခါမှာ အသုံးပြုနိုင်ပါတယ်
RotateTransition Object တစ်ခုရဲ့ လည်ပတ်မှု့ထောင့်တန်ဖိုးကို ပြောင်းလဲစေလိုတဲ့ အခါမှာ အသုံးပြုနိုင်ပါတယ်
StrokeTransition Object တစ်ခုရဲ့ လိုင်းအထူအပါးတန်ဖိုးကို ပြောင်းလဲစေလိုတဲ့ အခါမှာ အသုံးပြုနိုင်ပါတယ်
TranslateTransition Object တစ်ခုရဲ့ တည်နေရာကို x, y, z ဝင်ရိုးအပေါ်မှာ တစ်နေရာကနေ တစ်နေရာကို ပြောင်းရွှေ့စေလိုတဲ့ အခါမှာ အသုံးပြုနိုင်ပါတယ်
PathTransition Object တစ်ခုရဲ့ တည်နေရာကို Path လမ်းကြောင်းတစ်ခု အပေါ်မှာ ပြောင်ရွှေ့စေလို တဲ့အခါမှာ အသုံးပြုနိုင်ပါတယ်

နောက်ပြီး Animation ကို လုပ်ဆောင်စေရာမှာ တစ်ခုပြီးမှတစ်ခု လုပ်ဆောင်စေနိုင်သလို၊ အပြိုင်လဲလုပ်ဆောင် စေနိုင်ပါတယ်။ Transition Animation တွေကို တစ်ခုပြီးတစ်ခု လုပ်ဆောင်စေတဲ့ Animation မျိုးကို SequentialTransition ဖြင့် လုပ်ဆောင်စေနိုင်ပြီး၊ အပြိုင်လုပ်ဆောင်စေတဲ့ Animation မျိုးကို ParallelTransition ဖြင့်လုပ်ဆောင်စေနိုင်ပါတယ်။

အထက်ပါ Transition Class တွေအားလုံးဟာ javafx.animation.Transition Class ရဲ့ Sub Class တွေဖြစ်ကြတယ်။ Transition Class ဟာ javafx.animation.Animation ရဲ့ Sub Class ဖြစ်ပြီး Transition Animation တွေအားလုံးရဲ့ အခြေခံ Class ဖြစ်ပါတယ်။ Transition Class ထဲမှာ Transition အားလုံးမှာ အသုံးပြုနိုင်မယ့် အခြေခံ Properties တွေကို ပိုင်ဆိုင်ပါတယ်။ အဆိုပါ တန်ဖိုးများကို ပြောင်းလဲခြင်းအားဖြင့် Transition Animation များကို ရေးသားနိုင်မှာ ဖြစ်ပါတယ်။

Properties Type Description
interpolator ObjectProperty<Interpolator> Transition Cycle တစ်ခုခြင်းစီရဲ့ Acceleration နဲ့ Deceleration လုပ်မဲ့ Timing ကို Control လုပ်ပေးနိုင်ပါတယ်
autoReverse BooleanProperty Auto Reverse လုပ်မလား၊ မလုပ်ဘူးလား ဆိုတာကို ဆုံးဖြတ်ပေးနိုင်ပါတယ်
currentRate ReadOnlyDoubleProperty လက်ရှိ အလုပ်လုပ်နေတဲ့ အမြန်နှုန်းကို ဖော်ပြပေးနိုင်ပါတယ်
currentTime ReadOnlyObjectProperty<Duration> Timeline ပေါ်က လက်ရှိရောက်ရှိနေတဲ့ အချိန်ကို ဖော်ပြပေးနိုင်ပါတယ်
cycleCount IntegerProperty Transition ကို ဘယ်နှစ်ကြိမ်လုပ်မလဲ ဆိုတာကို ဆုံးဖြတ်ပေးနိုင်ပါတယ်
cycleDuration ReadOnlyObjectProperty<Duration> Cycle တစ်ခု ကို ဆောင်ရွက်ရာတွင် ကြာမည့် အချိန်ကို ဖေါ်ပြပေးနိုင်ပါတယ်
delay ObjectProperty<Duration> အချိန်ဘယ်လောက် အကြာမှာ Transition ကို စတင်မလဲဆိုတာကို သတ်မှတ်ပေးနိုင်ပါတယ်
onFinished ObjectProperty<EventHandler<ActionEvent>> Transition ပြီးဆုံးချိန်မှာ အလုပ်လုပ်မဲ့ EventHandler Object ကို သတ်မှတ်ပေးနိုင်ပါတယ်
rate DoubleProperty Animation တစ်ခုရဲ့ အလုပ်လုပ်မယ့် direction / speed နှုန်းကို ဖော်ပြပေးနိုင်ပါမယ်
status ReadOnlyObjectProperty<Animation.Status> လက်ရှိ Animation ရဲ့ အနေအထားကို ဖော်ပြပေးနိုင်ပါမယ်
totalDuration ReadOnlyObjectProperty<Duration> Animation တစ်ခုလုံးရဲ့ ကြာချိန်ကို ဖော်ပြပေးနိုင်ပါမယ်

အထက်ဖော်ပြပါ Member Fields တွေကို ကြည့်မယ်ဆိုရင် Property အမျိုးအစားတွေကို အသုံးပြုထားတာကို တွေ့ရပါမယ်။ Property အမျိုးအစားတွေဟာ ၎င်းတို့ရဲ့ တန်ဖိုးပြောင်းလဲမှု့ကို Event အနေနဲ့ ဖော်ပြနိုင်ပြီး အခြား Property Variable တစ်ခုခုနှင့်လည်း Bind လုပ်နိုင်ပါမယ်။ တန်ဖိုးပြောင်းလဲမှု့ အပေါ်မှာ EventHandler တွေကို သတ်မှတ်ထားနိုင်ပြီး အလိုအလျောက် အလုပ်လုပ်နိုင်ရန် စီမံထားနိုင်ပါမယ်။

အထက်ပါ Property များထဲမှာ Read Only Property တွေပါဝင်သလို Read And Write လုပ်နိုင်တဲ့ Property တွေလဲပါဝင်ပါတယ်။ Read Only Property တွေကိုလက်ရှိ Animation ရဲ့ State ကို သိရှိရန် အသုံးပြုပြီး၊ Read And Write Property တွေကို တော့ Animation ရဲ့ Default State ကို ပြုပြင်ရန် အသုံးပြုပါတယ်။

1. Transition Sample 1



2. Transition Sample 2



3. Transition Sample 3


ဆက်ပါဦးမည်။ လေးစားစွာဖြင့်
မင်းလွင်

April 5, 2015

Day 15 CSS (Part 3)

ပြီးခဲ့တဲ့ အခေါက်က CSS ကို သုံးပြီး Layout သတ်မှတ်ပုံ အကြောင်းအား ဖော်ပြခဲ့ပါသည်။ Layout Component များ၊ Controls Component များ ၏ အကျယ်၊ အမြင့်၊ Padding နှင့် Spacing များအား CSS ဖြင့် မည်သို့ သတ်မှတ်ရမည်ဆိုသည်ကို လေ့လာခဲ့ပါသည်။ ယခု တခေါက်တွင်လည်း အရောင်နှင့် Effectများ အား CSS ဖြင့် မည်သို့သတ်မှတ်ရမည် ဆိုသည်ကို ဆက်လက် ဖေါ်ပြသွားပါဦးမည်။


Background Color


Container Component များ၏ Background Color အားသတ်မှတ်ရာတွင် -fx-background-color Attribute အား အသုံးပြုရပါသည်။ ယခင် အသုံးပြုခဲ့သော နမူနာ၏ Sign Up Form ၏  heading နှင့် body class တို့၏ background color များအား ပြောင်းကြည့်ပါမည်။


Color နှင့် ပတ်သက်သော သတ်မှတ်ချက်များကို ယခင်အသုံးပြုခဲ့သော layout.css တွင် ဖြည့်စွက် ရေးသား အသုံးပြုနိုင်ပါသည်။ သို့ရာတွင် ယခုနမူနာ၌ အရောင်အတွက် css အား color.css ဟု file အသစ်တစ်ခု အား ရေးသား ၍ အသုံးပြုပါမည်။ ဤကဲ့သို့ color နှင့် layout အား သီးခြားစီ ရေးသားအသုံးပြုခြင်းအားဖြင့် Theme များအား လိုအပ်သလို ပြောင်းလဲအသုံးပြုနိုင်မည် ဖြစ်ပါသည်။

ယခင်နမူနာတွင် ဖေါ်ပြခဲ့သကဲ့သို့ပင် color.css file အသစ်တစ်ခုအား Singup.fxml တည်ရှိသည့် Package အောက်တွင် တည်ဆောက်ပါမည်။ ပြီးပါက Root Element ဖြစ်သော VBox အား Click လုပ် ရွေးချယ်၍ layout.css ၏ ဘေးရှိ အပေါင်လက္ခဏာ (+) အား Click လုပ်၍ ဖြည့်စွက် အသုံးပြုမည့် color.css အား အောက်ပါအတိုင်းရွေးချယ်ပါမည်။


ပြီးပါက ၎င်း ဖိုင်လ် အတွင်းတွင် အောက်ပါအတိုင်း ကုဒ်များအား ဖြည့်စွက်ပါမည်။
.heading {
 -fx-background-color: #8e44ad; 
}

.body {
 -fx-background-color: #3498db;
}
SceneBuilder အားပြန်ကြည့်သော အခါ အောက်ပါအတိုင်းပြောင်းလဲသွားသည်ကို တွေ့ရပါမည်။


ပြီးပါကအောက်ပါကုဒ်များ အားဖြည့်စွက်၍ Button နှင့် Input များ၏ နောက်ခံအရောင်များ အား ပြောင်းလဲပါမည်။
.form-group TextField, .form-group PasswordField {
 -fx-background-color: #2980b9;
}

.btn-group Button {
 -fx-background-color: #9b59b6;
}
HTML တွင် အသုံးပြုသော CSS ကဲ့သို့ပင် Form Group ၏ အောက်ရှိ TextField နှင့် PasswordField များ၏ Theme အား ပြောင်လဲလိုပါက .form-group TextField, .form-group PasswordField ဟု ရေးသားရပါသည်။



Text Color


တဖန် Lable နှင် Text တို့၏ အရောင်အား ပြောင်းလဲရန် အောက်ပါ ကုဒ်များအား color.css အတွင်း ဖြည့်စွက်ပါမည်။
.btn-group Button {
 -fx-background-color: #9b59b6;
 -fx-text-fill: #ecf0f1;
}

.heading Text, .body Text, .heading Label, .body Label {
 -fx-fill: #ecf0f1;
 -fx-text-fill: #ecf0f1;
}
JavaFX တွင် Text Component နှင့် အခြားသော Component များ၏ ရေးသားပုံခြင်းမတူညီပါ။ Text များအတွက်ဆိုပါက -fx-fill အား အသုံးပြု၍ အခြားသော Component များအား -fx-text-fill ဟု သတ်မှတ်ရေးသားရပါသည်။



Psudo Class Hover


Button များအား Mouse Over လုပ်သောအခါမျိုးတွင် နောက်ခံအရောင်များအား ပြောင်လဲလိုပါက Psudo Class ဖြစ်သော :hover အား အသုံးပြု ရေးသားရပါသည်။ အောက်ပါကုဒ်များအား color.css အတွင်း ဖြည့်စွက် ရေးသားပါမည်။
.btn-group Button:hover {
 -fx-background-color: #2c3e50;
 -fx-text-fill: #ecf0f1;
}
SceneBuilder ၏ preview menu ဖြင့် SingUp Form အား ဖွင်ကြည့်သော အခါ အောက်ပါအတိုင်း Button အပေါ Mouse Over လုပ်ကြည့်သော အခါ Style ပြောင်းလဲပေးနိုင်သည်ကို တွေ့ရပါသည်။



Adding Effect


Java FX တွင် CSS နှင် Effect အား ရေသားနိုင်သည်မှာ drop shadow နှင့် inner shadow တို့ဖြစ်ကြပါသည်။  -fx-effect Attribute နှင့် Component များ၏ Effect များအား သတ်မှတ်နိုင်ပါသည်။
ဦးစွာ အောက်ပါကုဒ်များအား color.css တွင်ဖြည့်စွက်ပြီး Heading Text ၏ effect အား ပြုပြင် ရေးသား ကြည့်ပါမည်။
.heading Label {
 -fx-effect: dropshadow( three-pass-box , #2c3e50 , 2,2,2,1 );
}
SceneBuilder ဖြင့်ကြည်သောအခါ အောက်ပါအတိုင်းတွေ့မြင်ရမည် ဖြစ်ပါသည်။


Drop Shadow Effect အား ရေးသားရာတွင် ပါရာမီတာအဖြစ် ၆ခု ရေးသားအသုံးပြုရမည် ဖြစ်သည်။
  1. blur type
    Blur Type အနေနှင့် one-pass-box, two-pass-box, three-pass-box နှင့် gaussian တို့ ဖြစ်ကြသည်။ 
  2. shadow color
    Shadow ၏ အရောင်အား သတ်မှတ်ရေးသားရပါမည်။
  3. width
    Shasow ၏ width အားသတ်မှတ်ရေးသားရပါမည်။
  4. height
    Shadow ၏ height အားသတ်မှတ်ရေးသားရပါမည်။
  5. radius
    Shadow ၏ radius အား သတ်မှတ်ရေးသားရပါမည်။
  6. spread
    Shadow ၏ spread အား သတ်မှတ်ရေးသားရပါမည်။
ယခုတစ်ခေါက်တွင် CSS အား အသုံးပြု၍ GUI Application များ၏​ အရောင်နှင့် Effect တို့အား အလွယ်တကူ ပြောင်းလဲပြုပြင်နိုင်ကြောင် အား ဖေါ်ပြခဲ့ ပါသည်။ နောက် အခန်းဖြင့် JavaFX ၏ Animation အကြောင်းအား ရေးသားဖေါ်ပြသွားပါဦးမည်။

လေးစားစွာဖြင့်
မင်းလွင်

April 2, 2015

Day 14 CSS (Part 2)

ဒီအခန်းဆက်ကို မရေးဖြစ်သည်မှာ ကြာပြီဖြစ်ပါသည်။ အလုပ်ရှုပ်နေသည်ကတကြောင်း ကွန်ပျူတာပြောင်းပြီ ကတည်းက ဂျပန်လက်ကွက်ဖြစ်တဲ့အတွက် မြန်မာလို မှန်အောင်ရိုက်လို့မရသည်က တကြောင်း၊ တော်ရုံနှင့် စာမရေးဖြစ်ခဲ့ပါ။ အခုတော့ ကီဘုတ် အသေးလေးတစ်ခုဝယ်ပြီး မြန်မာလို မှန်အောင်ရိုက်နိုင်လာပြီ ဖြစ်တဲ့ အတွက် အချိန်လုကာ စာရေးပါဦးမည်။

ဒီတစ်ခေါက်မှာတော့ CSS ကိုအသုံးပြုပြီး GUI Application တစ်ခု၏ အမြင်ပိုင်းဆိုင်ရာ ပြုပြင်မှု့အကြောင်ကို ရေးသားသွားပါမည်။ အရင်ဦးဆုံး ဘယ်လိုအခါမျိုးမှာ Style ကိုပြောင်းရန်လိုအပ်သနည်း ဆိုသည့် အချက်ကို စဥ်းစားကြည့်ပါမည်။ အခြေခံအားဖြင့် အသုံးပြုမည့် Component များအား Default အတိုင်းအသုံးမပြုလိုသည့် အခါ၊ ဒါမှမဟုတ် Application တစ်ခုလုံအတွင်းရှိ Component များအား ညီတူညာတူတန်ဖိုးသတ်မှတ်၍အသုံပြုလိုသည့် အခါမျိုးတွင် Style များ ပြောင်လဲရန် လိုအပ်တတ်ပါသည်။

နောက်တဆင့် အနေနဲ့ Component တွေရဲ့ ဘယ်လို Style တွေကို ပြောင်းလဲနိုင်သလဲဆိုတဲ့ အချက်ကို စဥ်းစားကြည့်ပါဦးမည်။
  • Component တစ်ခု၏ အရွယ်အစား အကြီအသေး အားပြောင်လဲနိုင်ပါမည်
  • Component ၏ Attributes တန်ဖိုးများအားပြောင်လဲသတ်မှတ်နိုင်မည်
  • Component ၏ အရောင်များအာ ပြောင်းလဲသတ်မှတ်နိုင်ပါမည်
  • Container Component များဆိုပါက ၎င်းအတွင်းရှိ Child Component များအား မည်ကဲမ့သို့ထားမည်၊ Space များ၊ Padding များ၊ Margin များအား မည်သို့ထားမည် ဆိုသည်တို့ကို သတ်မှတ်နိုင်ပါမည်

Customizing Layout



GUI Application တစ်ခု၏ Layout အားသတ်မှတ်ရာတွင် Component တို့၏ အရွယ်အစား၊ နေရာ အထားအသိုတို့ဖြင့် သတ်မှတ်ရပါမည်။ ထိုအပြင် Application တစ်ခုလုံးတွင်ရှိသော အမျိုးအစား တူညီသည့် Component များသည် အခြေခံအားဖြင့် တူညီမှု့ရှိရန် လိုအပ်ပါသည်။ ဟိုနေရာ တမျိုး ဒီနေရာ တမျိူး ရေးနေရင်ဖြင့် အစဥ်ပြေမည်မဟုတ်။

ဥပမာအားဖြင့် Login Form, Sign Up Form, Base Panel, Message Dialog များအား မည်သို့ ဖေါ်ပြမည် ဆိုသည့် ပေါ်လစီအား ကြိုတင် သတ်မှတ်ထားရပါမည်။ အခြေခံအားဖြင့် GUI Application များတွင် Window Full Size ဖြင့် အသုံးပြုလိုသည်ကများပါမည်။ တဖန် Sub Window နှင့် Login Form နှင့် Message Dialog များ အနေနှင့် အသုံပြုလိသည့် Size များ ရှိကြပါမည်။

ထို့ကြောင့် layout.css အားရေးသား၍ Layout နှင့် ပတ်သက်သည်များကို ထို File အတွင်းတွင် သတ်မှတ် ရေးသားသွားပါမည်။
.panel-1 {
 -fx-pref-width: 1280px;
 -fx-pref-height: 780px;
}
.panel-2 {
 -fx-pref-width: 980px;
 -fx-pref-height: 680px;
}
.panel-3 {
 -fx-pref-width: 480px;
}
Java FX တွင် StyleSheet အား အသုံးပြုလိုသည့် အခါတွင် Java Program အတွင်း Scene Object ၏ Property တွင်ဖြည့် စွက်၍သော်၎င်း၊ FXML File အတွင်းတွင် တိုက်ရိုက်ရေးသား၍သော်၎င်း အသုံးပြုနိုင်ပါသည်။ သို့ရာတွင် FXML File အတွင်းရေးသားခြင်းမှာ CSS File အတွင်း ပြုပြင်ခြင်း အား SceneBuilder အတွင်းမှ တိုက်ရိုက် သိမြင်နိုင်ခြင်းကြောင့် အလွန်အစဥ်ပြေလှပါသည်။

နမူနာ အနေဖြင့် Panel တစ်ခုအားရေးသားကြည့်ပါမည်။
e(fx)clipse Plugin Install လုပ်ပြီးဖြစ်သော Eclipse IDE အားဖွင့်၍၊ CTL + n အားနှိပ်ကာ New Wizard Window အားဖွင့်ပါမည်။


ပြီးပါက Text Box အတွင်းတွင် fxmlဟု ရိုက်ထည့်၍ New FXML Document အားရွေးကာ Next ခလုပ်အား နှိပ်ပါမည်။ ထိုနောက် SignUp ဟု ဖြည့်ကာ Finish အားနှိပ်ကာ SignUp.fxml ဖိုင်လ်အား တည်ဆောက်ပါမည်။


အထက်ဖော်ပြပါအတိုင်း SignUp.fxml အား Right Click ထောက်၍ SceneBuilder ဖြင့် ဖွင့်ပါမည်။ ပြီပါက Root Element အား Select လုပ်၍ အသုံပြုလိုသည့် Style Sheet အား ရွေးချယ် ရပါမည်။ FXML တစ်ခုတွင် Style Sheet တစ်ခုထက်မက ဖြည့်စွက်ရေးသားနိုင်ပါသည်။ အောက်နမူနာတွင် layout.css အား ဖြည့်စွက်ထားပါသည်။


ပြီးပါက Style Class အတွင်း နှစ်သက်ရာ class name အား ဖြည့်စွက် ရေးသားပါက Style Sheet အတွင်း သတ်မှတ် ရေးသားထားသည့် အတိုင်း ပြောင်းလဲဖော်ပြနိုင်သည်ကို တွေ့ရပါမည်။ အောက်ပါ နမူနာတွင် panel-3 အား ဖြည့်စွက်ရေးသားထားပါသည်။


နမူနာမှာ Sign Up Panel ဖြစ်ပါသဖြင် ၎င်းတွင် Heading နှင် Body အပိုင်းတို့ပါဝင်ပါမည်။ Heading အပိုင်းအား HBox အား အသုံးပြုပြီ၊ Body နေရာအား VBox အား အသုံးပြုမည် ဖြစ်ပါသဖြင့်HBox နှင့် VBox တို့အား အသီးသီး Drug And Drop လုပ်၍ Root Element ဖြစ်သော VBox အတွင်သို့ ဖြည့်စွက်ပါမည်။ ပြီးပါက layout.css တွင် .heading နှင့် .body class တို့၏သတ်မှတ်ချက်တို့အား အောက်ပါအတိုင်း ဖြည့်စွက်ရေးသားပါမည်။
.heading {
 -fx-alignment: center-left;
 -fx-padding: 15;
 -fx-spacing: 15;
}

.body {
 -fx-alignment: center-left;
 -fx-padding: 15 15 30 15;
 -fx-spacing: 15;
}
တဖန် အောက်ပါအတိုင်း Lable Component တစ်ခုအား HBox အတွင်းသို့ Drug And Drop လုပ်ကာ Value အား Sign Up Form ဟု ပြောင်လဲရေးသားပါမည်။


ပြီးပါက layout.css တွင် Heading Text နှင့် Form Group Element များအတွက် အောက်ပါ အတိုင်း Style Class များအား ဖြည့်စွက် ရေးသားပါမည်။
.h1 {
 -fx-font-size: 48px;
 -fx-font-weight: bold;
 -fx-font-family: "Chaparral Pro";
}

.h2 {
 -fx-font-size: 36px;
 -fx-font-weight: bold;
 -fx-font-family: "Chaparral Pro";
}

.h3 {
 -fx-font-size: 24px;
 -fx-font-weight: bold;
 -fx-font-family: "Chaparral Pro";
 -fx-font-style: italic;
}

.form-group {
 -fx-pref-width: 1280px; 
 -fx-spacing: 10;
 -fx-padding: 0 20 0 60;
}
.form-group class ၏ -fx-pref-width ကိုကြည့်ပါ။ တန်ဖိုးအား 1280px ဟု သတ်မှတ်ထားပါသည်။ သင့်တော်သော width အား အမြင့်ဆုံတန်ဖိုးဖြင့်သတ်မှတ်ထားပါသည်။ ထိုကဲ့သို့ရေးသားထားခြင်းဖြင့် Parent Element ၏ ဖြည့်စွက်နိုင်သော width ရှိသမျှ အဆိုပါ Element၏ width အားသတ်မှတ်ပေးမည် ဖြစ်ပါသည်။ Window ၏ ပမာဏအား ပြောင်းလဲလိုက်တိုင်း Element များ၏ ပမာဏအား Dynamically ပြောင်းစေလိုသည့် အခါမျိုးတွင် ထိုကဲ့သို့ ရေးသားနိုင်ပါသည်။ Dynamic Component Sizing အကြောင်းအား နောက်အခန်းဆက်များတွင်အသေးစိတ်ဖော်ပြသွားပါဦးမည်။


ထိုနောက် Label ၏ Style Class နေရာတွင် h2 ဟု ဖြည့်စွက်လိုက်ပါက အထက်ပါအတိုင်း ပြောင်းလဲ သွားသည်ကို တွေ့ရပါမည်။


ထို့နောက်တွင် VBox.body အတွင်းသို့ VBox တစ်ခုအား Drug And Drop လုပ်၍ Style Class အား form-group ဟု ဖြည့်စွက် ပါမည်။ ပြီးပါက ၎င်း၏ Prefer Width နှင့် Prefer Height တို့အား USE_COMPUTED_SIZE ဟု ပြောင်းထားပါမည်။ တဖန် ၎င်းအတွင်းသို့ Label တစ်ခုအား Drug And Drop လုပ်၍ တန်ဖိုးအား Name ဟု ပြောင်းပါမည်။ ပြီးပါက TextField တစ်ခုအား အလားတူ Label ၏ အောက်သို့ ဖြည့်စွက်ပါမည်။


အလားတူပင် Login ID နှင့် Password တို့အား အထက်ပါနည်း အတိုင်း ရေးသားပါမည်။ Password အတွက်မှာမူ TextField မဟုတ်ပဲ PasswordField အားအသုံးပြုသင့်ပါသည်။


နောက်ဆုံးတွင် Button များအတွက် HBox တစ်ခုအား VBox.body ၏ နောက်ဆုံသို့ Drug and Drop လုပ်၍ Style Class အား form-group ဟု ဖြည့်စွက်ထားပါမည်။ အထက်တွင်ဖော်ပြခဲ့သလိုပင် ၎င်း၏ Prefer Width နှင့် Prefer Height Properties တို့အား USE_COMPUTED_SIZE ဟု ပြောင်းထားပါမည်။ ထို့နောက် Button နှစ်ခုအား ၎င်းအတွင်းသို့ Drug And Drop လုပ်ကာ Value အား အသီးသီး CANCEL နှင့် SIGN UP ဟု ပြောင်း ထားပါမည်။


အထက်ပါ အတိုင်း Sign Up Form တစ်ခုအား CSS နှင့် SceneBuilder တို့အား အသုံးပြု၍ အလွယ် တကူ ရေးသားနိုင်သည်ကို တွေ့ရပါသည်။ တဖန် အလားတူ Login Form များအား ရေးသားရာတွင်လည်း ရေးသားပြီးဖြစ်သော layout.css ဖိုင်လ်အား အသုံးပြု၍ အလွယ်တကူ ရေးသားနိုင်မည် ဖြစ်ပါသည်။

ထို့ကြောင့် CSS အား အသုံးပြုပါက
  • View Style အား အလွယ်တကူ ပြောင်းလဲရေးသားနိုင်ခြင်း
  • ရေးသားထားသော CSS များအား ပြန်လည် အသုံးပြုနိုင်ခြင်း
  • Application တစ်ခုလုံး၏ Style အား ညီညာမှု့ရှိစေရန် တစ်နေရာထဲမှနေ၍ ထိမ်းချူပ်နိုင်ခြင်း
  • View Style နှင့် ပတ်သက်သော ကုဒ်များအား Program အတွင်းရေးသား ရန်မလိုအပ်သောကြောင့် Business Logic များအား အာရုံစိုက်၍ အမှားအယွင်းနည်းအောင် ရေးသားနိုင်ခြင်း
  • View နှင့် Logic Code များအား တနေရာစီကွဲအောင် ရေးသားနိုင်ခြင်းကြောင့် Maintenance ပြုလုပ်ရာတွင် လွယ်ကူစေခြင်း 
အစရှိသည့် အကျိုးများကို ရရှိစေနိုင်ပါသည်။ နောက်အခန်းဆက်များဖြင့် Color နှင့် Effect တို့အား CSS ဖြင့် သတ်မှတ် ရေးသားခြင်း အကြောင်းကို ဆက်လက် ရေးသားသွားပါဦးမည်။

လေးစားစွာဖြင့်
မင်းလွင်

April 20, 2014

Day 13 CSS (Part 1)

ဤတစ်ခေါက် JavaFX အကြောင်းအား မိတ်ဆက်ရာတွင် GUI ၏ Styling နှင့် ပတ်သက်ပြီး ဖော်ပြသွားပါမည်။

Swing ဖြင့် GUI အပလီကေးရှင်းများအား ရေးသားဘူးသူဆိုပါက သိပါလိမ့်မည်။ Swing တွင် Component တစ်ခု၏ Font Style ဒါမှမဟုတ် အရောင်များအား ပြောင်းလည်းလိုပါက Properties များအား Set လုပ်၍ ပြောင်းလည်းရပါသည်။ သို့ပါသော်လည်း Application တစ်ခုလုံး၏ Style အား ပြောင်းလည်းလိုသည့် အခါတွင် လွယ်ကူမည်မဟုတ်ပေ။

Look And Feel အားပြောင်း၍ Application တစ်ခုလုံး၏ Style အား အသင့်ပေထားသော Style ဖြင့် ပြောင်းလည်းလိုသည့် အခါတွင် အစဉ်ပြေသော်လည်း၊ တစ်ချို့တစ်နေရာအား ပြောင်းလည်းလိုပါက လွယ်သည့် ကိစ္စတော့မဟုတ်ပေ။ ဒီလောက်ကလေး ပြောင်းဖို့အတွက် ဘာလို့ ဒီလောက် ကုဒ်တွေရေးနေရတာလဲ ဟု အကြိမ်ကြိမ် ဒေါပွခဲ့မိဘူးပါသည်။

Layout တွေလည်းချ Style တွေလည်း ပြောင်းပြီးရော Swing Class တစ်ခုသည် ပြန်ဖတ်ချင်စရာ မကောင်းလောက်အောင် Source Code တွေ ဖောင်းပွနေတတ်ပါသည်။ Business Logic နဲ့ မဆိုင်တဲ့ Style အတွက်ရေးနေရင်းနဲ့ Business Logic အတွက် အားနည်းသွားတတ်ပါသည်။ ဒါမှမဟုတ်ရင် Business Logic ကို အရမ်းအားပေးပြီး Style ကို ကရုမစိုက်နိုင်တော့ပဲ အလွန်နုံခြာတဲ့ အပလီကေးရှင်းတွေ ဖြစ်သွားတတ်ပါသည်။


Using CSS in JavaFX


လက်ရှိ JavaFX တွင် Style အား သတ်မှတ်နိုင်သော နည်း ၃မျိုးရှိကြပါသည်။ (Java SE 8 အရောက်တွင် JavaFX 8 ဖြစ်လာပြီး၊ ၎င်းတွင် Look and Feel များကဲ့သို့ Theme အား ခွဲခြားသတ်မှတ်လာနိုင်မည် ဖြစ်သည်။ )
  1. Properties များတွင် တိုက်ရိုက် Set လုပ်၍ သတ်မှတ်ခြင်း
  2. Inline CSS အား အသုံးပြု၍ သတ်မှတ်ခြင်း
  3. ပြင်ပ CSS File အား အသုံးပြု၍ သတ်မှတ်ခြင်း

နံပါတ်တစ်နည်းသည် Component များတွင် တိုက်ရိုက် setter method များကိုသုံး၍ ၎င်း၏ တန်ဖိုးအား သတ်မှတ်ပေးနိုင်ပါသည်။ သို့ပါသော်လည်း ဤနည်းအား အသုံးပြုပါက Swing တုန်းက ကဲ့သို့ပင် အလွန်ရှည်လျားပြီး ဖတ်ရခက်သည့် Source Code တွေ ထွက်လာပါလိမ့်မည်။

ဒုတိယနည်းသည်လည်း Component များတွင် တိုက်ရိုက် setStyle အား အသုံးပြု၍သော်၎င်း၊ FXML ဖိုင် အတွင်းရှိ style tag တွင် တိုက်ရိုက်ရေးသား၍ သော်၎င်း Style အား သတ်မှတ်နိုင်ပါသေးသည်။ ဤနည်းသည်လည်း Source Code ဒါမှမဟုတ် FXML ဖိုင်တွင် တိုက်ရိုက် ရေးသားနေရ၍၊ နောက်ပိုင်းတွင် ထိမ်းသိမ်းရခက်ခဲသော Code များကို မွေးထုတ်ပေးနေသလိုပင် ဖြစ်၏။

နောက်ဆုံးနည်းသည် ပြင်ပ CSS ဖိုင်အား အသုံးပြု၍ Style အား သတ်မှတ်သောနည်း ဖြစ်ပါသည်။ CSS ဆိုပါက ကျွှန်တော်တို့ HTML အား လေ့လာဘူးသည်နှင့် ရင်းနှီးပြီး ဖြစ်နေပါလိမ့်မည်။ Component ၏ Style အား Component အားတိုက်ရိုက် ရေး၍သော်၎င်း၊ Style Class နှင့် id များအား အသုံးပြု၍သော်၎င်း သတ်မှတ်ရေးသားနိုင်ပါသည်။ တဖန် ပြင်ပတွင် CSS ဖိုင်အား ထားရှိခြင်းကြောင့် အပလီကေးရှင်းတစ်ခုလုံးတွင် အသုံးပြုနိုင်သည့် Common CSS ဖိုင်တစ်ခုအား အသုံးပြု၍လည်း အပလီကေးရှင်းတစ်ခုလုံး၏ Style ညီညာမှု့ကိုလည်း ထိမ်းသိမ်းနိုင်ပါသည်။ အနည်းဆုံးတော့ Source Code ကို တိုက်ရိုက် ပြင်စရာမလိုသောကြောင့်၊ Style ပြင်ပြီးနောက် အထွေအထူး Test လုပ်စရာလိုမည် မဟုတ်။

အတိုဆုံးအကြံပေးရမည်ဆိုလျှင်၊ ခပ်သေးသေး Tutorial Application မျိုးရေးမည်ဆိုပါက နှစ်သက်သလို ရေးသားနိုင်သော်လည်း၊ Enterprise Level Application များအား ရေးသားမည်ဆိုပါက External CSS File အား အသုံးပြု၍ ရေးသားသင့်ပါသည်။

JavaFX ၏ Scene တစ်ခုတွင် External CSS File အား အသုံးပြုနိုင်ရန် ရေးသားပုံ ရေးသားနည်း ၂ မျိုးရှိပါသည်။ ပထမတစ်နည်းမှာ Program ထဲတွင် ရေးသား၍ သတ်မှတ်နည်းဖြစ်ပြီး၊ ဒုတိယနည်းမှာ FXML File အတွင်းတွင် ရေးသားသောနည်းတို့ ဖြစ်ပါသည်။ ဤသို့ ရေးသားရာတွင်လည်း ပထမနည်းအား သိပ်ပြီးအားမပေးချင်ပေ။ အကယ်၍ ပရိုဂရမ်အတွင်း Style Sheet အား Set လုပ်မည်ဆိုပါက FXML အတွင်းတွင် Style နှင့် ပတ်သက်သည်များအား ရေးသားထားတော့မည် မဟုတ်ပေ။ ထို့ကြောင့် SceneBuilder အား  အသုံးပြု၍ Scene အား ရေးသားရာတွင် Style ပြောင်းလည်းမှု့အား တွေ့မြင်နိုင်မည် မဟုတ်။ ထို့ကြောင့် ဤဘလောဂ်တွင် SceneBuilder မှတဆင့် FXML File တွင် တိုက်ရိုက် CSS File အား ရေးသားသည့်နည်းအား မိတ်ဆက်သွားပါမည်။

ကျွှန်တော်တို့ လက်တွေ့ JavaFX Project တစ်ခုအားတည်ဆောက်၍ CSS File အား အသုံးပြုသွား ကြည့်ပါမည်။

  1. Eclipse အားဖွင့်၍ photo-album အမည်နှင့် JavaFX Project တစ်ခုအားတည်ဆောက်ပါမည်။
  2. FXML ဖိုင်အား SceneBuilder ဖြင့်ဖွင့်၍ Edit လုပ်ပါမည်။
  3. Controller Class အား ပြုပြင်ရေးသားပါမည်။
  4. css ဖိုင်အား ပြုပြင်ရေးသားပါမည်။


Creating Java FX Project

ဒီတစ်ခေါက် Java SE 8 နှင့်အတူ JavaFX 8 ကိုလည်း Release လုပ်ခဲ့ပြီး၊ JavaFX သည် JDK ၏ အစိတ်အပိုင်းတစ်ခု ဖြစ်ခဲ့သည်။ ထို့ကြောင့် ဤနမှုနာတွင် Java 8 အား အခြေခံ၍ ရေးသားကြည့်ပါမည်။ အသုံးပြုသော ပတ်ဝင်းကျင်မှာ Eclipse Kepler SR2 နှင့် e(fx)clipse 0.9 Plugin တို့ပဲ ဖြစ်ပါသည်။

File > New > Project Menu ဖြင့် New Project Wizard အား ဖွင့်ပါမည်။ ပြီးလျှင် JavaFX Project အား ရွေး၍ Next အား နှိပ်ပါမည်။


အောက်ပါအတိုင်း  Project name နေရာတွင် photo-album ဟု ဖြည့်၍ Next အား နှိပ်ပါမည်။


ပြီးပါက Application type တွင် Desktop၊ Package Name တွင် com.dtc.album, Language တွင် FXML, Root-Type ကတော့ Default အတိုင်း BorderPane၊ File Name တွင် Album နှင့် Controller နေရာတွင် AlbumController ဟု ဖြည့်ကာ Finish ကို နှိပ်ပါမည်။


အောက်ပါအတိုင်း လိုအပ်သည့် Main Class, AlbumController Class, Album.fxml နှင့် application.css တို့အား အလိုအလျှောက် ရေးသားပြီး ဖြစ်နေပါလိမ့်မည်။


Main.java တွင် အတော်များများသော ကုဒ်များအား အလိုအလျှောက်ရေးသားထားပါသည်။ အထွေအထူး ပြုပြင်စရာမလိုအပ်ပါက ဒီအတိုင်းသုံးနိုင်ပါသည်။ သို့ရာတွင် Scene scene အား new Scene(root, 400, 400) ဟု Scene ၏ Size အား သတ်မှတ်ပေးနေပါသည်။ Scene ၏ size အား FXML တွင် ရေးသားလိုသောကြောင့် ဤနေရာတွင် new Scene(root) ဟု ပြုပြင်လိုက်ပါသည်။

ကျွှန်တော်တို့အနေနှင့် Main Class အား ဒီထက်ပို၍ ပြုပြင်စရာမလိုပဲ၊ FXML, Controller နှင့် CSS တို့ကိုသာ ပြုပြင်ရေးသားသွားရုံသာပင် ဖြစ်၏။


FXML ဖိုင်အား ရေးသားခြင်း

SceneBuilder 2.0 ဖြင့် Album.fxml ဖိုင်အား ဖွင့်၍ အောက်ပါအတိုင်း ပြုပြင် ရေးဆွဲပါမည်။


Component များအား Drug And Drop လုပ်၍ နေရာချရုံသာ ဖြစ်၏။ အသေးစိတ်မှာမူ CSS ဖြင့် Style အား သတ်မှတ်ပါမည်။ ရရှိလာသော Code များမှာ အောက်ပါအတိုင်း ဖြစ်ပါသည်။
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.geometry.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>

<BorderPane xmlns="http://javafx.com/javafx/8" xmlns:fx="http://javafx.com/fxml/1" fx:controller="com.dtc.album.AlbumController">
<top><HBox alignment="CENTER" BorderPane.alignment="CENTER">
<children><Label text="My Photos">
<styleClass>
<String fx:value="label" />
<String fx:value="my_label" />
</styleClass></Label>
</children>
<padding>
<Insets bottom="10.0" top="10.0" />
</padding></HBox>
</top>
<bottom><HBox alignment="CENTER" spacing="10.0">
<children><Button fx:id="" mnemonicParsing="false" onAction="#previous" text="&lt; Previous">
<styleClass>
<String fx:value="button" />
<String fx:value="my_button" />
</styleClass></Button><Button fx:id="" mnemonicParsing="false" onAction="#next" text="Next &gt;">
<styleClass>
<String fx:value="button" />
<String fx:value="my_button" />
</styleClass></Button>
</children>
<padding>
<Insets bottom="10.0" top="10.0" />
</padding></HBox>
</bottom>
<center><StackPane id="" fx:id="container" prefHeight="420.0" prefWidth="580.0">
<BorderPane.margin>
<Insets left="20.0" right="20.0" />
</BorderPane.margin></StackPane>
</center></BorderPane>


Controller Class အား ရေးသားခြင်း

AlbumController.java အား အောက်ပါအတိုင်း ရေးသားပါမည်။
package com.dtc.album;

import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.file.Files;
import java.nio.file.LinkOption;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.Arrays;
import java.util.ListIterator;
import java.util.ResourceBundle;
import java.util.stream.Collectors;

import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.StackPane;
import javafx.stage.DirectoryChooser;

public class AlbumController implements Initializable {

 @FXML
 private StackPane container;

 private Path photoDirectory;
 private ListIterator<ImageView> photos;

 @Override
 public void initialize(URL location, ResourceBundle resources) {
  try {
   this.loadPhoto();
  } catch (Exception e) {
   this.handleError();
  }
 }

 public void next() {
  try {
   if (this.photos.hasNext()) {
     this.setImageView(this.photos.next());
   } else {
    this.showMessage("There is no images to next");
    this.photos.previous();
   }
  } catch (Exception e) {
   this.handleError();
  }
 }

 public void previous() {
  try {
   if (this.photos.hasPrevious()) {
     this.setImageView(this.photos.previous());
   } else {
    this.showMessage("There is no images to next");
    this.photos.next();
   }
  } catch (Exception e) {
   this.handleError();
  }
 }

 private void loadPhoto() throws IOException {

  // configuration file
  Path conf = Paths.get(getClass().getSimpleName() + ".txt");
  if (Files.exists(conf, LinkOption.NOFOLLOW_LINKS)) {
   Files.readAllLines(conf).forEach(
     (x) -> this.photoDirectory = Paths.get(x));
  }
  // no configuration file (first time only)
  else {
   DirectoryChooser fc = new DirectoryChooser();
   fc.setInitialDirectory(new File(System.getProperty("user.home")
     + "\\Pictures"));
   fc.setTitle("Photos Directory");
   File f = fc.showDialog(null);

   if (null != f) {
    this.photoDirectory = f.toPath();
    Files.write(conf,
      Arrays.asList(this.photoDirectory.toString()),
      StandardOpenOption.CREATE_NEW);
   }
  }

  if (null != this.photoDirectory
    && Files.isDirectory(photoDirectory, LinkOption.NOFOLLOW_LINKS)) {

   // load photos as imageview
   this.photos = Files
     .list(photoDirectory)
     .filter(x -> x.toString().toLowerCase().endsWith("jpg")
       || x.toString().toLowerCase().endsWith("png")
       || x.toString().toLowerCase().endsWith("gif"))
     .map(AlbumController.this::getImageView)
     .collect(Collectors.toList()).listIterator();

   // set first image
   if (this.photos.hasNext()) {
    this.setImageView(this.photos.next());
   }
  } else {
   this.showMessage("There is no photos to show. Please restart again.");
   this.showCloseBtn();
  }
 }

 private ImageView getImageView(Path path) {
  try {
   Image image = new Image(Files.newInputStream(path,
     StandardOpenOption.READ));
   ImageView view = new ImageView(image);
   view.setPreserveRatio(true);
   view.setFitWidth(this.container.getPrefWidth());
   view.setFitHeight(this.container.getPrefHeight());

   return view;
  } catch (Exception e) {
   this.handleError();
  }
  return null;
 }

 private void handleError() {
  this.container.getChildren().clear();
  this.showMessage("There is an error. Please restart again.");
  this.showCloseBtn();
 }

 private void setImageView(ImageView view) throws IOException {
  this.container.getChildren().clear();
  this.container.getChildren().add(view);
 }

 private void showMessage(String message) {
  Label l = new Label(message);
  l.getStyleClass().add("message");
  this.container.getChildren().add(l);
 }

 private void showCloseBtn() {
  Button btn = new Button("OK");
  btn.getStyleClass().add("ok_btn");
  btn.setOnAction(e -> System.exit(0));
  this.container.getChildren().add(btn);
 }

}
AlbumController Class အဓိက အလုပ်လုပ်သော Class ဖြစ်ပါသည်။ ဤအခန်းတွင် CSS အကြောင်းကို ဖော်ပြလိုသောကြောင့် Private Method များကိုတော့ အသေးစိတ် ရှင်းပြတော့မည် မဟုတ်ပါ။

AlbumController တွင် အဓိကပါဝင်သည်မှာ Member အနေနှင့် ဓာတ်ပုံတွေကို ဖော်ပြမည့် StackPane Object တစ်ခု၊ Photo များကို သိမ်းပေးထားနိုင်မည့် ListIterator Object တစ်ခုနှင့် Photo တွေကိုထားထားသည့် Directory အားကိုယ်စားပြုသည့် Path Object တစ်ခုတို့ ဖြစ်ကြပါသည်။ Public Method အနေနှင့် Application စစခြင်းလုပ်မည့် initialize, next နှင့် previous တို့သာရှိကြပါသည်။

initialize method အတွင်းတွင် loadPhoto private method အား ခေါ်ယူနေပြီး၊ ၎င်းအတွင်းတွင် configuration file အား ရှာဖတ်၍ ၎င်းအတွင်းမှ path name ဖြင့် Member ဖြစ်သည့် Path Object တွင် အစားထိုးပါသည်။ Configuration File မတွေ့ရှိခဲ့ပါက DirectoryChooser ဖြင့် အသုံးပြုမည့် Directory အား ရွေးချယ်ခိုင်းကာ ၎င်းအား Path Object တွင် အစားထိုး၍ အဆိုပါ Path အား Configuration File တွင် သိမ်းဆည်းစေပါသည်။

ထို့နောက် Path Object အတွင်းမှ Photo File များအား ImageView အဖြစ်ပြောင်းကာ ListIterator တွင် သိမ်းဆည်းထားစေပါသည်။ ဤနေရာတွင် Java 8 ၏ Stream API အား အသုံးပြုထားသောကြောင့် တစ်ကြောင်းတည်းနှင့် ရေးသားနိုင်ပါသည်။

next method သည် Next Button အား နှိပ်သည့်အခါတွင် အလုပ်လုပ်မည့် Method ဖြစ်ပြီး၊ ListIterator အတွင်း နောက်တစ်ပုံရှိပါက နောက်တစ်ပုံအား StackPane တွင် ဖော်ပြပေးပါမည်။ မရှိပါက Message အား StackPane တွင် ဖော်ပြပါလိမ့်မည်။

previous method သည် Previous Button အား နှိပ်သည့်အခါတွင် အလုပ်လုပ်မည့် Method ဖြစ်ပြီး၊ ListIterator အတွင်း အရှေ့ပုံရှိပါက အရှေ့ပုံအား ဖော်ပြသွားမည် ဖြစ်ပါသည်။ မရှိပါက Message အား StackPane တွင် ဖော်ပြပါလိမ့်မည်။


CSS ဖိုင်ဖြင့် နေရာပြောင်းရမည့်နေရာများ



Application စစခြင်း၊ Directory Chooser Dialog အား Cancel လုပ်လိုက်သည့်အခါတွင် ထွက်ပေါ်လာမည့် Window ဖြစ်ပါသည်။ StackPane တွင် Label တစ်ခုနှင့် Button တစ်ခုအား ဒီအတိုင်း ဖြည့်စွက်ထားသောကြောင့် ဖြစ်သည်။ ၎င်းတို့အား CSS အား အသုံးပြု၍ နေရာပြောင်းပါမည်။

တဖန် ခေါင်းစဉ် စာလုံးသည်လည်း Default အတိုင်း ဖြစ်နေပါသေးသည်။ ထို့အပြင် Previous နှင့် Next Button တို့သည်လည်း မညီမညာဖြစ်နေသည်။ ၎င်းတို့အားလည်း CSS ကို အသုံးပြု၍ ပြုပြင်ရပါမည်။

အောက်ပါအတိုင်း application.css အား ပြုပြင်၍ ရေးသားကြည့်ပါမည်။
.my_button {
 -fx-pref-width: 120px;
}

.my_label {
 -fx-font-family: Century;
 -fx-font-size: 26px;
 -fx-effect: dropshadow(one-pass-box , black , 8 , 0.0 , 4 , 0) 
}

.message {
 -fx-translate-y: 100px;
 -fx-font-family: Century;
 -fx-font-size: 18px;
 -fx-text-fill: red;
}

.ok_btn {
 -fx-translate-y: 140px;
 -fx-pref-width: 120px;
}
ထို့နောက် Application အား စမ်းကြည့်သောအခါ အောက်ပါအတိုင်း ရရှိပါသည်။


Directory အား မှန်ကန်စွာရွေးပေးပြီးသောအခါ အောက်ပါအတိုင်းတွေ့ရပါသည်။


ဒီတစ်ခေါက် Blog ဖြင့် CSS အား အသုံးပြုပါက အလွယ်တကူ Style, Effect နှင့် Layout တို့အား ပြုပြင်နိုင်ကြောင်း နမှုနာအနေနှင့် ဖော်ပြခဲ့ပါသည်။ နောက်အကြိမ်များနှင့် Java FX ၏ Style များအကြောင်းကို ဖော်ပြသွားပါဦးမည်။

လေးစားစွာဖြင့်။
မင်းလွင်

December 20, 2013

Day 12 Bind (Part 3)

အပြန်အလှန် Bind လုပ်ခြင်း


ပြီးခဲ့သော ဘလောဂ်များဖြင့် Bind Function အား မိတ်ဆက်ခဲ့ရာတွင် Variable တစ်ခုချင်းအား တစ်ဘက်တည်းကနေသာ Bind လုပ်ခဲ့ပါသည်။ အကဲ၍ Bind လုပ်ထားသော Variable အား တန်ဖိုး ပြောင်းကြည့်ပါက မည်သို့ဖြစ်မည်နည်း။
    public static void main(String[] args) {
        IntegerProperty var1 = new SimpleIntegerProperty(0);
        IntegerProperty var2 = new SimpleIntegerProperty(0);
        
        var1.bind(var2);
        
        var1.set(10); // Runtime Exception
    }
အထက်မှ ကုဒ်များအား Run ကြည့်ပါက RuntimeException ကို ဖြစ်ပေါ်စေမည် ဖြစ်ပါသည်။ Bind လုပ်ထားသည့် Variable အား ဒီအတိုင်း တန်ဖိုးပြောင်း၍ မရပါ။

သို့ရာတွင်လည်း Variable များအား တစ်ဘက်နှင့် တဘက် အပြန်အလှန် Bind လုပ်လိုသည့် အခါလည်း ရှိပါမည်။ ထိုအခါမျိုးတွင် bindBidirectional method အား အသုံးပြုရပါမည်။
package com.dtc.bidirection;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.TextArea;
import javafx.stage.Stage;

public class BidirectionalBind extends Application implements Initializable {

    @FXML
    private TextArea text1;
    @FXML
    private TextArea text2;

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene((Parent) FXMLLoader.load(getClass()
                .getResource(getClass().getSimpleName() + ".fxml"))));
        stage.show();
    }

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

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        text1.textProperty().bindBidirectional(text2.textProperty());
    }
}
အထက်ကုဒ်စာကြောင်း ၃၅တွင် ဖော်ပြထားသကဲ့သို့ text1 အား bindBidirectional အား အသုံးပြု၍ text2 အား အပြန်အလှန် Bind လုပ်စေပါသည်။ ဤနည်းအားဖြင့် Text Area 1 တွင် တစ်ခုခု ပြောင်းပြီးရေးသားပါက Text Area 2 တွင် ဖော်ပြနိုင်သကဲ့သို့၊ Text Area 2 တွင် ပြောင်းလည်းမှု့ရှိပါက Text Area 1 တွင် ပြောင်းလည်းပေးနိုင်မည် ဖြစ်ပါသည်။



Low-Level Binding API

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

ထိုအခါမျိုးတွင် Low-Level Binding API အား အသုံးပြုနိုင်ပါသည်။ Binding Class ၏ computeValue method အား Override လုပ်၍ ရေးသားခြင်း ဖြစ်ပါသည်။

နမှုနာ အနေနှင့် Rectangle တစ်ခု၏ အရောင်အား၊ Slider 3 ခု၏ တန်ဖိုးဖြင့် ပြောင်းလည်းသည့် အပလီတစ်ခုအား ရေးသားကြည့်ပါမည်။
package com.dtc.low;

import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Application;
import javafx.beans.binding.ObjectBinding;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Slider;
import javafx.scene.paint.Color;
import javafx.scene.shape.Rectangle;
import javafx.stage.Stage;

public class LowLevelTest extends Application implements Initializable {

    @FXML
    private Rectangle shape;
    @FXML
    private Slider slider1, slider2, slider3;

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        shape.fillProperty().bind(new ObjectBinding<Color>() {

            {
                super.bind(slider1.valueProperty(), slider2.valueProperty(),
                        slider3.valueProperty());
            }

            @Override
            protected Color computeValue() {
                return Color.color(slider1.valueProperty().get(), slider2
                        .valueProperty().get(), slider3.valueProperty().get());
            }
        });
    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene((Parent) FXMLLoader.load(getClass()
                .getResource(getClass().getSimpleName() + ".fxml"))));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
အထက်ပါ နမှုနာထဲတွင် Rectangle ၏ fillProperty အား ObjectBinding Object နှင့် လှမ်း Bind ထားပါသည်။ fillProperty မှာ Color Object ဖြစ်သောကြောင့် ObjectBinding ၏ Generics Type အား Color အား အသုံးပြုပါသည်။ အဲ့ဒီအတွက် Override လုပ်သည့် computeValue Method အတွင်းတွင် Slider များ၏ တန်ဖိုးဖြင့် Color Object အား တည်ဆောက်၍ Return ပြန်လုပ်ခြင်း ဖြစ်ပါသည်။


အထက်ပါ နမှုနာအား Run ကြည့်သောအခါ Slider များ၏ တန်ဖိုးအား ပြောင်းကြည့်သည့်အခါ အရောင်သည်လည်း ၎င်းတန်ဖိုးများနှင့် အညီ လိုက်၍ ပြောင်းလည်းသည်ကို တွေ့ရပါသည်။

ဤ JavaFX Days ခေါင်းစဉ်ဖြင့် JavaFX ၏ Property နှင့် Binding များအကြောင်းကို ၄ကြိမ်ဆက်တိုက် ဖော်ပြခဲ့ပါသည်။ Binding Function အား အသုံးပြု၍ လွယ်ကူသော Binding များမှအစ၊ နှစ်သက်သလို Object များနှင့် Bind လုပ်နိုင်ပြီး၊ User နှင့် Interactive ဖြစ်သော GUI Application များအား ရေးသားနိုင်ပါလိမ့်မည်။

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

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 အကြောင်းကို မိတ်ဆက်ပါတော့မယ်။

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

December 16, 2013

Day 10 Bind (Part 1)

Bind


Java FX ရဲ့ ထူးခြားချက်များတွင် UI Component များကြီးသာမဟုတ်။ လုပ်ဆောင်မှု့ Architecture အပိုင်းမှာလည်း ထူးခြားမှု့များပါလာပါသည်။ ၎င်းမှာ Bind Function ဖြစ်၏။ Bind Function သည် Property အချင်းချင်းအား အလိုအလျှောက် Synchronize လုပ်စေနိုင်သော ဖန်ရှင်ဖြစ်ပါသည်။ ထို့အတွက် Bind အကြောင်း မဖော်ပြခင် Property အား ပြီးခဲ့သော ဘလောဂ်ဖြင့် ဖော်ပြခဲ့ပါသည်။
public class Test {

    public static void main(String[] args) {
        IntegerProperty x = new SimpleIntegerProperty();
        IntegerProperty y = new SimpleIntegerProperty(0);
        
        x.bind(y);
        System.out.println(x.get()); // out value is 0

        y.set(10);
        System.out.println(x.get()); // out value is 10
        
        y.set(20);
        System.out.println(x.get()); // out value is 20
    }
}

အထက်ပါ ကုဒ်၏ စာကြောင်းနံပါတ် ၇ တွင် x variable အား y နှင့် bind လုပ်စေပါသည်။ ဤနည်းအားဖြင့် y တန်ဖိုးအားပြောင်းလည်းသည်နှင့် x တန်ဖိုးတွင်လည်း ပြောင်းလည်းသွားမည် ဖြစ်ပါသည်။

သို့သော်လည်း လက်တွေ့မှာမှု့  x ၏ တန်ဖိုးပြောင်းလည်းမှု့မှာ y နှင့် အချိန်တူမဟုတ်ပါ။ အနည်းငယ် နောက်ကျ၍ ပြောင်းလည်းမည် ဖြစ်ပါသည်။ x ၏ တန်ဖိုးအား ခေါ်ယူသည့်အခါတွင် x ၏ တန်ဖိုးအား ပြောင်းပေးမည် ဖြစ်ပါသည်။

အထက်ပါ ကုဒ်များ၏ စာကြောင်း ၁၀တွင် y ၏ တန်ဖိုးအား ၁၀ ဟုပြောင်းလည်းပါသည်။ သို့သော်လည်း x ၏ တန်ဖိုးသည် ဤနေရာတွင် ပြောင်းလည်းသွားသေးသည် မဟုတ်။ စာကြောင်း ၁၁အရောက် x ၏ တန်ဖိုးအား ခေါ်ဆိုသည့်အခါတွင် တန်ဖိုးပြောင်းလည်းသွားခြင်းပင် ဖြစ်၏။



IvalidationListener


ဤကဲ့သို့ Bind လုပ်ထားသော Variable ၏ တန်ဖိုးအား သိရှိစေနိုင်သည်မှာ javafx.beans.InvalidationListener Interface ပင် ဖြစ်၏။
public class Test {

    public static void main(String[] args) {
        IntegerProperty x = new SimpleIntegerProperty();
        IntegerProperty y = new SimpleIntegerProperty(0);
        
        x.bind(y);
        x.addListener(new InvalidationListener() {
            
            @Override
            public void invalidated(Observable o) {
                System.out.println("Invalidated : " + o);
            }
        });
        System.out.println(x.get()); 

        y.set(10);
        y.set(20);
        System.out.println(x.get()); 
    }
}
အထက် ပါ ကုဒ်များတွင် စာကြောင်းနံပါတ် ၈ တွင် x ၏ တန်ဖိုးပြောင်းလည်းပါက အလုပ်လုပ်နိုင်သည့် InvalidateListener အား ဖြည့်စွက် ရေးသားထားပါသည်။ ဤနည်းအားဖြင့် x ၏ တန်ဖိုး ပြောင်းလည်း သွားချိန်တွင် invalidate method အား ခေါ်ဆိုမည် ဖြစ်ပါသည်။

အဆိုပါ ပရိုဂရမ်အား Run ကြည့်သောအခါ အောက်ပါအတိုင်း အဖြေရရှိပါသည်။
0
Invalidated : IntegerProperty [bound, invalid]
20
ကုဒ်ထဲတွင် စာကြောင်း ၁၇ နှင့် ၁၈တို့တွင် y ၏ တန်ဖိုးအား ၂ကြိမ် ပြောင်းလည်းခဲ့သော်လည်း လက်တွေ့မှာမူ invalidate အား တစ်ကြိမ်သာ ခေါ်ဆိုသည်ကို တွေ့ရပါသည်။ စာကြောင်း ၁၉ အရောက်တွင် x.get အား ခေါ်ဆိုသည့်အခါတွင် invalidate အား တစ်ကြိမ်သာ ခေါ်ဆိုသောကြောင့် ဖြစ်ပါသည်။



Bind With UI Component


ကျွှန်တော်တို့  Bind Function အား UI Component များနှင့် တွဲပြီး နောက်ထပ်နမှုနာလေးတစ်ခု ရေးကြည့်ပါဦးမည်။

ပထမဦးဆုံး ScenBuilder အား အသုံးပြု၍ အောက်ပါ အတိုင်း Controller.fxml အား ရေးသားပါမည်။
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import javafx.scene.chart.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.effect.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.text.*?>

<AnchorPane prefHeight="481.0" prefWidth="656.0"
    xmlns:fx="http://javafx.com/fxml/1" xmlns="http://javafx.com/javafx/2.2"
    fx:controller="com.dtc.bind.Controller">
    <children>
        <Label layoutX="21.0" layoutY="24.0" text="Bind Tutorial"
            textOverrun="CENTER_ELLIPSIS">
            <effect>
                <DropShadow height="5.059523809523809" offsetX="2.0"
                    offsetY="4.0" radius="3.2946428571428568" spread="0.19841269841269843"
                    width="10.119047619047619" />
            </effect>
            <font>
                <Font name="Bleucher" size="30.0" />
            </font>
        </Label>
        <PieChart id="PieChart" fx:id="chart" labelsVisible="false"
            layoutX="21.0" layoutY="60.0" prefHeight="369.0" prefWidth="457.0"
            visible="true" />
        <Slider fx:id="slideB" layoutX="574.0" layoutY="66.0"
            orientation="VERTICAL" prefHeight="356.0" showTickLabels="true"
            showTickMarks="true" />
        <Slider fx:id="slideA" layoutX="508.0" layoutY="66.0"
            orientation="VERTICAL" prefHeight="356.0" showTickLabels="true"
            showTickMarks="true" />
        <Label layoutX="502.0" layoutY="434.0" text="Data A" />
        <Label layoutX="560.0" layoutY="434.0" text="Data B" />
    </children>
</AnchorPane>

အတွင်းတွင် ခေါင်းစဉ် Label ၃ခု၊ PieChart တစ်ခု နှင့် VerticalSlideBar နှစ်ခု အား ရေးသားထားပါသည်။ ပြီးပါက Controller.java အား ဆက်၍ရေသားပါမည်။
package com.dtc.bind;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Application;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.chart.PieChart;
import javafx.scene.control.Slider;
import javafx.stage.Stage;

public class Controller extends Application implements Initializable {

    @FXML
    private Slider slideA;
    @FXML
    private Slider slideB;
    @FXML
    private PieChart chart;

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        
        chart.getData().addAll(new PieChart.Data("Data A", 0),
                new PieChart.Data("Data B", 0),
                new PieChart.Data("Data C", 20));
        
        slideA.valueProperty().set(30);
        slideB.valueProperty().set(10);;
        
        chart.getData().get(0).pieValueProperty().bind(slideA.valueProperty());
        chart.getData().get(1).pieValueProperty().bind(slideB.valueProperty());
    }

    @Override
    public void start(Stage stage) throws IOException {
        stage.setScene(new Scene((Parent) FXMLLoader.load(getClass()
                .getResource(getClass().getSimpleName() + ".fxml"))));
        stage.show();
    }

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

}

အထက်ပါ ပရိုဂရမ်အတွင်းတွင် စာကြောင်း ၂၉ မှ ၃၁ အထိ PieChart ၏ Data များကို ဖြည့်စွက်နေပါသည်။ PieChart တွင် အသုံးပြုသော Data မှာ ObservableList ဖြစ်ပြီး ၎င်းအတွင်းတွင် PieChart.Data အား အသုံးပြုပြီး၊ Type Parameter အနေနှင့် String နှင့် Number အား အသုံးပြုပါသည်။ ထို့ကြောင့် စာကြောင်း ၂၉ တွင် PieChart#getData ဖြင့် ObservableList အား ခေါ်ယူ၍ ၎င်း၏ addAll function ဖြင့် အသုံးပြုမည့် Data များအား ဖြည့်စွက်နေပါသည်။ ဤနေရာသည် PieChart အတွက် Data များကို ပြင်ဆင်နေသည်သာ ဖြစ်၏။

Bind လုပ်နေသည်မှာ စာကြောင်း ၃၆ နှင့် ၃၇ ဖြစ်ပါသည်။ PieChart၏ Data အတွင်းမှ ပထမဦးဆုံး တန်ဖိုးအား Slider A ၏ တန်ဖိုးနှင့် Bind လုပ်ပြီး၊ ဒုတီယမြောက် Data ၏ တန်ဖိုးအား Slider B ၏ တန်ဖိုးနှင့် Bind လုပ်ထားပါသည်။ ဤနည်းအားဖြင့် Slider A ၏ တန်ဖိုး ပြောင်းလည်းသွားပါက၊ PieChart ၏ ပထမ တန်ဖိုးလည်း ပြောင်းလည်းမည်ဖြစ်ပြီး၊ Slider B ၏ တန်ဖိုးပြောင်းလည်းပါက PieChart ၏ ဒုတိယ တန်ဖိုးလည်း ပြောင်းလည်းမည် ဖြစ်ပါသည်။ လက်တွေ့ စမ်းကြည့်ပါမည်။


အထက်ဖော်ပြပါအတိုင်း Slider အား ရွှေ့ကြည့်သည်နှင့် PieChart အတွင်းရှိ တန်ဖိုးများလည်း ပြောင်းလည်းသွားသည်ကို တွေ့ရပါသည်။

ယခင်တစ်ခေါက် Property အား လေ့လာခဲ့စဉ်က User Input အပြင် Data ပြောင်းသွားတာကို သိနိုင်တဲ့ ထူးခြားချက်က ရှိခဲ့ပေမယ့်၊ Event ကို Catch လုပ်ပြီး ရေးနေရတဲ့အတွက် သိပ်ပြီး ထူးခြားတယ် လို့မထင်ခဲ့မိပါ။ သို့ရာတွင် ယခုတစ်ခေါက် Bind တွင်မှာမူ တကူးတက EventListener ကို ရေးထားစရာမလိုပဲ Bind လုပ်ထားရုံနဲ့ အခြားသော Variable တစ်ခုရဲ့ တန်ဖိုးနဲ့ တွဲပေးနိုင်တဲ့ အချက်ဟာ အတော်လေးကို ကွာခြားပါတယ်။

Ajax မှာတောင် Asynchronous ကို သုံးခဲ့ရင်တောင်မှ၊ အနည်းငယ်နောက်ကျပြီးအလုပ်လုပ်ပေမယ့် Java FX ရဲ့ Bind မှာတော့ အသုံးပြုသူက တစ်ခုခုလုပ်လိုက်တာနဲ့ Application က ချက်ချင်း တုံ့ပြန်နိုင်တာကို တွေ့ရပါတယ်။ Bind ဖန်ရှင်ကိုသုံးပြီး၊ User နဲ့ Interactive ဖြစ်တဲ့ Application တွေကို ရေးသားနိုင်လိမ့်မယ် လို့ မျှော်လင့်ပါတယ်။

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

December 14, 2013

Day 9 JavaFX ၏ Property Class

ပြီးခဲ့သော အခန်းများအထိ Java FX နဲ့ပတ်သက်ပြီး View Component တွေရဲ့ အသုံးပြုပုံကို အဓိကထားဖော်ပြခဲ့၏။ View နှင့်ပတ်သက်ပြီး ဖော်ပြရန် အတော်ကျန်နေသေးသော်လည်း ယခုတစ်ခေါက်တွင် View ကနေ ခဏခွဲခွာပြီး Function ပိုင်းဆိုင်ရာ ဘက်ကို ခေတ္တရေးသားပါဦးမည်။

GUI အပလီကေးရှင်းများတွင် MVC Pattern အား အသုံးပြု၍ ရေးသားရာတွင် View တွင် ဖော်ပြမည့် အချက်အလက်များအား Model တွင် သိမ်းဆည်းထားလေ့ရှိ၏။ လက်တွေ့ ရေးကြည့်ရာတွင် Model အား View တွင် ဖော်ပြရသည်မှာ လွယ်သည့်ကိစ္စတစ်ခု တော့မဟုတ်ပါ။

ဤနေရာတွင် အသုံးဝင်သည်မှာ ယခုတစ်ခေါက် ဖော်ပြမည့် Java FX ၏ Binding Function ပင် ဖြစ်၏။

Binding သည် Variable နှစ်ခုအား Synchronize လုပ်ပေးနိုင်သည့် Function တစ်ခု ဖြစ်ပါသည်။ Variable တစ်ခုအား အခြားသော Variable နှင့် Bind လုပ်ထားပါက၊ Variable တစ်ခု၏ တန်ဖိုး ပြောင်းလည်းသွားသည်နှင့် အလိုအလျှောက် အခြားသော Variable ၏ တန်ဖိုးသည်လည်း ပြောင်းလည်းသွားမည် ဖြစ်ပါသည်။ သို့ရာတွင် Binding အား Variable အားလုံးတွင် အသုံးပြုနိုင်သည် မဟုတ်ပါ။ Binding အား အသုံးပြုနိုင်သော Variable များမှာ ယခုတစ်ခေါက် ဖော်ပြမည့် Property Variable များပင် ဖြစ်ကြ၏။

Property


Java ဘာသာရပ်တွင် Property များအား ဖော်ပြရန် Java Beans အား အသုံးပြုလာခဲ့ကြ၏။ အလားတူပင် JavaFX ၏ Property သည်လည်း Java Beans Technology အား အခြေခံထားပါသည်။ Jaba Beans အား အသုံးပြုပါက လိုအပ်သလို Java Beans အား ကိုယ်တိုင်ရေးသား ရပါသည်။ Java FX တွင်လည်း JavaFX မှ ပံ့ပိုးပေးထားသော Property Class အား အသုံးပြုနိုင်ပါသည်။

Property Class များအား javafx.bean.property Package အောက်တွင် ထားရှိပါသည်။ ဥပမာအားဖြင့် IntegerProperty ကဲ့သို့သော Primitive Data များအတွက် Property Class များလည်းရှိသကဲ့သို့၊ StringProperty ကဲ့သို့ String အား အသုံးပြုနိုင်သော Class နှင့် နှစ်သက်ရာ Object များကို အသုံးပြုနိုင်သော ObjectProperty Class များလည်း ရှိကြပါသည်။  အဆိုပါ Class များတွင် getter နှင့် setter method များကို ပိုင်ဆိုင်ပြီး၊ တန်ဖိုးများအား get နှင့် set လုပ်ဆောင်နိုင်ပါသည်။ သို့ရာတွင် ထို Class များသည် Abstract Class များဖြစ်ကြသောကြောင့် Instance များအား တိုက်ရိုက် ပြုလုပ်နိုင်ခြင်းမရှိပါ။ အသုံးပြုလိုပါက SimpleIntegerProperty နှင့် SimpleStringProperty ကဲ့သို့သော Sub Class များမှတဆင့် အသုံးပြုနိုင်ပါသည်။

လက်တွေ့ ရေးသားကြည့်ပါမည်။

    public static void main(String[] args) {
        // Integer Property
        IntegerProperty variable = new SimpleIntegerProperty(10);
        System.out.println(variable.get());
        
        // change property value
        variable.set(20);
        System.out.println(variable.get());
    }

အဘယ်ကြောင့် ရိုးရိုး Variable အား အသုံးမပြုပဲ Property Variable များအား အသုံးပြုနေပါသနည်း။ အကြောင်းရှိပါသည်။  ပထမ အဖြေမှာ ပြောင်းလည်းမှု့အား သိရှိစေနိုင်ခြင်း ဖြစ်ပြီး၊ နောက်အဖြေတစ်ခုမှာ Binding ဖြစ်၏။

GUI အပလီကေးရှင်းများတွင် ပြောင်းလည်းမှု့များအား သိရှိခြင်းသည် လွန်စွာမှ အရေးကြီးလှပါသည်။ Model ၏ တန်ဖိုးပြောင်းလည်းသွားပါက View တွင် ချက်ချင်းဖော်ပြရန် လိုအပ်သလို၊ အသုံးပြုသူ၏ လုပ်ဆောင်မှု့ကြောင့် View ၏ Input တန်ဖိုး ပြောင်းလည်းသွားပါက Model အား ချက်ချင်း ပြောင်လည်း ပေးရန် လိုအပ်ပါသည်။ ဤကဲ့သို့ ပြောင်းလည်းမှု့အား သိရှိစေရန်အတွက် Property အား အသုံးပြုရပါသည်။ Property ၏ တန်ဖိုးပြောင်းလည်းသွားပါက Property Object သည် Event အား ဖြစ်ပေါ်စေပြီး၊ ထို Event အား လုပ်ဆောင်စေနိုင်သည်မှာ ChangeListener Object များပင် ဖြစ်၏။

    public static void main(String[] args) {
        // Integer Property
        IntegerProperty variable = new SimpleIntegerProperty(10);
        
        variable.addListener(new ChangeListener<Number>() {

            @Override
            public void changed(ObservableValue<? extends Number> oveservable,
                    Number oldValue, Number newValue) {
                System.out.println("Ovservable Value : " + oveservable.getValue());
                System.out.println("Old Value        : " + oldValue);
                System.out.println("New Value        : " + newValue);
            }

        });
        
        // change property value
        variable.set(20);
        variable.set(30);
    }
ChangeListener Object အား Property တွင် အသုံးပြုလိုပါက addListener method အား အသုံးပြုရပါသည်။ အကြောင်းတစ်မျိုးမျိုးကြောင့် Property ၏ တန်ဖိုးတွင် ပြောင်းလည်းမှု့ကို ဖြစ်ပေါ်စေပါက အသုံးပြုနေသော ChangeListener ၏ changed method ကို ခေါ်ယူမည် ဖြစ်သည်။

changed method တွင် Argument ၃ ခုပါဝင်ပြီး၊ ဒုတိယ Argument မှာ မပြောင်းလည်းခင်တန်ဖိုး ဖြစ်ပြီး၊ တတိယ Argument မှာ ပြောင်းလည်းပြီး တန်ဖိုး ဖြစ်ပါသည်။ ပထမဦးဆုံး Argument မှာ ObservableValue Interface ဖြစ်ပြီး၊ အသုံးပြုနေသော Property Object ကိုယ်တိုင်ပင် ဖြစ်၏။ Property များသည် ObservableValue Interface အား Inplements လုပ်ထားသောကြောင့် ဤကဲ့သို့ အသုံးပြုနိုင်ခြင်း ဖြစ်ပါသည်။

တဖန် Property တန်ဖိုးများအား အသုံးပြု၍ View Component များအား ရေးသားကြည့်ပါဦးမည်။

package com.dtc.jfx.property;

import java.io.IOException;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.application.Application;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.stage.Stage;

public class Main extends Application implements Initializable {

    @FXML
    private Slider slider;
    @FXML
    private Label label;

    @Override
    public void start(Stage primaryStage) throws IOException {
        primaryStage.setScene(new Scene((Parent) FXMLLoader.load(getClass()
                .getResource("Main.fxml"))));
        
        primaryStage.setTitle("Property Test");
        
        primaryStage.show();
    }

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        slider.valueProperty().addListener(new ChangeListener<Number>() {
            @Override
            public void changed(ObservableValue<? extends Number> arg0,
                    Number arg1, Number arg2) {
                label.setText(String.valueOf(arg2));
            }
        });
    }

    public static void main(String[] args) {
        launch(args);
    }
}
လိုင်း နံပါတ် ၃၈ အားကြည့်ပါ။ Slider Object ၏ propertyValue အား ခေါ်ယူ၍ DoubleProperty Object အား ရယူပါသည်။ ထို့နောက် addListener method အား အသုံးပြု၍ ChangeListener အား ဖြည့်စွက်ပါသည်။ တဖန် ChangeListener ၏ changed method အတွင်းမှာမူ Lable Objecct ၏ တန်ဖိုးအား ပြောင်းလည်းသွားသည့် တန်ဖိုးဖြင့် ဖော်ပြစေနေပါသည်။

အဆိုပါ ပရိုဂရမ်အား အလုပ်လုပ်ခိုင်းကြည့်သည့် အခါ အောက်ပါအတိုင်း Slider အား ပြောင်းရွှေ့လိုက်သည့်အခါတိုင်း Lable ၏ တန်ဖိုးလည်း လိုက်၍ ပြောင်းသည်ကို တွေ့ရမည် ဖြစ်ပါသည်။




ကိုယ်ပိုင် Property Class များအား ရေးသားချင်း

အထက်တွင် ဖော်ပြသကဲ့သို့ပင် JavaFX မှ ပံ့ပိုးထားသော Property များအား အသုံးပြုနိုင်သကဲ့သို့ပင် ကိုယ်ပိုင် Custom Property Class များအား ရေးသားအသုံးပြုနိုင်ပါသည်။ ဤကဲ့သို့ ရေးသားရာတွင် လိုက်နာရန် စီးကမ်းများ ရှိကြပါသည်။

  • Member Filed များအား Property များကို သာအသုံးပြုနိုင်ပါသည်
  • getter နှင့် setter များမှာလည်း Java Beans Technology ၏ Naming Rule အား လိုက်နာရန် လိုအပ်ပြီး၊ Property ၏ တန်ဖိုးများအား Get Set လုပ်နိုင်မည် ဖြစ်သည်
  • Property Member များအား ရယူလိုသည့်အခါ variableProperty ဟု အမည်ပေးရန် လိုအပ်ပါသည်
package propertydemo;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleDoubleProperty;

class Bill {
    // Define a variable to store the property
    private DoubleProperty amountDue 
        = new SimpleDoubleProperty();
    
    // Define a getter for the property's value
    public final double getAmountDue(){
        return amountDue.get();
    }
    
    // Define a setter for the property's value
    public final void setAmountDue(double value){
        amountDue.set(value);
    }
    
    // Define a getter for the property itself
    public DoubleProperty amountDueProperty() {
        return amountDue;
    }
}

အထက်ပါအတိုင်း မိမိ၏ ကိုယ်ပိုင် Property Object များအား ရေးသားနိုင်ပါသည်။ JavaFX ၏ Standard API များတွင်လည်း အထက်ပါစီးမျဉ်းအား လိုက်နာ၍ ရေးသားထားပါသည်။


Property အား အသုံးပြုနိုင်သော Collection

JavaFX တွင် အသုံးပြုသော Collection များသည်လည်း Property များကဲ့သို့ပင် ပြောင်းလည်းမှု့အား သိရှိနိုင်ပါသည်။ JavaFX တွင် အသုံးပြုနိုင်သော Collection များမှာ ObservableList, ObservableMap နှင့် ObservableSet တို့ ဖြစ်ကြ၏။ အသုံးပြုပုံတို့မှာ အခြေခံအားဖြင့် သာမန် Collection များနှင့် အတူတူပင် ဖြစ်၏။ ထူးခြားသည်မှာ ၎င်းတို့အတွက် Implements လုပ်ထားသော Class များ ပံ့ပိုးထားခြင်းမရှိဆိုသည့် အချက်ပင် ဖြစ်၏။ ထို့ကြောင့် သာမန် Collection များကဲ့သို့ new လုပ်၍ မရခြင်း ပင်ဖြစ်၏။

JavaFX ၏ Collection များအား ရယူလိုပါက Utility Class ဖြစ်သော FXCollections Class မှ တဆင့် အသုံးပြုရမည် ဖြစ်ပါသည်။ ၎င်း၏ observableList, observableArrayList, observableMap နှင့် observableSet တို့အား အသုံးပြု၍ JavaFX Collection Object များအား ရယူနိုင်မည် ဖြစ်ပါသည်။

Collection များ၏ Element တန်ဖိုးများပြောင်းလည်းခြင်းအား Property များကဲ့သို့ပင် သိရှိနိုင်မည် ဖြစ်ပါသည်။ ပြောင်းလည်းခြင်း Event များအား အလုပ်လုပ်စေရန် ChangeListener များအား သတ်မှတ် အသုံးပြုနိုင်ပါသည်။ Observable Collection များတွင် အသုံးပြုနိုင်သော Change Listener များမှာ ListChangeListener, MapChangeListener နှင့် SetChangeListener တို့ ပင် ဖြစ်ကြ၏။

package com.dtc.jfx.property;

import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener;
import javafx.collections.ObservableList;

public class CollectionTest {

    public static void main(String[] args) {
        ObservableList<Integer> list = FXCollections.observableArrayList(1,2,3);
        list.addListener(new ListChangeListener<Number>() {

            @Override
            public void onChanged(
                    javafx.collections.ListChangeListener.Change<? extends Number> change) {
                while(change.next()) {
                    if(change.wasAdded()) {
                        System.out.println("Add : " + change.getAddedSubList());
                    }
                    
                    if(change.wasRemoved()) {
                        System.out.println("Removed : " + change.getRemoved());
                    }
                }
            }
        });
        
        // single value
        list.add(4);
        list.remove(0);
        
        // multiple value
        list.addAll(5,6,7);
        list.removeAll(2,3);
        
    }
}
အထက်ပါ Source Code ထဲတွင် ObservableList အား FXCollections#observableArrayList အား အသုံးပြု၍ ရယူပါသည်။ တဖန် ChangeListener အဖြစ် ListChangeListener အား အသုံးပြုပါသည်။ Collection ၏ Element များ အပြောင်းအလည်းရှိပါက onChange method အား ခေါ်ယူမည် ဖြစ်သည့်အတွက် လုပ်ဆောင်ချက်များကို ထို Method ထဲတွင် ရေးသားရပါမည်။

onChange method ၏ Argument သည် ListChangeListener.Change Class ဖြစ်ပြီး၊ ၎င်းတွင် ပြောင်းလည်းမှု့အား သိရှိစေနိုင်သော wasAdded, wasRemoved, wasReplased, wasUpdated နှင့် wasPermuted လုပ်ဆောင်ချက်များအား ပြင်ဆင်ထားပါသည်။ တဖန် ပြောင်းလည်းမှု့တန်ဖိုးများအား ရယူနိုင်ရန် အတွက် getAddedSubList နှင့် getRemoved အစရှိသည့် Method များအား ပြင်ဆင်ထားပါသည်။

အထက်ပါ Program အား Run ကြည့်သောအခါ အောက်ပါအတိုင်း အဖြေရမည် ဖြစ်ပါသည်။
Add : [4]
Removed : [1]
Add : [5, 6, 7]
Removed : [2, 3]
ယခု အခေါက်တွင် JavaFX ၏ အဓိက ဖန်ရှင်တစ်ခု ဖြစ်သော Property အကြောင်းကို ရေးသားဖော်ပြခဲ့၏။ Java ဘာသာရပ်အတွက် အလွန်အသုံးဝင်သော Function တစ်မျိုး ဖြစ်ပါသည်။ Property ၏ အရည်အသွေး အပြည့်အဝ အသုံးပြုရန်မှာ နောက်တစ်ကြိမ်ဖော်ပြရန် ရည်ရွယ်ထားသော Binding တွင် ဖြစ်ပါသည်။ ကျွှန်တော်ကိုယ်တိုင်လည်း Property အား လေ့လာရင်း ဤ Function သည် လွန်စွာမှ အသုံးဝင်သော ဖန်ရှင်တစ်ခု ဖြစ်သည်ဟု သဘောပေါက်မိပါသည်။ ဆက်ပါဦးမည်။

လေးစားစွာဖြင့်
မင်းလွင်

December 13, 2013

Day 8 TableView

အခုတလော အခြားအလုပ်တစ်ခုနဲ့ အလုပ်တွေအရမ်းများနေခဲ့တဲ့အတွက် ပရိုဂရမ်တောင် သိပ်ပြီးမရေး ဖြစ်ပါ။ ပြီးခဲ့တဲ့နေ့က နည်းနည်း အချိန်ရတာနဲ့ Java EE 7 ရဲ့ Injection အကြောင်းကို Java EE 7 General ခေါင်းစဉ်နဲ့ ရေးဖြစ်ခဲ့၏။ ဒီနေ့လည်း အချိန်နည်းနည်း ရလာပြန်သည်။ ဒါနဲ့ ဘာဆက်ရေးရင်ကောင်းမလဲ စဉ်းစားရင်း Java EE ရဲ့ JPA ကို စရင်ကောင်းမလား၊ Database လည်း အခြေခံကို ပြီးပြီဖြစ်တဲ့အတွက် Relational Data Modeling အကြောင်းကို စရင်ကောင်းမလား။ Component Base Software Science အကြောင်းကို ရေးရင်ကောင်းမလား စဉ်းစားနေခဲ့မိပါသည်။ ဒါနဲ့ ပြီးခဲ့တဲ့ ဘလောဂ်တွေပြန်ကြည့်ရင်း အား Java FX ဟာ ရေးစရာ တွေကျန်နေသေးပါလားဟု Java FX အကြောင်းကို ဆက်ရေးရန် ဆုံးဖြတ်ခဲ့ပါသည်။

ယခုဆိုလျှင် Java FX အကြောင်းရေးလာသည်မှာ ၈ ကြိမ်မြောက်ကိုရောက်လာပြီဖြစ်၏။ Control အကြောင်းမှာ အတော်လေးကို ကြာနေခဲ့ပါသည်။ Controls အကြောင်းကို ရေးမည်ဆိုလျှင် အများကြီးရေးရပေမည်။ အစကတည်းက အားလုံးကို ရေးရန် မလွယ်မှန်းသတိထားခဲ့မိ၍ Enterprise App တွေမှာ မရှိမဖြစ်လိုအပ်တဲ့၊ List, Combo နဲ့ Table ကို အသေးစိတ်ရေးပြီး၊ ကျန်တဲ့ Controls တွေကိုတော့ အခြားအခန်းတွေရေးရင်း မိတ်ဆက်သွားမည် ဟုစဉ်းစားထားခဲ့ပါသည်။ ယခုတခေါက်မှာတော့ TableView အကြောင်းကို ဆက်လက်ရေးသားသွားပါဦးမည်။

TableView

ယခင်ရေးသားခဲ့သော Controls များသည် Swing တွင်ရေးသားခဲ့သော Component များနှင့် သိပ်ပြီးကွာခြားလှသည် မဟုတ်ပေ။ သို့ရာတွင် TableView မှာမူ ယနေ့တိုင်းအသုံးပြုခဲ့သည့် JTable နှင့် များစွာကွာခြားလှပေသည်။

အဓိကကွာခြားသည်မှာ Data Model ပင်ဖြစ်၏။ JTable သည် အသုံးပြုမည့် Data များအား javax.swing.table.TableModel အနေဖြင့် အသုံးပြုရန် လိုအပ်ခဲ့၏။ ထို့အတွက် မိမိ၏ Data များအား Wrap လုပ်ရန် လိုအပ်ခဲ့ပေသည်။ သို့ရာတွင် JavaFX ၏ TableView မှာမူ Data Model အား အသုံးမပြုပဲ နှစ်သက်ရာ Object အား အသုံးပြုနိုင်၏။

နမှုနာ အနေဖြင့် Address Book အပလီ တစ်ခု ရေးကြည့်ပါမည်။ အမည်နှင့် လိပ်စာကို Input Form အတွင်း ရေးပြီး သိမ်းပါက ဖိုင်တွင် သွားသိမ်းထားပြီး၊ အပလီအား ပြန်ပြီး Run သည့်အခါ ဖိုင်အတွင်းမှ လိပ်စာ စာရင်းအား ခေါ်၍ Table အတွင်းတွင် ဖော်ပြမည့် အပလီ ဖြစ်ပါသည်။

ဦးစွာ Scene Builder အား အသုံးပြု၍ အသုံးပြုမည့် View အား ရေးသားကြည့်ပါမည်။

ဦးစွာ ခေါင်းစဉ် အဖြစ် Lable တစ်ခုအား နေရာချ၍ Text နေရာတွင် My First Address Book ဟု ရေးသားပါမည်။ ပြီးပါက Input များအား ရေးသားပါသည်။ Input များ အနေဖြင့် Name, Phone နှင့် Address အား ရေးသား ပါသည်။ တဖန် Save Button အား နေရာချပါသည်။ နောက်ဆုံးတွင် TableView တစ်ခုအား နေရာချ၍ အတွင်းတွင် TableColumn သုံးခုအား နေရာချကာ အသီးသီး အမည်များအား ပြောင်းလိုက်ပါသည်။ အားလုံးအား Drug And Drop ဖြင့်သာ ရေးသားသွားပါသည်။

ပြီးပါက Controller အဖြစ်အသုံးပြုရန် AddressBook Class အား Java FX Main Class အဖြစ် ရေးသားပါသည်။

public class AddressBook extends Application implements Initializable {

    @FXML
    private TextField name;
    @FXML
    private TextField phone;
    @FXML
    private TextField address;

    @FXML
    private TableView<Address> table;
    @FXML
    private TableColumn<Address, String> cName;
    @FXML
    private TableColumn<Address, String> cPhone;
    @FXML
    private TableColumn<Address, String> cAddress;

ဦးစွာ View  အတွင်းမှ အသုံးပြုလိုသည့် Component များအား @FXML အား အသုံးပြု၍ Declare လုပ်ပါသည်။ ဤကဲ့သို့ Declare လုပ်ထားရုံဖြင့် Controller အတွင်းမှ အသုံးပြုနိုင်မည် ဖြစ်သည်။ တစ်ခု သတိထားရန် လိုအပ်သည်မှာ JavaFX သည် FXML အား ဖတ်၍ Container မှ Create လုပ်ပေးပါသဖြင့် Constructor အတွင်း တွင် Component များအား Data ဖြည့်ခြင်းများ ပြုလုပ်၍မရပေ။ Initializable Interface အား implements လုပ်၍ initialize method အတွင်းတွင် ရေးသားရပါသည်။ ဤနေရာတွင် TableView တွင် Address Object အား အသုံးပြုနိုင်ရန်အတွက် Generics Type အနေဖြင့် Address အား အသုံးပြုထား၏။ TableColumn တွင်လည်း <Address,String> အား အသုံးပြုထား၏။

    @Override
    public void initialize(URL arg0, ResourceBundle arg1) {
        cName.setCellValueFactory(new PropertyValueFactory<Address, String>(
                "name"));
        cPhone.setCellValueFactory(new PropertyValueFactory<Address, String>(
                "phone"));
        cAddress.setCellValueFactory(new PropertyValueFactory<Address, String>(
                "address"));
        
        fm = new FileManager();
        this.table.getItems().addAll(fm.getAllData());
ပြီးပါက initialize method အတွင်းတွင် TableColumn များ၏ Cell Value Factory အား သတ်မှတ်ပေးရပါသည်။ အသုံးပြုသည့် Class မှာ PropertyValueFactory ဖြစ်ပြီး Generics Type မှာ TableColumn နည်းတူ <Address,String> ဖြစ်ပါသည်။ Constructor Argument အနေဖြင့် String အား ရေးသားရန်လိုပြီး၊ Address Class ၏ အသုံးပြုလိုသည့် Member ၏ အမည်အား ရေးသားရပါသည်။ ဤနည်းအားဖြင့် Address Class ၏ Member က Column အတွင်းတွင် ဖော်ပြနိုင်မည် ဖြစ်ပါသည်။

နောက်ဆုံးတွင် TableView#getItems ဖြင့် Data Model အား ခေါ်ယူကာ ၎င်းအတွင်းတွင် FileManager#getAllData ဖြင့် ရယူထားသော Address List အား ထည့်ပေးနေပါသည်။ ဤနည်းအားဖြင့် TableView အတွင်း Address ၏ အချက်အလက်များအားဖော်ပြပေးနိုင်မည် ဖြစ်သည်။


Swing အား အသုံးပြုပါက တကူးတက DataModel အဖြစ်ပြောင်းရန် လိုအပ်ခဲ့သော်လည်း JavaFX မှာမူ Object များအား ထိုအတိုင်း အသုံးပြုနိုင်သောကြောင့် အလွန်အစဉ်ပြေပါသည်။ ထို့အပြင် View Component များအားလည်း FXML ဖြင့် ရေးသားနိုင်သောကြောင့် View အတွက် ရေးသားရသော Code များ အလွန်နည်းပါးသွားပါသည်။


Table အတွင်းသို့ Data ဖြည့်စွက်ခြင်း


TableView ပေါ်မှာ Data ကို ဖော်ပြီးတဲ့အခါမှာ Input တွေကနေ ဖြည့်ထားတဲ့ အချက်အလက်တွေကို File ထဲမှာ သိမ်းပြီး၊ TableView ထဲကို ပြန်ဖြည့်တဲ့ အပိုင်းကို ရေးပါမယ်။ Button ကို နှိပ်တဲ့အခါမှာ အလုပ်လုပ်မယ့် save method  ကို အသစ်ရေးသားပါတယ်။ ပြီးတော့ Save Button ရဲ့ On Action မှာ save လို့ ရေးပြီး Save လုပ်ပါတယ်။

အဲ့ဒီလိုရေးလိုက်တာနဲ့ Save Button ကို နှိပ်တဲ့အခါမှာ save method ကို ခေါ်မှာဖြစ်ပါတယ်။


ပြီးတဲ့ အခါ Controller Class ထဲမှာ save method ကို အောက်ပါတတိုင်း ဆက်ရေးပါတယ်။

    public void save() {
        // get data from inputs
        Address a = new Address(this.name.getText(), this.phone.getText(),
                this.address.getText());

        // add to table
        this.table.getItems().add(a);

        // save to file
        fm.AddData(a);

        // clear inputs
        this.name.clear();
        this.phone.clear();
        this.address.clear();
    }

ဦးစွာ Input တွေထဲက ဒေတာတွေကို ယူပြီး Address အဖြစ်ပြောင်းပါတယ်။ ထို့နောက် TableView#getItems#add ကို သုံးပြီး Address Object ကို TableView ထဲကို ဖြည့်စွက်ပါတယ်။ ပြီးတော့ အဲ့ဒီ Address object ကိုပဲ FileManager#addData ကို သုံးပြီး file မှာသွား save ပါတယ်။ နောက်ဆုံးမှာတော့ Input တွေထဲက Data တွေကို clear လုပ်ပစ်ပါတယ်။ FileManager Class ကတော့ JavaFX နဲ့ မဆိုင်တဲ့အတွက် ဒီနေရာမှာ မရှင်းတော့ပါဘူး။



TableView မှ ရွေးထားတဲ့ Record ကို ရယူခြင်း

TableView အတွင်းမှ Data များကို ရွေးပြီး၊ ပြင်လိုတဲ့အခါ ပြီးတော့ ပြန်ပြီး ဖျက်လိုတဲ့အခါမှာ TableView#getSelectionModel ကို အသုံးပြုနိုင်ပါတယ်။ ပြီးတော့ TableView မှာ Right Click ထောက်ပြီး Menu ကို ပေါ်အောင် ContextMenu ကို အသုံးပြုနိုင်ပါတယ်။

    // delete menu
        MenuItem delete = new MenuItem("Delete");
        delete.setOnAction(new EventHandler<ActionEvent>() {

            @Override
            public void handle(ActionEvent arg0) {
                Address selectedAddress = table.getSelectionModel()
                        .getSelectedItem();
                fm.delete(selectedAddress);
                table.getItems().clear();
                table.getItems().addAll(fm.getAllData());
            }
        });
ဦးစွာ Delete အမည်နဲ့ MenuItem ကို Create လုပ်ပါတယ်။ ပြီးတဲ့အခါမှာ အဲ့ဒီ Object မှာ EventHandler ကို Action Handler အနေနဲ့ သတ်မှတ်ပါတယ်။ ဒီမှာတော့ Anonymous Class အနေနဲ့ပဲ ရေးပြီး handle ထဲမှာတော့ လိုအပ်တဲ့ လော့ဂျစ်တွေကို ရေးသားပါတယ်။

TableView ထဲက ရွေးထားတဲ့ Object ကို ရယူလိုတဲ့ အခါမှာ TableView#getSelectionModel#getSelectedItem ကို အသုံးပြုနိုင်ပါတယ်။ ရွေးထားတဲ့ Address Object ကို FileManager#delete ကို သုံးပြီး File ထဲကနေ ဖျက်ထုတ်ပစ်ပါတယ်။ ပြီးတော့မှ TableView ထဲကနေ အားလုံးကို ဖျက်ထုတ်ပြီးမှ၊ FileManager#getAllData ကိုသုံးပြီး၊ ရလာတဲ့ Address တွေအားလုံးကို TableView ထဲကို ထည့်ပါတယ်။ တကယ်ဆိုရင်တော့ TableView#getItems#remove ကို သုံးမယ်ဆိုလဲ ရပါတယ်။ File နဲ့ View ကို Data အတူတူထားချင်တဲ့ အတွက် TableView ထဲက Data တွေကို Clear လုပ်ပြီး၊ File ထဲက ရလာတဲ့ Data တွေနဲ့ View ကို ပြန်ပြီးဖော်ပြစေတာဖြစ်ပါတယ်။


နောက်ဆုံး အလားတူ Edit MenuItem ကို ရေးသားပြီး၊ ContextMenu မှာ Add လုပ်ပါတယ်။ နောက်ဆုံးမှာတော့ TableView ရဲ့ Context Menu အနေနဲ့ သွားပြီး Set လုပ်ရုံပါပဲ။

    ContextMenu cm = new ContextMenu();
    cm.getItems().addAll(edit, delete);

    this.table.setContextMenu(cm);


ကျွှန်တော်တို့ TableView နဲ့ သိပ်ပြီး မဆိုင်တဲ့ အတွက် ဒီနေရာမှာတော့ Edit အကြောင်းကို မရေးတော့ပါဘူး။ နောက် Dialog အကြောင်းကို ရေးသားတော့မှပဲ Edit ကို ဆက်ပြီးရေသားပါတော့မယ်။

Source Code တွေ အားလုံးကိုတော့ Git Hub မှာ လေ့လာနိုင်ပါတယ်။

Address.java
AddressBook.java
AddressEditor.java
FileManager.java
AddressBook.fxml
AddressEditor.fxml

အခုတစ်ခေါက် အလုပ်များနေတာနဲ့ ဘလောဂ်မရေးဖြစ်ဘူးဖြစ်နေပါတယ်။ ဆက်ပါဦးမည်။

လေးစားစွာဖြင့်
မင်းလွင်