Custom UI Controls with JavaFX – Part 1

Original source: http://www.guigarage.com/2012/11/custom-ui-controls-with-javafx-part-1/

 

Tùy chỉnh điều khiển UI với JavaFX – Phần 1

Đăng ngày 2012/11/17 bởi Hendrik


 

 

Một thứ tôi hay thường làm là lập trình Swing, sự tuỳ chỉnh và sáng tạo ra các loại thành phần mới. Ví dụ như là JGrid. Kể từ khi JavaFX ra đời tôi muốn thêm Jgrid vào nó. Sau một số thí nghiệm và các mẫu chương trình thất bại tôi nghĩ rằng tôi tìm ra con đường đúng đắng để làm điều đó. Các cuộc đàm phán từ Gerrit GrunwaldJonathan Giles tại JavaOne đã thực sự giúp tôi rất nhiều để làm được như vậy. Các tài liệu ghi âm của cuộc đàm thoại này có thể tìm thấy online tại đây (liên kết, liên kết) vì vậy tôi khuyên mọi người, những ai quan tâm đến chủ đề này có thể dành ra ít thời gian và xem chúng.

 

Bắt đầu

Mỗi thành phần UI trong JavaFX được tạo ra bởi một hệ control, skin và behavior. Trong một trường hợp lý tưởng có thể có phần css.

custom-components1

Cách tốt nhất để bắt đầu là tạo ra một lớp điều khiển mới mà có phần đuôi mở rộng là javafx.scene.control.Control. Lớp này về cơ bản là tương đương với JComponent. Nó sẽ giữ các thuộc tính của các thành phần và hoạt động như các lớp chính cho nó, vì trường hợp của lớp này sau đó sẽ được tạo ra trong code ứng dụng của bạn và được thêm vào nhánh UI.

 

1

2

MyCustomControl myControl = new MyCustomControl();

panel.getChildren().add(myControl);

 

Khi lập trình các thành phần swing đúng cách bạn đặt tất cả mọi thứ mà phụ thuộc vào hình dạng hoặc người dùng tương tác vào một lớp UI (xem ví dụ LabelUI). JavaFX đi một bước xa hơn và cung cấp lớp skin cho tất cả hình dạng và bố trí liên quan đến code và các lớp behavior cho tất cả các tương tác người dùng.

custom-components2

Để làm như vậy trong JavaFX bạn cần phải các lớp phụ thuộc vào nhau như thế nào. Dưới đây là một biểu đồ ngắn cho thấy mối quan hệ giữa chúng:

custom-componens3

Tạo Behavior

Nếu thành phần của bạn chỉ có dữ liệu hình dung và không có tương tác thì khá đơn giản để tạo ra một behavior. Vì vậy, bạn chỉ cần đuôi mở rộng com.sun.javafx.scene.control.behavior.BehaviorBase.

1

2

3

4

5

6

 

public class MyCustomControlBehavior extends BehaviorBase {

 

   public MyCustomControlBehavior(MyCustomControl control) {

      super(control);

   }

}

 

Một số bạn có thể bối rối khi nhìn thấy những gói BehaviorBase. Tại thời điểm này đây là một API riêng tư và bạn không nên sử dụng các lớp này trong code của bạn một cách thông thường, nhưng những kẻ tại Oracle biết về vấn đề này và sẽ cung cấp các BehaviorBase như một API công cộng với JavaFX 8. Vì vậy, tốt nhất là thực hành sử dụng các lớp riêng tư ngay bây giờ và cấu trúc lại việc nhập liệu khi Java 8 ra đời.

 

Tạo Skin

Sau lớp behavior được tạo ra, chúng tai có thể nhìn vào skin. Lớp skin của bạn sẽ chủ yếu có mở rộng com.sun.javafx.scene.control.skin.BaseSkin và tạo ra một behavior mới để bạn kiểm soát. Code của bạn bình thường giống như thế này:

 

1

2

3

4

5

6

public class MyCustomControlSkin extends SkinBase{

 

   public MyCustomControlSkin(MyCustomControl control) {

      super(control, new MyCustomControlBehavior(control));

   }

}

 

Như bạn có thể thấy các BaseSkin là một API riêng tư. Nó cũng sẽ đổi thành công khai đối với Java 8.

 

Tạo Control

Lớp cuối cùng chúng ta sẽ cần là control. Đầu tiên chúng ta tạo ra một lớp rỗng đơn giản:

 

1

2

3

4

5

public class MyCustomControl extends Control {

    

   public MyCustomControl() {

   }

}

 

Tại thời điểm này chúng ta có một sự rò rỉ trong sự phụ thuộc của ba lớp. Skin sẽ biết về behavior và control. Ở đây mọi thứ có vẻ đúng. Tuy nhiên trong code ứng dụng của bạn sẽ tạo ra một control mới và sử dụng nó như tôi đã giới thiệu trước đó. Vấn đề là lớp control không biết bất cứ điều gì về skin hoặc behavior. Đây là một trong những cạm bẫy lớn nhất tôi đã phải đối mặt khi học JavaFX.

 

Gắn kết các thứ lại với nhau

Một vấn đề lớn đập vào mắt ta ở cái nhìn đầu tiên là một trong những tiềm năng JavaFX cung cấp. Với JavaFX nó rất dễ dàng để tạo hình ảnh khác nhau (skin) cho phần control. Về phần này, bạn có thể tùy chỉnh giao diện các thành phần của css. Bởi vì skin là phần chính của hình dáng bên ngoài, nó cũng được xác định bởi css. Vì vậy, thay vì tạo ra một đối tượng skin để kiểm soát bằng cách riêng của bạn, bạn chỉ cần xác định các lớp skin được bạn sử dụng để kiểm soát. Các chú thích và mọi thứ khác được thực hiện tự động bởi các API JavaFX. Để làm như vậy bạn có để ràng buộc kiểm soát của bạn cho một lớp css.

Trước hết tất cả các bạn phải tạo một tập tin css mới trong chương trình của bạn. Tôi nghĩ rằng cách tốt nhất là sử dụng các gói giống như các control có và đặt một file css dưới src/main/resource:

custom-components4.png

Bên trong css bạn phải xác định một bộ lựa chọn mới cho thành phần của bạn và thêm vào skin như một thuộc tính cho nó. Ví dụ như thế này:

1

2

3

.custom-control {

   -fx-skin: com.guigarage.customcontrol.MyCustomControlSkin;

}

 

Một khi bạn đã tạo css thì bạn phải xác định nó trong phần control của bạn. Vì vậy, bạn phải cấu hình các đường dẫn đến file css và bộ lựa chọn các thành phần của bạn:

 

1

2

3

4

5

6

7

8

9

10

11

 

public class MyCustomControl extends Control {

   public MyCustomControl() {

      getStyleClass().add(custom-control);

   }

 

   @Override

   protected String getUserAgentStylesheet() {

      return MyCustomControl.class.getResource(customcontrol.css).toExternalForm();

   }

}

 

Sau khi tất cả các thứ được thực hiện một cách chính xác, JavaFX sẽ tạo ra một skin yêu cầu cho phần control của bạn. Bạn không cần phải quan tâm về phần lý thuyết hoặc phần cơ chế phụ thuộc. Tại thời điểm này, tôi muốn cảm ơn Jonathan Giles người đã cùng với tôi tốn khá nhiều thời gian cho mã tích hợp css của gridfx và giải thích cho tôi tất cả các cơ chế và lợi ích của chúng.

 

Truy cập Skin và Behavior

Thông thường thì việc truy cập vào skin hoặc behavior từ bên trong bộ điều khiển có nhu cầu không cao. Nhưng nếu bạn muốn bạn có thể truy cập chúng theo cách sau:

custom-controls5

Bởi vì controler.getSkin () nhận một javafx.scene.control.Skin không phải là một SkinBase, bạn phải bỏ nó nếu bạn cần các Behavior:

 

1 ((SkinBase)getSkin()).getBehavior();

 

Giải pháp cho người ghét css

Đối với một số bạn, cơ chế này có vẻ như quá khủng. Có lẽ bạn chỉ cần một control cụ thể một lần trong ứng dụng của bạn và bạn không có kế hoạch skin nó với css và làm tất cả các thứ như thế này. Đối với trường hợp sử dụng này có một cách giải quyết tốt đẹp trong các API JavaFX. Bạn có thể bỏ qua tất cả những thứ css và thiết lập lớp skin cho control của bạn trong code như sau:

 

1

2

3

4

5

public class MyCustomControl extends Control {

   public MyCustomControl() {

      setSkinClassName(MyCustomControlSkin.class.getName());

   }

}

 

Các lợi ích của việc này là có thể cấu trúc lại các gói hoặc classnames mà không làm phá vỡ code của bạn và bạn không cần thêm file css. Mặt khác nó có một bất lợi rất lớn. Bạn không thể sử dụng được css skin đã xác định trong bất kỳ phần mở rộng nào của hệ điều khiển này. Tôi nghĩ rằng tất cả các API công cộng như gridfx nên sử dụng cách của css. Trong một số trường hợp sử dụng trong nội bộ, cách khó hơn có thể được hoàn thành nhanh hơn.

 

Lời kết

Bây giờ chúng ta đã tạo được một control, một skin và một behavior làm việc tốt và có thể thêm vào nhánh UI của bạn. Nhưng giống như trong swing khi chỉ đơn giản là mở rộng JComponent bạn không thể nhìn thấy bất cứ điều gì trên màn hình. Vì vậy, bước tiếp theo là phong cách và bố trí thành phần của bạn. Tôi sẽ xử lý vấn đề này ở bài tiếp theo của tôi.

Nếu bạn muốn xem một số code của các thành phần tùy chỉnh hiện có bạn có thể xem jgridfx hoặc JFXtras. Tại jgridfx các file sau đây phù hợp với bài viết:

 

 com.guigarage.fx.grid.GridView (control)

 com.guigarage.fx.grid.skin.GridViewSkin (skin)

 com.guigarage.fx.grid.behavior.GridViewBehavior (behavior)

 /src/main/resources/com/guigarage/fx/grid/gridview.css (css)

Advertisements