Java: Cách sử dụng Combo Box

JComboBox, giúp người dùng chọn một trong các lựa chọn, và có thể có hai dạng khác nhau. Dạng mặc định là loại combo box không cho phép chỉnh sửa, đặc điểm của dạng này là gồm một nút và một danh sách drop-down các giá trị. Dạng thứ hai là combo box cho phép chỉnh sửa, đặc điểm của dạng này là nó bao gồm một text field cùng với một nút nhỏ ngay cạnh text field; người dùng có thể điền giá trị vào text field hoặc click vào nút nhỏ để hiển thị danh sách drop-down. Dưới đây là minh họa cho hai loại combo box này:

An uneditable combo box

An editable combo box

An uneditable combo box

An editable combo box

Combo box không chỉnh sửa được, trước và sau khi click chuộtCombo box cho phép chỉnh sửa, trước và sau khi click chuột

Combo box yêu cầu ít không gian hơn, và dạng cho phép chỉnh sửa (text field) rất hữu dụng với người dùng vì nó giúp người dùng chọn nhanh hơn, bởi vì chỉ cần điền ký tự đầu tiên là nó sẽ hiển thị các giá trị gợi ý.

Bài viết này sẽ trình bày những vấn đề sau:

  • Cách sử dụng Combo Box không chỉnh sửa được
  • Xử lý sự kiện trên Combo Box
  • Cách sử dụng Combo Box chỉnh sửa được
  • Cung cấp Renderer tùy chỉnh
  • API Combo Box
  • Các ví dụ có sử dụng Combo Box

Cách sử dụng Combo Box không chỉnh sửa được

Hình dưới đây thể hiện một ứng dụng combo box không chỉnh sửa được, nó cho phép người dùng chọn một con vật:

An uneditable combo box

 

Đoạn mã sau trính từ ví dụ ComboBoxDemo, dùng để tạo và thiết lập một uneditable combo box:

String[] petStrings = { “Bird”, “Cat”, “Dog”, “Rabbit”, “Pig” };

//tạo combo box, selected cho mục có chỉ số = 4 (Pig).
JComboBox petList = new JComboBox(petStrings);
petList.setSelectedIndex(4);
petList.addActionListener(this);

Combo box tạo ra chứa một mảng chuỗi, nhưng ta cũng có thể thay thế bằng các icon được. Để đặt thành phần nào đó vào combo box hoặc tùy chỉnh các mục trong nó thì ta cần viết một renderer. Mỗi editable combo box cũng nên có một editor tuỳ chỉnh.

Đoạn mã trên cũng đăng ký một action listener trên combo box. Xin xem phần Xử lý sự kiện trên Combo Box ở dưới để hiểu chi tiết hơn.

Combo box sử dụng một combo box model để chứa và quản lý các mục của nó. Khi ta khởi tạo một combo box với một mảng hay một vector thì combo box sẽ tạo một đối tượng model mặc định cho bạn. Cũng như những thành phần Swing khác thì ta có thể tùy chỉnh combo box bằng cách thực thi model tùy chỉnh – đó là một đối tượng thực thi giao diện ComboBoxModel.

Lưu ý: 

Hãy cẩn thận khi thực thi một model tùy chỉnh cho combo box. Các phương thức của 

JComboBox

 sẽ thay đổi các mục của combo box như

insertItemAt

, nó chỉ làm việc nếu model dữ liệu thực thi giao diện 

MutableComboBoxModel

 (một giao diện con của 

ComboBoxModel

). Xin xem thêm API bên dưới để biết những phương thức nào gây ảnh hưởng.

Hãy đảm bảo rằng model tùy chỉnh phải kích hoạt các sự kiện dữ liệu khi dữ liệu của combo box hoặc trạng thái của nó thay đổi. Ngay cả khi ta không có sự thay đổi model combo box thì dữ liệu cũng không bao giờ thay đổi, khi đó phải kích hoạt một sự kiện dữ liệu (sự kiện CONTENTS_CHANGED) khi có sự thay đổi lựa chọn ở combo box. Có một cách để lấy sự kiện dữ liệu là kích hoạt mã lệnh để sao cho model combo box là một lớp con của AbstractListModel.

Đoạn mã dưới đây trích từ ví dụ ComboBoxDemo.java dùng để đăng ký và thực thi một bộ lắng nghe hành động (action listener) trên combo box:

public class ComboBoxDemo … implements ActionListener {
    . . .
        petList.addActionListener(this) {
    . . .
    public void actionPerformed(ActionEvent e) {
        JComboBox cb = (JComboBox)e.getSource();
        String petName = (String)cb.getSelectedItem();
        updateLabel(petName);
    }
    . . .
}

Bộ lắng nghe hành động này sẽ lấy lựa chọn được chọn để từ đó lấy tên của file ảnh và hiển thị ra một nhãn. Combo box sẽ kích hoạt sự kiện hành động khi người dùng chọn một mục từ combo box.

Combo boxes also generate item events, which are fired when any of the items’ selection state changes. Only one item at a time can be selected in a combo box, so when the user makes a new selection the previously selected item becomes unselected. Thus two item events are fired each time the user selects a different item from the menu. If the user chooses the same item, no item events are fired. Use addItemListener to register an item listener on a combo box. How to Write an Item Listener gives general information about implementing item listeners.

Although JComboBox inherits methods to register listeners for low-level events — focus, key, and mouse events, for example — we recommend that you don’t listen for low-level events on a combo box. Here’s why: A combo box is a compound component — it is comprised of two or more other components. The combo box itself fires high-level events such as action events. Its subcomponents fire low-level events such as mouse, key, and focus events. The low-level events and the subcomponent that fires them are look-and-feel-dependent. To avoid writing look-and-feel-dependent code, you should listen only for high-level events on a compound component such as a combo box. For information about events, including a discussion about high- and low-level events, refer toWriting Event Listeners.

Here’s a picture of a demo application that uses an editable combo box to enter a pattern with which to format dates.

An editable combo box

Try this: 

  1. Click the Launch button to run the ComboBox2 Demo using Java™ Web Start (download JDK 7 or later). Alternatively, to compile and run the example yourself, consult the example index.Launches the ComboBoxDemo2 example

     

  2. Enter a new pattern by choosing one from the combo box’s menu. The program reformats the current date and time.
  3. Enter a new pattern by typing one in and pressing Enter. Again the program reformats the current date and time.

The following code, taken from ComboBoxDemo2.java, creates and sets up the combo box:

String[] patternExamples = {
         "dd MMMMM yyyy",
         "dd.MM.yy",
         "MM/dd/yy",
         "yyyy.MM.dd G 'at' hh:mm:ss z",
         "EEE, MMM d, ''yy",
         "h:mm a",
         "H:mm:ss:SSS",
         "K:mm a,z",
         "yyyy.MMMMM.dd GGG hh:mm aaa"
};
. . .
JComboBox patternList = new JComboBox(patternExamples);
patternList.setEditable(true);
patternList.addActionListener(this);

This code is very similar to the previous example, but warrants a few words of explanation. The bold line of code explicitly turns on editing to allow the user to type values in. This is necessary because, by default, a combo box is not editable. This particular example allows editing on the combo box because its menu does not provide all possible date formatting patterns, just shortcuts to frequently used patterns.

An editable combo box fires an action event when the user chooses an item from the menu and when the user types Enter. Note that the menu remains unchanged when the user enters a value into the combo box. If you want, you can easily write an action listener that adds a new item to the combo box’s menu each time the user types in a unique value.

See Internationalization to learn more about formatting dates and other types of data.

A combo box uses a renderer to display each item in its menu. If the combo box is uneditable, it also uses the renderer to display the currently selected item. An editable combo box, on the other hand, uses an editor to display the selected item. A renderer for a combo box must implement the ListCellRenderer interface. A combo box’s editor must implement ComboBoxEditor. This section shows how to provide a custom renderer for an uneditable combo box.

The default renderer knows how to render strings and icons. If you put other objects in a combo box, the default renderer calls thetoString method to provide a string to display. You can customize the way a combo box renders itself and its items by implementing your own ListCellRenderer.

Here’s a picture of an application that uses a combo box with a custom renderer:

A combo box with a custom renderer

Click the Launch button to run the CustomComboBox Demo using Java™ Web Start (download JDK 7 or later). Alternatively, to compile and run the example yourself, consult the example index.

Launches the CustomComboBoxDemo example

The full source code for this example is in CustomComboBoxDemo.java. To get the image files it requires, consult the example index.

The following statements from the example create an instance of ComboBoxRenderer (a custom class) and set up the instance as the combo box’s renderer:

JComboBox petList = new JComboBox(intArray);
. . .
ComboBoxRenderer renderer = new ComboBoxRenderer();
renderer.setPreferredSize(new Dimension(200, 130));
petList.setRenderer(renderer);
petList.setMaximumRowCount(3);

The last line sets the combo box’s maximum row count, which determines the number of items visible when the menu is displayed. If the number of items in the combo box is larger than its maximum row count, the menu has a scroll bar. The icons are pretty big for a menu, so our code limits the number of rows to 3. Here’s the implementation of ComboBoxRenderer, a renderer that puts an icon and text side-by-side:

class ComboBoxRenderer extends JLabel
                       implements ListCellRenderer {
    . . .
    public ComboBoxRenderer() {
        setOpaque(true);
        setHorizontalAlignment(CENTER);
        setVerticalAlignment(CENTER);
    }

    /*
     * This method finds the image and text corresponding
     * to the selected value and returns the label, set up
     * to display the text and image.
     */
    public Component getListCellRendererComponent(
                                       JList list,
                                       Object value,
                                       int index,
                                       boolean isSelected,
                                       boolean cellHasFocus) {
        //Get the selected index. (The index param isn't
        //always valid, so just use the value.)
        int selectedIndex = ((Integer)value).intValue();

        if (isSelected) {
            setBackground(list.getSelectionBackground());
            setForeground(list.getSelectionForeground());
        } else {
            setBackground(list.getBackground());
            setForeground(list.getForeground());
        }

        //Set the icon and text.  If icon was null, say so.
        ImageIcon icon = public/images/article[selectedIndex];
        String pet = petStrings[selectedIndex];
        setIcon(icon);
        if (icon != null) {
            setText(pet);
            setFont(list.getFont());
        } else {
            setUhOhText(pet + " (no image available)",
                        list.getFont());
        }

        return this;
    }
    . . .
}

As a ListCellRendererComboBoxRenderer implements a method called getListCellRendererComponent, which returns a component whose paintComponent method is used to display the combo box and each of its items. The easiest way to display an image and an icon is to use a label. So ComboBoxRenderer is a subclass of label and returns itself. The implementation of getListCellRendererComponent configures the renderer to display the currently selected icon and its description.

These arguments are passed to getListCellRendererComponent:

  • JList list — a list object used behind the scenes to display the items. The example uses this object’s colors to set up foreground and background colors.
  • Object value — the object to render. An Integer in this example.
  • int index — the index of the object to render.
  • boolean isSelected — indicates whether the object to render is selected. Used by the example to determine which colors to use.
  • boolean cellHasFocus — indicates whether the object to render has the focus.

Note that combo boxes and lists use the same type of renderer — ListCellRenderer. You can save yourself some time by sharing renderers between combo boxes and lists, if it makes sense for your program.

The following tables list the commonly used JComboBox constructors and methods. Other methods you are most likely to invoke on a JComboBox object are those it inherits from its superclasses, such as setPreferredSize. See The JComponent API for tables of commonly used inherited methods.

The API for using combo boxes falls into two categories:

 

Customizing the Combo Box’s OperationMethod or ConstructorPurposevoid addActionListener(ActionListener)Add an action listener to the combo box. The listener’s actionPerformed method is called when the user selects an item from the combo box’s menu or, in an editable combo box, when the user presses Enter.void addItemListener(ItemListener)Add an item listener to the combo box. The listener’s itemStateChanged method is called when the selection state of any of the combo box’s items change.void setEditable(boolean)
boolean isEditable()Set or get whether the user can type in the combo box.void setRenderer(ListCellRenderer)
ListCellRenderer getRenderer()Set or get the object responsible for painting the selected item in the combo box. The renderer is used only when the combo box is uneditable. If the combo box is editable, the editor is used to paint the selected item instead.void setEditor(ComboBoxEditor)
ComboBoxEditor getEditor()Set or get the object responsible for painting and editing the selected item in the combo box. The editor is used only when the combo box is editable. If the combo box is uneditable, the renderer is used to paint the selected item instead.

This table shows the examples that use JComboBox and where those examples are described.

ExampleWhere DescribedNotesComboBoxDemoThis sectionUses an uneditable combo box.ComboBoxDemo2This sectionUses an editable combo box.CustomComboBoxDemoThis sectionProvides a custom renderer for a combo box.TableRenderDemoHow to Use Tables (Using a Combo Box as an Editor)Shows how to use a combo box as a table cell editor.