Daily Archives: 04/03/2016

Java Swing – Xử lý sự kiện

Event (sự kiện) là một phần quan trọng của bất kỳ ứng dụng GUI nào. Tất cả các ứng dụng dạng GUI xử lý event trong suốt thời gian mà nó chạy. Event phần lớn được tạo ra bởi người dùng, nhưng cũng có lúc được tạo ra bởi chính ứng dụng. Có ba thành phần tham gia vào hệ thống event:

  • Event nguồn
  • Đối tượng event
  • Đối tượng lắng nghe event

Event nguồn là đối tượng tạo ra sự thay đổi. Cứ có gì đó trong ứng dụng tạo ra sự thay đổi nào đó thì nó chính là event nguồn. Đối tượng event là chính bản thân cái event đó đã được mã hóa. Đối tượng lắng nghe event làm công việc xử lý event đó.

Đối tượng Event

Mỗi khi có thứ gì đó xảy ra thì một đối tượng event sẽ được tạo. Chẳng hạn như khi bạn click vào một button hay chọn một item trong một danh sách. Đối tượng event lưu trữ thông tin về loại sự kiện đã xảy ra. Chúng ta sẽ xem xét thông tin đó trong ví dụ dưới đây.

import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.text.DateFormat;
import java.util.Date;
import java.util.Locale;
import javax.swing.AbstractAction;
import javax.swing.BorderFactory;
import javax.swing.DefaultListModel;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JList;


public class EventObjectEx extends JFrame {

    private JList list;
    private DefaultListModel model;
    

    public EventObjectEx() {
        
        initUI();
    }
    
    private void initUI() {

        Container pane = getContentPane();
        GroupLayout gl = new GroupLayout(pane);
        pane.setLayout(gl);   

        model = new DefaultListModel();
        list = new JList(model);
        list.setMinimumSize(new Dimension(250, 150));
        list.setBorder(BorderFactory.createEtchedBorder());

        JButton okButton = new JButton("OK");
        okButton.addActionListener(new ClickAction());

        gl.setAutoCreateContainerGaps(true);
        
        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(okButton)
                .addGap(20)
                .addComponent(list)
        );

        gl.setVerticalGroup(gl.createParallelGroup()
                .addComponent(okButton)
                .addComponent(list)
        );
        
        pack();

        setTitle("Event Example");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }
    
    private class ClickAction extends AbstractAction {
        
        @Override
        public void actionPerformed(ActionEvent e) {
            
            Locale locale = Locale.getDefault();
            Date date = new Date(e.getWhen());
            String tm = DateFormat.getTimeInstance(DateFormat.SHORT,
                    locale).format(date);

            if (!model.isEmpty()) {
                model.clear();
            }

            if (e.getID() == ActionEvent.ACTION_PERFORMED) {
                model.addElement("Event Id: ACTION_PERFORMED");
            }

            model.addElement("Time: " + tm);

            String source = e.getSource().getClass().getName();
            model.addElement("Source: " + source);           
        }
    }        

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {
            @Override
            public void run() {
                EventObjectEx ex = new EventObjectEx();
                ex.setVisible(true);
            }
        });
    }
}

Trong ví dụ này chúng ta tạo một button và một list, list sẽ hiển thị các thông tin về đối tượng event được gây ra bởi button.

okButton.addActionListener(new ClickAction());

Lớp ClickAction lắng nghe sự kiện từ button OK.

private class ClickAction extends AbstractAction {
    
    @Override
    public void actionPerformed(ActionEvent e) {
        ...
    }
}        

Phương thức actionPerformed() được gọi mỗi lần sự kiện xảy ra, phương thức này nhận tham số là một đối tượng ActionEvent.

Locale locale = Locale.getDefault();
Date date = new Date(e.getWhen());
String s = DateFormat.getTimeInstance(DateFormat.SHORT,
        locale).format(date);

Chúng ta có thể lấy thời điểm event xảy ra từ phương thức getWhen(), thời gian trong phương thức này tính theo mili giây nên chúng ta phải định dạng lại cho phù hợp.

String source = e.getSource().getClass().getName();
model.addElement("Source: " + source);

Chúng ta có thể lấy được thông tin về đối tượng gây ra event từ phương thức getSource() và một số thông tin bên trong lớp đó.

Capturaae

Tạo đối tượng lắng nghe sự kiện

Có 3 cách để tạo một đối tượng lắng nghe sự kiện trong Java Swing.

  • Tạo lớp ẩn nội (Anonymous Inner class)
  • Tạo lớp nội (Inner class)
  • Tạo lớp kế thừa (Derived class)

Tạo lớp ẩn nội

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;


public class AnonymousInnerClassEx extends JFrame {


    public AnonymousInnerClassEx() {

        initUI();
    }

    private void initUI() {
        
        Container pane = getContentPane();
        GroupLayout gl = new GroupLayout(pane);
        pane.setLayout(gl);                

        JButton closeButton = new JButton("Close");
        closeButton.setBounds(40, 50, 80, 25);

        closeButton.addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent event) {
                System.exit(0);
            }
        });
        
        gl.setAutoCreateContainerGaps(true);
        
        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(closeButton)
                .addGap(220)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(closeButton)
                .addGap(180)
        );
        
        pack();        

        setTitle("Event Example");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                AnonymousInnerClassEx ex =
                        new AnonymousInnerClassEx();
                ex.setVisible(true);
            }
        });
    }
}

Trong ví dụ này chúng ta tạo một đối tượng lắng nghe sự kiện từ một button.

JButton closeButton = new JButton("Close");

Khi click button thì thoát chương trình.

closeButton.addActionListener(new ActionListener() {
    @Override
    public void actionPerformed(ActionEvent event) {
        System.exit(0);
    }
});

Để gắn một đối tượng lắng nghe vào một component thì chúng ta dùng phương thức addActionListener(). Ở đây chúng ta dùng lớp “ẩn nội”, nếu bạn chưa biết thì tức là tạo một đối tượng và code các phương thức trừu tượng ngay bên trong một phương thức khác.

Tạo lớp nội

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;

public class InnerClassExample extends JFrame {

    public InnerClassExample() {

        initUI();
    }

    private void initUI() {

        Container pane = getContentPane();
        GroupLayout gl = new GroupLayout(pane);
        pane.setLayout(gl);    

        JButton closeButton = new JButton("Close");

        ButtonCloseListener listener = new ButtonCloseListener();
        closeButton.addActionListener(listener);

        gl.setAutoCreateContainerGaps(true);
        
        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(closeButton)
                .addGap(220)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(closeButton)
                .addGap(180)
        );
        
        pack();        

        setTitle("Event Example");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private class ButtonCloseListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                InnerClassExample ex = new InnerClassExample();
                ex.setVisible(true);
            }
        });
    }
}

Ví dụ này cũng giống ví dụ trên, chỉ khác là chúng ta dùng lớp “nội”: định nghĩa một lớp bên trong một lớp khác, tuy nhiên các lớp này phải được implements từ lớp ActionListener.

private class ButtonCloseListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        System.exit(0);
    }
}

Tạo lớp kế thừa

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;

public class DerivedClassExample extends JFrame {

    public DerivedClassExample() {

        initUI();
    }

    private void initUI() {
        
        Container pane = getContentPane();
        GroupLayout gl = new GroupLayout(pane);
        pane.setLayout(gl);            
        
        MyButton closeButton = new MyButton("Close");

        gl.setAutoCreateContainerGaps(true);
        
        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(closeButton)
                .addGap(220)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(closeButton)
                .addGap(180)
        );
        
        pack();   
        
        setTitle("Event Example");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private class MyButton extends JButton 
        implements ActionListener {

        public MyButton(String text) {
            super.setText(text);
            addActionListener(this);
        }

        @Override
        public void actionPerformed(ActionEvent e) {
            System.exit(0);
        }
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                DerivedClassExample ex = new DerivedClassExample();
                ex.setVisible(true);
            }
        });
    }
}

Trong ví dụ này chúng ta cũng tạo một button có chức năng thoát chương trình, nhưng ở đây chúng ta tạo một lớp kế thừa từ lớp JButton là implements sẵn giao diện ActionListenerTức là bản thân lớp này đã tự động lắng nghe các sự kiện click rồi, không cần phải dùng đến phương thức addActionListener() như với lớp ẩn nội nữa.

private class MyButton extends JButton 
    implements ActionListener {

    public MyButton(String text) {
        super.setText(text);
        addActionListener(this);
    }

    @Override
    public void actionPerformed(ActionEvent e) {
        System.exit(0);
    }
}

Gắn một listener vào nhiều component

Một listener có thể lắng nghe từ nhiều component khác nhau.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JLabel;
import javax.swing.JPanel;

public class MultipleSources extends JFrame {

    private JLabel statusbar;

    public MultipleSources() {

        initUI();
    }

    private void initUI() {
        
        JPanel panel = new JPanel();
        GroupLayout gl = new GroupLayout(panel);
        panel.setLayout(gl);
        
        statusbar = new JLabel("Ready");

        statusbar.setBorder(BorderFactory.createEtchedBorder());
        
        ButtonListener butlist = new ButtonListener();

        JButton closeButton = new JButton("Close");
        closeButton.addActionListener(butlist);

        JButton openButton = new JButton("Open");
        openButton.addActionListener(butlist);

        JButton findButton = new JButton("Find");
        findButton.addActionListener(butlist);

        JButton saveButton = new JButton("Save");
        saveButton.addActionListener(butlist);

        gl.setAutoCreateContainerGaps(true);
        gl.setAutoCreateGaps(true);
        
        gl.setHorizontalGroup(gl.createParallelGroup()
                .addComponent(closeButton)
                .addComponent(openButton)
                .addComponent(findButton)
                .addComponent(saveButton)
                .addGap(250)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(closeButton)
                .addComponent(openButton)
                .addComponent(findButton)
                .addComponent(saveButton)
                .addGap(20)
        );
        
        gl.linkSize(closeButton, openButton, 
                findButton, saveButton);
        
        add(panel, BorderLayout.CENTER);
        add(statusbar, BorderLayout.SOUTH);
        
        pack();
                
        setTitle("Event Example");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private class ButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {

            JButton o = (JButton) e.getSource();
            String label = o.getText();
            statusbar.setText(" " + label + " button clicked");
        }
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                MultipleSources ms = new MultipleSources();
                ms.setVisible(true);
            }
        });
    }
}

Chúng ta tạo 4 button và một statusbar. Statusbar sẽ hiển thị thông tin tùy vào button nào được click.

JButton closeButton = new JButton("Close");
closeButton.addActionListener(butlist);

JButton openButton = new JButton("Open");
openButton.addActionListener(butlist);

...

Ở đây chúng ta tạo lớp nội ButtonListener và gắn vào từng button.

private class ButtonListener implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {

        JButton o = (JButton) e.getSource();
        String label = o.getText();
        statusbar.setText(" " + label + " button clicked");
    }
}

Đoạn text trên statusbar được hiển thị tùy theo từng button.

Capture

Gắn nhiều listener vào một component

Nếu có thể dùng một listener cho nhiều component thì cũng có thể gắn nhiều listener vào một component.

import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Calendar;
import javax.swing.BorderFactory;
import javax.swing.GroupLayout;
import static javax.swing.GroupLayout.Alignment.CENTER;
import static javax.swing.GroupLayout.DEFAULT_SIZE;
import static javax.swing.GroupLayout.PREFERRED_SIZE;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSpinner;
import javax.swing.SpinnerModel;
import javax.swing.SpinnerNumberModel;


public class MultipleListeners extends JFrame {

    private JLabel statusbar;
    private JSpinner spinner;
    private int count = 0;

    public MultipleListeners() {

        initUI();
    }

    private void initUI() {
        
        JPanel panel = new JPanel();
        GroupLayout gl = new GroupLayout(panel);
        panel.setLayout(gl);   
        add(panel, BorderLayout.CENTER);

        statusbar = new JLabel("0");
        statusbar.setBorder(BorderFactory.createEtchedBorder());
        add(statusbar, BorderLayout.SOUTH);

        JButton addButton = new JButton("+");
        addButton.addActionListener(new ButtonListener1());
        addButton.addActionListener(new ButtonListener2());

        Calendar calendar = Calendar.getInstance();
        int currentYear = calendar.get(Calendar.YEAR);

        SpinnerModel yearModel = new SpinnerNumberModel(currentYear,
                currentYear - 100,
                currentYear + 100,
                1);

        spinner = new JSpinner(yearModel);
        spinner.setEditor(new JSpinner.NumberEditor(spinner, "#"));

        
        gl.setAutoCreateContainerGaps(true);
        
        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addComponent(addButton)
                .addGap(20)
                .addComponent(spinner, DEFAULT_SIZE,
                        DEFAULT_SIZE, PREFERRED_SIZE)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup(CENTER)
                        .addComponent(addButton)
                        .addComponent(spinner, DEFAULT_SIZE,
                                DEFAULT_SIZE, PREFERRED_SIZE))
        );
        
        pack();         
        
        setTitle("Event Example");
        setSize(300, 200);
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private class ButtonListener1 implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            Integer val = (Integer) spinner.getValue();
            spinner.setValue(++val);
        }
    }

    private class ButtonListener2 implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            statusbar.setText(Integer.toString(++count));
        }
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                MultipleListeners ml = new MultipleListeners();
                ml.setVisible(true);
            }
        });
    }
}

Trong ví dụ này chúng ta tạo một button, một spinner và một statusbar. Chúng ta định nghĩa 2 listener và gắn cả hai listener này vào button, một listener sẽ thay đổi giá trị trên spinner, một listener sẽ thay đổi đoạn text trong statusbar.

SpinnerModel yearModel = new SpinnerNumberModel(currentYear,
        currentYear - 100,
        currentYear + 100,
        1);

spinner = new JSpinner(yearModel);

Chúng ta khởi tạo một đối tượng SpinnerModel, bạn có thể hiểu đối tượng này làm công việc khởi tạo giá trị cho JSpinner, tham số đầu tiên là giá trị khởi tạo, tham số thứ 2 và thứ 3 là giá trị min và max trong listener, tham số thứ 4 là giá trị tăng thêm lên bao nhiêu sau mỗi lần click vào spinner.

spinner.setEditor(new JSpinner.NumberEditor(spinner, "#"));

Dòng code trên có tác dụng loại bỏ dấu phẩy trong định dạng số (2,016 → 2016).

private class ButtonListener1 implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        Integer val = (Integer) spinner.getValue();
        spinner.setValue(++val);
    }
}

Listener thứ nhất thay đổi giá trị của spinner.

private class ButtonListener2 implements ActionListener {

    @Override
    public void actionPerformed(ActionEvent e) {
        statusbar.setText(Integer.toString(++count));
    }
}

Listener thứ hai làm công việc thay đổi đoạn text trong statusbar.

Capture

Gỡ bỏ listener ra khỏi component

Để làm việc này thì chỉ đơn giản là dùng phương thức removeActionListener().

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.GroupLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class RemoveListenerEx extends JFrame {

    private JLabel lbl;
    private JButton addButton;
    private JCheckBox activeBox;
    private ButtonListener buttonlistener;
    private int count = 0;

    public RemoveListenerEx() {
        
        initUI();
    }
    
    private void initUI() {

        Container pane = getContentPane();
        GroupLayout gl = new GroupLayout(pane);
        pane.setLayout(gl);  

        addButton = new JButton("+");
        buttonlistener = new ButtonListener();

        activeBox = new JCheckBox("Active listener");
        activeBox.addItemListener(new ItemListener() {

            @Override
            public void itemStateChanged(ItemEvent event) {
                
                if (activeBox.isSelected()) {
                    addButton.addActionListener(buttonlistener);
                } else {
                    addButton.removeActionListener(buttonlistener);
                }
            }
        });

        lbl = new JLabel("0");
        
        gl.setAutoCreateContainerGaps(true);
        
        gl.setHorizontalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(addButton)
                        .addComponent(lbl))
                .addGap(30)
                .addComponent(activeBox)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addGroup(gl.createParallelGroup()
                        .addComponent(addButton)
                        .addComponent(activeBox))
                .addGap(30)
                .addComponent(lbl)
        );
        
        pack(); 

        setTitle("Event Example");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    private class ButtonListener implements ActionListener {

        @Override
        public void actionPerformed(ActionEvent e) {
            
            lbl.setText(Integer.toString(++count));
        }
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                RemoveListenerEx ex = new RemoveListenerEx();
                ex.setVisible(true);
            }
        });
    }
}

Trong ví dụ này chúng ta tạo một button, một checkbox và một label. Button được gắn một listener và chỉ khi checkbox được check thì listener này mới lắng nghe sự kiện từ button.

buttonlistener = new ButtonListener();

Lớp listener được tạo ra phải là lớp không ẩn thì mới có thể gỡ ra khỏi component được,

if (activeBox.isSelected()) {
    addButton.addActionListener(buttonlistener);
} else {
    addButton.removeActionListener(buttonlistener);
}

Chúng ta kiểm tra nếu checkbox được check thì gắn listener vào còn không nếu thì gỡ ra.

Capture

Xử lý sự kiện di chuyển

Trong ví dụ này chúng ta tìm hiểu về cách bắt sự kiện có liên quan đến sự hiện diện của component.

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ComponentEvent;
import java.awt.event.ComponentListener;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;


public class MovingWindowEx extends JFrame
        implements ComponentListener {

    private JLabel labelx;
    private JLabel labely;

    public MovingWindowEx() {

        initUI();
    }

    private void initUI() {

        Container pane = getContentPane();
        GroupLayout gl = new GroupLayout(pane);
        pane.setLayout(gl);  
        
        addComponentListener(this);

        labelx = new JLabel("x: ");
        labelx.setFont(new Font("Serif", Font.BOLD, 14));
        labelx.setBounds(20, 20, 60, 25);

        labely = new JLabel("y: ");
        labely.setFont(new Font("Serif", Font.BOLD, 14));
        labely.setBounds(20, 45, 60, 25);

        gl.setAutoCreateContainerGaps(true);
        gl.setAutoCreateGaps(true);
        
        gl.setHorizontalGroup(gl.createParallelGroup()
                .addComponent(labelx)
                .addComponent(labely)
                .addGap(250)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(labelx)
                .addComponent(labely)
                .addGap(130)
        );
        
        pack();   

        setTitle("Event Example");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);
    }

    @Override
    public void componentResized(ComponentEvent e) {
    }

    @Override
    public void componentMoved(ComponentEvent e) {
        
        int x = e.getComponent().getX();
        int y = e.getComponent().getY();
        
        labelx.setText("x: " + x);
        labely.setText("y: " + y);
    }

    @Override
    public void componentShown(ComponentEvent e) {
    }

    @Override
    public void componentHidden(ComponentEvent e) {
    }


    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                MovingWindowEx ex = new MovingWindowEx();
                ex.setVisible(true);
            }
        });
    }
}

Chúng ta sẽ gắn một listener vào cửa sổ chính để lấy tọa độ của cửa sổ trên màn hình.

public class MovingWindowExample extends JFrame
        implements ComponentListener {

Giao diện ComponentListener là giao diện dùng để lắng nghe các sự kiện liên quan đến sự xuất hiện của component.

@Override
public void componentResized(ComponentEvent e) {
}

@Override
public void componentMoved(ComponentEvent e) {
    
    int x = e.getComponent().getX();
    int y = e.getComponent().getY();
    
    labelx.setText("x: " + x);
    labely.setText("y: " + y);
}

@Override
public void componentShown(ComponentEvent e) {
}

@Override
public void componentHidden(ComponentEvent e) {
}

Theo luật của Java thì chúng ta phải override toàn bộ phương thức ảo nhưng ở đây chúng ta chỉ quan tâm tới phương thức componentMoved() thôi.

int x = e.getComponent().getX();
int y = e.getComponent().getY();

Để lấy tọa độ của component thì chúng ta dùng phương thức getX()getY().

Capture

Sử dụng lớp Adapter thay thế cho Listener

Trong ví dụ trên chúng ta implement giao diện ComponentListener và phải override toàn bộ các phương thức ảo trong giao diện này, như thế rất khó chịu, chính vì thế và Java đã tạo ra các lớp adapter. Adapter đơn giản là các lớp đã implement sẵn các lớp listener tương ứng và override toàn bộ phương thức ảo trước rồi. Vì thế nên khi dùng adapter chúng ta chỉ cần dùng các phương thức mà chúng ta muốn thôi.

Ví dụ:

  • Giao diện ComponentListener bắt buộc bạn phải override các phương thức ảo componentResized(), componentMoved(), componentShown(), và componentHidden() mặc dù bạn không muốn.
  • Lớp ComponentAdapter đã implement sẵn giao diện ComponentListener và override toàn bộ các phương thức ảo trên rồi, bạn chỉ cần override lại phương thức mà bạn muốn.

Ví dụ dưới đây giống như ví dụ ở trên, chỉ khác là chúng ta dùng lớp ComponentAdapter.

import java.awt.Container;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;

public class AdapterExample extends JFrame {

    private JLabel labelx;
    private JLabel labely;

    public AdapterExample() {

        initUI();
    }

    private void initUI() {

        Container pane = getContentPane();
        GroupLayout gl = new GroupLayout(pane);
        pane.setLayout(gl);  
        
        addComponentListener(new MoveAdapter());

        labelx = new JLabel("x: ");
        labelx.setFont(new Font("Serif", Font.BOLD, 14));
        
        labely = new JLabel("y: ");
        labely.setFont(new Font("Serif", Font.BOLD, 14));

        gl.setAutoCreateContainerGaps(true);
        gl.setAutoCreateGaps(true);
        
        gl.setHorizontalGroup(gl.createParallelGroup()
                .addComponent(labelx)
                .addComponent(labely)
                .addGap(250)
        );

        gl.setVerticalGroup(gl.createSequentialGroup()
                .addComponent(labelx)
                .addComponent(labely)
                .addGap(130)
        );
        
        pack();   

        setTitle("Event Example");
        setLocationRelativeTo(null);
        setDefaultCloseOperation(EXIT_ON_CLOSE);        
    }

    private class MoveAdapter extends ComponentAdapter {

        @Override
        public void componentMoved(ComponentEvent e) {
            
            int x = e.getComponent().getX();
            int y = e.getComponent().getY();
            
            labelx.setText("x: " + x);
            labely.setText("y: " + y);
        }
    }

    public static void main(String[] args) {

        EventQueue.invokeLater(new Runnable() {

            @Override
            public void run() {
                AdapterExample ex = new AdapterExample();
                ex.setVisible(true);
            }
        });
    }
}