0

我想在 Swing 中创建一个多选组合框,显示用户选择的项目,用分号或其他字符分隔。

例如:

选择文章<- 显示用户的选择 选择 文章
没有
文章

如果用户选择了“a”和“the”,则将显示“a; the”而不是“Select article(s)”。

我曾尝试编写这样的组合,但我的问题是“选择文章”没有被当前用户选择替换。

您只能看到类似的内容:
Select article(s) <- 显示用户的选择(不被“a; the”替换)
a; 没有
文章_
_

这是我的代码:

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.plaf.basic.BasicComboBoxRenderer;

public class MultiSelectionComboBox {

    private DefaultComboBoxModel model;

    private JPanel getContent() {
        Object[] items = { "Select article(s)", "No article", "a", "the" };

        model = new DefaultComboBoxModel(items);
        JComboBox combo = new JComboBox(model);

        SelectionManager manager = new SelectionManager();
        manager.setNonSelectable(items[0]);

        Renderer renderer = new Renderer(manager);
        combo.addActionListener(manager);
        combo.setRenderer(renderer);

        JPanel panel = new JPanel();
        panel.add(combo);
        return panel;
    }

    class SelectionManager implements ActionListener {
        JComboBox combo = null;
        private List<Object> selectedItems = new ArrayList<Object>();
        private Object nonSelectable;

        public void setNonSelectable(Object val) {
            nonSelectable = val;
        }
        public void actionPerformed(ActionEvent e) {
            if (combo == null) {
                combo = (JComboBox) e.getSource();
            }
            Object item = combo.getSelectedItem();
            // Toggle the selection state for item.  
            if (selectedItems.contains(item)) {
                selectedItems.remove(item);
            } else if (!item.equals(nonSelectable)) {
                selectedItems.add(item);
            }

            combo.setSelectedIndex(0);
        }

        public List<Object> getSelectedItems() {
            return selectedItems;
        }
    }

    class Renderer extends BasicComboBoxRenderer {
        SelectionManager selectionManager;

        public Renderer(SelectionManager sm) {
            selectionManager = sm;
        }

        public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected,
                boolean cellHasFocus) {
            setFont(list.getFont());

            if (index == 0) { // first item shows currently selected items delimited by ;
                StringBuffer firstItem = new StringBuffer();
                for (Object sel : selectionManager.getSelectedItems()) {
                    firstItem.append(sel + "; ");
                }
                if (firstItem.toString().endsWith("; ")) {
                    firstItem.deleteCharAt(firstItem.length() - 2);
                }
                setText((value == null) ? "" : firstItem.toString());
            } else {// other items
                setText((value == null) ? "" : value.toString());
            }

            return this;
        }
    }

    public static void main(String[] args) {
        JFrame f = new JFrame();
        f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        f.getContentPane().add(new MultiSelectionComboBox().getContent());
        f.setSize(300, 145);
        f.setLocation(200, 200);
        f.setVisible(true);
    }
}

我知道组合不适合多选,但在我的情况下,我没有看到更好的 UI 元素,因为我想将这样的组合放在句子中。例如:“ |a; the|键在哪里?”

4

2 回答 2

4

在您的单元格渲染器中,您假设索引 0 是所选值,但事实并非如此。它实际上是-1(或者更准确地说,这是用来表示编辑器值的索引)

public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
    setFont(list.getFont());

    if (index == -1 && selectionManager.getSelectedItems().size() > 0) {
        StringBuffer firstItem = new StringBuffer();
        for (Object sel : selectionManager.getSelectedItems()) {
            firstItem.append(sel + "; ");
        }
        if (firstItem.toString().endsWith("; ")) {
            firstItem.deleteCharAt(firstItem.length() - 2);
        }
        setText((value == null) ? "" : firstItem.toString());
    } else {// other items
        setText((value == null) ? "" : value.toString());
    }

    return this;
}
于 2012-07-28T07:24:39.053 回答
2

The base problem with your code is that you pack model responsibilities (== decide about selection) into your view (== renderer + action code). Instead, solve them where they belong, that is the model. Below is a very simplistic implementation, just to point the direction:

@SuppressWarnings({ "unchecked", "rawtypes" })
public static class MyComboBoxModel extends AbstractListModel
    implements ComboBoxModel {
    public static Object NONE = "none"; 
    List values = new ArrayList();
    List selected = new ArrayList();

    public MyComboBoxModel(Object... values) {
        for (Object object : values) {
            this.values.add(object);
        }
    }

    @Override
    public int getSize() {
        return values.size();
    }

    @Override
    public Object getElementAt(int index) {
        return values.get(index);
    }
    @Override
    public void setSelectedItem(Object anItem) {
        if (anItem == null || anItem == NONE) {
            if (selected.isEmpty()) return;
            selected.clear();
        } else {
            boolean removed = selected.remove(anItem);
            if (!removed) {
                selected.add(anItem);
            }
        }
        fireContentsChanged(this, -1, -1);
    }

    @Override
    public Object getSelectedItem() {
        return selected;
    }

}

// use
MyComboBoxModel model = new MyComboBoxModel(MyComboBoxModel.NONE , "a", "the", "other");
model.setSelectedItem(null);
JComboBox box = new JComboBox(model);

Even with the standard renderer we can see it working :-) A custom renderer can further fine tune the visual representation as you need it - without containing any data related logic.

于 2012-07-28T10:01:22.480 回答