طراحی اپلیکیشن دسکتاپ با Javafx


سلام، تو این پست قصد دارم یک روال کلی از پروژه ساده دسکتاپ با javafx رو بهتون نشون بدم. برای اینکار میخوام یک Music player اولیه رو با این ابزار پیاده سازی کنم. خواهید دید که اصلا چیز سختی نیست و علاوه جذابیت خاص خودش به راحتی قابل انجام است.

من از ابزار های زیر استفاده میکنم:

  1. JDK 8
  2. IntelliJ IDEA Community
  3. Scene builder

دو مورد اولی رو احتمال زیاد باهاشون آشنایی دارید، Scene builder ابزاری هست که به شما کمک میکنه جلوه بصری نرم افزار خودتون رو راحت تر پیاده سازی کنید. خروجی کد fxml میده که بعدا میتونید اون رو تو پروژه جاوا بارگیری کنید.

1. ساخت پروژه اولیه

در intellij از قسمت new project مستقیم javafx رو انتخاب کنید. برای شما پروژه ساده ای ایجاد میشود که در پکیج sample شامل controller.java , main.java, sample.fxml می باشد. میتونید به دلخواه خودتون ساختار پکیج بندی پروژه رو تغییر بدید که فعلا من اینجا برای سادگی از اینکار صرف نظر میکنم.

2. طراحی view

وارد محیط scene builder میشویم میتوانید اونو مستقیم روی سیستمتون نصب کنید یا اینکه از طریق intellij باهاش کار کنید. ترجیح من اینه که از روش اول استفاده کنم(امکانات بیشتری بهتون میده).

Scene builder
Scene builder

به راحتی میتونید کامپوننت های مورد نیاز خودتون رو به صورت کشیدن و رها کردن به صفحه بندیتون اضافه کنید من برای کار خودم از موجودیت های زیر استفاده میکنم:

  1. AnchorPane

یکی از نگه دارنده ها در javafx هست که به شما کمک میکنه صفحه بندی مورد نظرتون رو بسازید. با استفاده از نگه دارنده ها میتونید کامپوننت ها (نظیر دکمه، تکست فیلد و نوار) رو به شکل خاصی نمایش بدید.

2. ToolBar

یک منوی خیلی ساده در اختیارتون قرار میده که در ابتدا فقط شامل یک دکمه هست از اون برای انتخاب موسیقی دلخواه استفاده میکنیم

3. Slider

برای نمایش وضعیت پخش موسیقی مورد نظر استفاده میکنیم(اینکه درحال حاضر در کدام قسمت زمانی پخش هستیم) همچنین باید بتوانیم با تغییر وضعیت آن، قسمت دلخواه خودمون رو بشنویم

4. Text

برای نمایش ثانیه و دقیقه لحظه ای هنگام پخش استفاده میکنیم

5. Button

یک دکمه ساده برای play و pause

خب تا اینجا کار اصلیمون رو هنوز شروع نکردیم ولی قبل از اون بیاید یه تغییر کوچیک تو صفحه ایجاد کنیم

برای تغییر نوشته یک دکمه به راحتی میتونیم روی اون کلیک کرده و از منوی سمت راست با تغییر Text، متن داخل اون رو شخصی سازی کنیم ولی اگه بخوایم شکل به خصوصی (مثلا دایره ای ) به یکی از دکمه ها بدیم چی؟ یا مثلا رنگش رو چه طور عوض کنیم؟

جاوا اف ایکس از نوعی css خاصی پشتیبانی میکنه که به ما این قابلیت رو میده ظاهر کاپوننت های خودمون رو شخصی سازی کنیم. این ویژگی به ما کمک میکنه تا رابط های کاربری با ظاهر چشم نوازی ایجاد کنیم (بر خلاف Swing که کامپوننت های قدیمیش واقعا تو ذوق آدم میزد :) )

در حال حاضر صرفا برای شروع روی دکمه روبروی slider کلیک کرده و از منوی سمت راست قسمت Style، ویژگی خاص fx-background-radius- را به 50% تغییر میدهیم. همچنین میتوانیم از کاراکتر های خاص html که از یونی کد های مختلف شناخته میشن برای دکمه ها استفاده کنیم.

3. نوشتن کلاس Controller

اول از همه نیاز داریم به یک سری از کامپوننت های خودمون دسترسی داشته باشیم تا بتونیم تغییراتی رو هنگام اجرا اعمال کنیم پس ویژگی fx:id یکتایی به هر کدوم اختصاص میدیم. همچنین باید یکسری event (رویداد)هایی مشخص کنیم تا هنگام وقوع اونها بتونیم تغییرات را اعمال کنیم مثلا اینکه وقتی کاربر روی دکمه ای کلیک میکنه چه اتفاقی رخ بده یا اینکه موقع تغییر دادن مکان نمای slider بتونیم وضعیت پخش رو تغییر بدهیم. برای این کارها کافیه روی کامپوننت مورد نظر کلیک کرده و از قسمت سمت راست منوی code موارد زیر را تعریف کنیم

کامپوننت هایی که برای آن ها شناسه تعیین میکنیم
1. اسلایدر

2. کلید پخش

3. نمایشگر زمان

رویداد ها
1. Action event برای هر کدام از کلید های file و play

2. همچنین یک on mouse dragged برای اسلایدر

خب کارمون تقریبا با scene builder تموم شد بعد از اینکه view رو در مسیر پروژه به جای Sample.fxml کپی کردیم (فراموش نکنید ویژگی fx:controller رو در فایل fxml برابر مسیر و نام کلاس کنترلر قرار بدید)حالا باید کلاس controller خودمون رو بنویسیم برای اینکه کارمون یکم راحتتر بشه از محیط Scene builder مسیر

View -> Show Sample Controller Skeleton

یک نسخه اولیه از کلاس کنترلر میگیریم که شامل کامپوننت ها و رویداد هایی است که تعریف کردیم. همگی را در کلاس Controller کپی میکنیم

کامپوننت هایی که شناسه یکتایی بهشون دادیم همگی با انوتیشن FXML@ مشخص می شوند و نیازی به نمونه سازی اولیه ندارند.

خب اینجا ما علاوه بر مواردی که در بالا با انوتیشن مشخص شده اند به یک سری کلاس های بیشتری هم نیاز داریم:

  1. MediaPlayer
  2. File

همچنین یک متغیر boolean برای تمایز بین حالت پخش و سکون

public class Controller {

    @FXML 
    private Slider slider;

    @FXML 
    private Button playButton;

    @FXML 
    private Text timeLabel;

    private MediaPlayer mediaPlayer;

    private File file;

    private boolean isPlaying = false;
...
}

اگه بخوایم از دید کاربر به مساله نگاه کنیم برای شروع کار نیاز داریم فایل مورد نظرمون رو انتخاب کنیم یک پیاده سازی ابتدایی که میتوانیم ارائه دهیم در متد Action event برای کلید file:

@FXML
void openFile(ActionEvent event) {

    FileChooser fileChooser = new FileChooser();
    fileChooser.setTitle(&quotSelect your music&quot);

    file = fileChooser.showOpenDialog(new Stage());

    Media media = new Media(file.toURI().toString());
    mediaPlayer = new MediaPlayer(media);

}

هرچه بیشتر درگیر جزئیات شویم میتونیم این قطعه کد رو کاملتر کنیم مثلا در ادامه نیاز داریم که به media player بگیم که هنگام پخش مکان نمای اسلایدر رو در جای مناسب قرار بده پس در همین متد قطعه کد زیر را هم داریم

mediaPlayer.setOnPlaying(new Runnable() {
    @Override
    public void run() {
        new Thread(()->{
            while (isPlaying){
                slider.setValue(mediaPlayer.getCurrentTime().toSeconds() / mediaPlayer.getStopTime().toSeconds() * 100);
                try {
                    Thread.currentThread().sleep(500);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }).start();
    }
});

میبینید که برای کارمون مجبور شدیم یک ترد(Thread) جدید هم ایجاد کنیم و در آخر برای اینکه تردمون همیشه درگیر نباشه یک sleep کوتاه هم براش میگذاریم (این کار باعث میشه تا بتونیم مکان نمای اسلایدر رو تغییر بدیم و گرنه تردمون زورش از ما خیلی بیشتر میشه ).

برای Action event کلید play:

@FXML
void playClicked(ActionEvent event) {
        if(isPlaying){
            mediaPlayer.pause();
            playButton.setText(&quot⏵&quot);
        }
        else{
            mediaPlayer.play();
            playButton.setText(&quot||&quot);
        }

        isPlaying = !isPlaying;
}

و همچنین برای رویداد کشیدن Slider:

@FXML
public void sliderDragged(MouseEvent mouseEvent) {
    mediaPlayer.seek(new Duration(slider.getValue() * mediaPlayer.getStopTime().toSeconds() * 10));
}

توجه کنید که موارد بالا صرفا برای پیاده سازی اولیه میباشند. همونطور که گفتم هر چی بیشتر درگیر جزئیات بشیم کاملترش میکنیم

سورس کد در گیتهاب