Java Swing – Quản lý layout

4.6/5 - (9 votes)

Các component trong Java Swing được chia làm hai loại là container và children, trong đó container là loại component dùng để chứa các component khác, ví dụ như JFrame, JPanel… còn children là các component cụ thể như JLabel, JButton… khi chúng ta đặt các component con lên các container thì việc đặt vị trí và sắp xếp chúng là một công việc rất mất thời gian, từ đó khái niệm layout ra đời.

Layout có tác dụng tự động bố trí các component trên container, bạn tưởng tượng layout giống như một căn nhà có nhiều phòng và các phòng này có thể tự thay đổi kích thước cũng như vị trí theo một quy luật nào đó và theo đó mà cách vật dụng trong phòng cũng tự thay đổi theo. Đối với người mới bắt đầu lập trình GUI thì layout là một khái niệm khá khó hiểu vì nó không có hình ảnh trực quan như text hay button.

Cũng có những trường hợp chúng ta không cần dùng đến layout. Tuy nhiên nên dùng thì vẫn tốt hơn.

FlowLayout

Đây là loại layout đơn giản nhất, layout này thường được dùng kết hợp với các layout khác. Layout này quy định kích thước của các component con là vừa đủ với nội dung hiển thị của component.

Mặc định các component sẽ được sắp xếp trên một hàng từ trái sang phải, nếu không vừa đủ một hàng thì chúng sẽ xuống hàng. Các component sẽ cách nhau 5 pixel và cách viền cửa sổ 5 pixel. Tuy nhiên chúng ta có thể thay đổi các thuộc tính này.

FlowLayout()
FlowLayout(int align)
FlowLayout(int align, int hgap, int vgap) 

Ở trên là 3 kiểu khởi tạo một FlowLayout, align quy định sắp xếp từ trái sang phải hay ngược lại, hgap là khoảng cách giữa các component theo chiều ngang, vgap là khảng cách giữa các component theo chiều dọc.

import java.awt.Dimension;

import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.JTree;
import javax.swing.SwingUtilities;


public class FlowLayoutExample extends JFrame {


    public FlowLayoutExample() {

        initUI();
    }

    public final void initUI() {

        JPanel panel = new JPanel();

        JTextArea area = new JTextArea("text area");
        area.setPreferredSize(new Dimension(100, 100));

        JButton button = new JButton("button");
        panel.add(button);

        JTree tree = new JTree();
        panel.add(tree);

        panel.add(area);

        add(panel);

        pack();

        setTitle("Layout Example");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {

                FlowLayoutExample ex = new FlowLayoutExample();
                ex.setVisible(true);
            }
        });
    }
}

Trong ví dụ trên chúng ta tạo một đối tượng JButton, JTreeJTextArea. Lớp JTextArea tạo một ô gõ văn bản. Lớp JTree hiển thị dữ liệu theo dạng cây, khi chúng ta tạo một đối tượng JTree rỗng thì mặc nhiên trong này có sẵn một số giá trị rồi.

JPanel panel = new JPanel();

Mặc định khi tạo một JPanel thì layout được dùng sẽ là flowLayout luôn nên ở đây chúng ta không cần phải thiết lập lại.

JTextArea area = new JTextArea("text area");
area.setPreferredSize(new Dimension(100, 100));

Mặc định flowLayout sẽ thiết lập kích thước cho các component con vừa đủ để bao bọc nội dung bên trong component đó, nên nếu muốn có kích thước riêng thì chúng ta phải tự thiết lập lại bằng phương thức setPreferredSize().

panel.add(area);

Và để thêm một component con vào một container thì chỉ đơn giản là dùng phương thức add() là được.

Capture

GridLayout

Layout này sắp xếp các component theo dạng bảng. Các component sẽ có kích thước bằng nhau.

import java.awt.GridLayout;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class GridLayoutExample extends JFrame {

    public GridLayoutExample() {

        initUI();
    }

    public final void initUI() {

        JPanel panel = new JPanel();

        panel.setBorder(BorderFactory.createEmptyBorder(5, 5, 5, 5));
        panel.setLayout(new GridLayout(5, 4, 5, 5));

        String[] buttons = {
            "Cls", "Bck", "", "Close", 
            "7", "8", "9", "/", 
            "4", "5", "6", "*", 
            "1", "2", "3", "-", 
            "0", ".", "=", "+"
        };

        for (int i = 0; i < buttons.length; i++) {

            if (i == 2)
                panel.add(new JLabel(buttons[i]));
            else
                panel.add(new JButton(buttons[i]));
        }

        add(panel);

        setTitle("Layout Example");
        setSize(350, 300);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {

                GridLayoutExample ex = new GridLayoutExample();
                ex.setVisible(true);
            }
        });
    }
}

Chúng ta sẽ tạo giao diện máy tính bỏ túi.

panel.setLayout(new GridLayout(5, 4, 5, 5));

Trong phương thức khởi tạo GridLayout, 2 tham số đầu là số hàng và số cột, hai tham số cuối là khoảng cách giữa các component theo chiều ngang và chiều dọc.

Capture

BorderLayout

BorderLayout sắp xếp các component theo vùng, ở đây có 5 vùng là Đông (EAST), Tây (WEST), Nam (SOUTH), Bắc (NORTH), và Chính giữa (CENTER). Nhưng mỗi vùng chỉ được chứa một component, do đó khi muốn đưa nhiều component vào một vùng thì chúng ta đặt một layout khác vào vùng đó rồi đặt các component vào layout mới đó. Khác với các layout khác, kích thước của các component trong mỗi vùng là do chúng ta thiết lập chứ không phải do layout tự co dãn, ngoại trừ vùng CENTER sẽ có kích thước thay đổi tùy thuộc vào 4 vùng còn lại.

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Insets;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JTextArea;
import javax.swing.JToolBar;
import javax.swing.SwingUtilities;
import javax.swing.border.EmptyBorder;
import javax.swing.border.LineBorder;


public class BorderLayoutExample extends JFrame {


    public BorderLayoutExample() {

        initUI();
    }

    public final void initUI() {

        // Create menu bar
        JMenuBar menubar = new JMenuBar();
        JMenu file = new JMenu("File");
 
        menubar.add(file);
        setJMenuBar(menubar);
 
        // Create horizontal toolbar (NORTH)
        JToolBar horizontalToolbar = new JToolBar();
        horizontalToolbar.setFloatable(false);
 
        ImageIcon exitIcon = new ImageIcon("F:/exit.png");
        JButton exitButton = new JButton(exitIcon);
        exitButton.setBorder(new EmptyBorder(0, 0, 0, 0));
        horizontalToolbar.add(exitButton);
 
        add(horizontalToolbar, BorderLayout.NORTH);
 
        // Create vertical toolbar (WEST)
        JToolBar verticalToolbar = new JToolBar(JToolBar.VERTICAL);
        verticalToolbar.setFloatable(false);
        verticalToolbar.setMargin(new Insets(10, 5, 5, 5));
 
        ImageIcon driveIcon = new ImageIcon("F:/drive.png");
        ImageIcon computerIcon = new ImageIcon("F:/computer.png");
        ImageIcon printerIcon = new ImageIcon("F:/printer.png");
 
        JButton driveButton = new JButton(driveIcon);
        driveButton.setBorder(new EmptyBorder(3, 0, 3, 0));
 
        JButton computerButton = new JButton(computerIcon);
        computerButton.setBorder(new EmptyBorder(3, 0, 3, 0));
 
        JButton printerButton = new JButton(printerIcon);
        printerButton.setBorder(new EmptyBorder(3, 0, 3, 0));
 
        verticalToolbar.add(driveButton);
        verticalToolbar.add(computerButton);
        verticalToolbar.add(printerButton);
 
        add(verticalToolbar, BorderLayout.WEST);
 
        // Craete Text area (CENTER)
        add(new JTextArea(), BorderLayout.CENTER);
 
        // Create Status bar (SOUTH)
        JLabel statusbar = new JLabel("Statusbar");
        statusbar.setPreferredSize(new Dimension(-1, 22));
        statusbar.setBorder(LineBorder.createGrayLineBorder());
        add(statusbar, BorderLayout.SOUTH);
  
        setSize(350, 300);
        setTitle("Layout Example");
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                BorderLayoutExample ex = new BorderLayoutExample();
                ex.setVisible(true);
            }
        });
    }
}

Trong ví dụ này chúng ta sẽ tạo một giao diện giống như các ứng dụng thường thấy bao gồm 1 thanh menu, 1 toolbar nằm ngang (NORTH), 1 toolbar nằm dọc (WEST), 1 text area (CENTER), 1 status bar (SOUTH).

Mặc định JFrame dùng sẵn BorderLayout nên chúng ta không cần phải thiết lập lại.

Capture

BoxLayout

BoxLayout cho phép chúng ta tạo các giao diện phức tạp. Layout này sắp xếp các component theo chiều ngang hoặc chiều dọc, tức là giống với FlowLayout, chỉ khác ở chỗ là BoxLayout cho phép các component có thể tự thay đổi kích thước dựa vào kích thước cửa sổ chính. Ngoài ra bạn còn có thể lồng một BoxLayout vào một BoxLayout khác.

BoxLayout thường đi cùng với một lớp khác nữa là lớp Box, lớp này có tác dụng tạo các khoảng trống để “đẩy” các component về các vị trí khác nhau. Các loại khoảng trống mà Box có thể tạo ra là Glue, StrutRigidArea. Mình sẽ giải thích ngay bên dưới.

Trong ví dụ này chúng ta sẽ tạo 2 button nằm ở góc dưới bên phải cửa sổ.

import java.awt.Dimension;

import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;


public class TwoButtonsExample extends JFrame {

    public TwoButtonsExample() {

        initUI();
    }

    public final void initUI() {

        JPanel basic = new JPanel();
        basic.setLayout(new BoxLayout(basic, BoxLayout.Y_AXIS));
        add(basic);

        basic.add(Box.createVerticalGlue());

        JPanel bottom = new JPanel();
        bottom.setAlignmentX(Component.RIGHT_ALIGNMENT);
        bottom.setLayout(new BoxLayout(bottom, BoxLayout.X_AXIS));

        JButton ok = new JButton("OK");
        JButton close = new JButton("Close");

        bottom.add(ok);
        bottom.add(Box.createRigidArea(new Dimension(5, 0)));
        bottom.add(close);
        bottom.add(Box.createRigidArea(new Dimension(15, 0)));

        basic.add(bottom);
        basic.add(Box.createRigidArea(new Dimension(0, 15)));

        setTitle("Layout Example");
        setSize(300, 150);
        setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        setLocationRelativeTo(null);
    }

    public static void main(String[] args) {

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                TwoButtonsExample ex = new TwoButtonsExample();
                ex.setVisible(true);
            }
        });
    }
}

Bạn có thể hình dung sơ đồ bố trí các component như hình dưới.

Untitled11

Chúng ta tạo 2 panel, một panel chính sắp xếp các component theo chiều dọc, một panel thứ 2 sắp xếp các component theo chiều ngang, panel thứ 2 sẽ chứa 2 button căn lề phải. Để panel thứ 2 có thể nằm phía dưới cùng của cửa sổ (hay phía dưới cùng của panel chính) thì chúng ta dùng đến Glue. 

basic.setLayout(new BoxLayout(basic, BoxLayout.Y_AXIS));

Đầu tiên chúng ta tạo panel chính với BoxLayout theo chiều dọc.

basic.add(Box.createVerticalGlue());

Chúng ta tạo khoảng trống glue bằng phương thức createVerticalGlue(). Cơ chế hoạt động ở đây rất đơn giản. Ví dụ bạn đặt một button, sau đó gọi phương thức tạo glue, thì một vùng trống sẽ được thêm vào phía sau button (ở dưới button hoặc bên phải button tùy vào layout ngang hay dọc).

2fill5fill

JPanel bottom = new JPanel();
bottom.setAlignmentX(Component.RIGHT_ALIGNMENT);
bottom.setLayout(new BoxLayout(bottom, BoxLayout.X_AXIS));

Tiếp theo chúng ta tạo panel thứ hai và thiết lập cho các component con trượt qua lề bên phải bằng phương thức setAlignmentX(), panel này dùng BoxLayout theo chiều ngang.

bottom.add(Box.createRigidArea(new Dimension(5, 0)));

Chúng ta thêm một khoảng trống nhỏ giữa 2 button. Nhưng ở đây chúng ta không dùng glue và dùng rigid. Sự khác nhau của glue với rigid là kích thước của glue tự động thay đổi tùy theo các component, còn kích thước của rigid là do chúng ta quy định.

3fill 6fill

basic.add(bottom);

Sau khi đã thiết kế giao diện cho panel thứ hai thì chúng ta đặt panel đó vào panel chính.

basic.add(Box.createRigidArea(new Dimension(0, 15)));

Đồng thời chúng ta cũng tạo một khoảng trống rigid giữa panel thứ 2 với cạnh đáy của cửa sổ.

Capture
5 3 votes
Article Rating
Subscribe
Thông báo cho tôi qua email khi
guest

This site uses Akismet to reduce spam. Learn how your comment data is processed.

0 Comments
Inline Feedbacks
View all comments