使用复选框创建JComboBox

时间:2016-05-11 23:59:17

标签: java swing user-interface jcombobox jcheckbox

TL:DR我想创建一个JComboBox,其中JCheckboxes代替JLabels,但在主标签中 - 在这个地方我想保留字符串代表所有选中的值框。我设法让它工作,但是我在点击鼠标时保持菜单打开时遇到了一些麻烦。我还希望箭头键只是在框中导航而不选择要选择的项目和空格键。

无论我做什么,在我的ActionListener捕获鼠标并选择一个项目之后,菜单通过嵌套在JComboBox和方法setPopupVisible中的其他一些监听器关闭 - 它适用于我的丑陋的解决方法,但必须有一个更好的方法来做到这一点..

我相信我需要listBox课程BasicComboBoxUI上的小鼠听众,但我该如何到达那里?

尽可能少的代码缩小到:

ComboCheckBoxModel:

import javax.swing.*;
import java.awt.*;
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

public class ComboCheckBoxModel extends DefaultListModel implements ComboBoxModel, ItemSelectable {


    private List<String> items = new ArrayList<>();
    private List<String> checkedItems = new ArrayList<>();
    private String selectedItem;

    public ComboCheckBoxModel(List<String> items) {
        if(!items.isEmpty()) {
            selectedItem = items.get(0);
        }
        this.items = items;
    }

    public void check(Object item) {
        String itemAsString = item.toString();
        if(checkedItems.contains(itemAsString)) {
            checkedItems.remove(itemAsString);
        } else {
            checkedItems.add(itemAsString);
        }
        fireContentsChanged(this, items.indexOf(item), items.indexOf(item));
    }

    public String getDataStringRepresentation() {
        return checkedItems.stream().sorted().collect(Collectors.joining(", "));
    }

    @Override
    public void setSelectedItem(Object anItem) {
        selectedItem = anItem.toString();
    }

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

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

    @Override
    public Object getElementAt(int index) {
        return items.get(index);
    }

    public boolean isChecked(String stringValue) {
        return checkedItems.contains(stringValue);
    }

    @Override
    public Object[] getSelectedObjects() {
        return items.toArray();
    }

    @Override
    public void addItemListener(ItemListener l) {

    }

    @Override
    public void removeItemListener(ItemListener l) {

    }
}

的JComboBox:

import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.InputEvent;
import java.awt.event.KeyEvent;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;

public class JComboCheckBox extends JComboBox {

    private final ComboCheckBoxModel model = new ComboCheckBoxModel(Stream.of("bum", "kabum", "dabum").collect(toList()));
    private boolean shouldntClose;

    public JComboCheckBox() {
        super();
        setRenderer(new CheckBoxCellRenderer());
        setModel(model);

        addActionListener(new ActionListener() {
            @Override
            public void actionPerformed(ActionEvent e) {
                if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
                    System.out.println("LPM MASK");
                    model.check(model.getSelectedItem());
                    shouldntClose = true;
                }
            }
        });
    }

    @Override
    protected boolean processKeyBinding(KeyStroke ks, KeyEvent e, int condition, boolean pressed) {
        if (ks.getKeyCode() == KeyEvent.VK_SPACE && isPopupVisible()) {
            if (ks.getKeyCode() == KeyEvent.VK_SPACE && condition == WHEN_FOCUSED && ks.isOnKeyRelease()) {
                System.out.println("ks = [" + ks + "], e = [" + e + "], condition = [" + condition + "], pressed = [" + pressed + "]");
                model.check(model.getSelectedItem());
            }
            return true;
        }
        return super.processKeyBinding(ks, e, condition, pressed);
    }


    @Override
    public void setPopupVisible(boolean v) {
        if (shouldntClose) {
            shouldntClose = false;
            return;
        } else {
            super.setPopupVisible(v);
        }

    }

    @Override
    public Object getSelectedItem() {
        return model.getSelectedItem();
    }
}

细胞渲染器:

import javax.swing.*;
import java.awt.*;

public class CheckBoxCellRenderer extends JCheckBox implements ListCellRenderer {
    @Override
    public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {

        ComboCheckBoxModel model = (ComboCheckBoxModel) list.getModel();
        if (index != -1) {
            String stringValue = value == null ? "null" : value.toString();
            setText(stringValue);

            boolean checked = model.isChecked(stringValue);
            setSelected(checked);

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

            return this;
        } else {
            String stringValue = model.getDataStringRepresentation();
            return new JLabel(stringValue);
        }
    }
}

发射器:

import javax.swing.*;

public class Launcher {
    public JFrame create() {
        JFrame f=new JFrame("Type the name of frame");
        f.add(new JComboCheckBox());
        f.setSize(400,400);
        return f;
    }

    public static void main(String[] args) {
        Launcher launcher = new Launcher();
        launcher.create().setVisible(true);
    }
}

1 个答案:

答案 0 :(得分:0)

  

类BasicComboBoxUI中的listBox但是我如何到达那里?

listBox(JList?)可以从BasicComboPopup获取:

Accessible a = getAccessibleContext().getAccessibleChild(0);
if (a instanceof BasicComboPopup) {
  BasicComboPopup pop = (BasicComboPopup) a;
  int index = pop.getList().getSelectedIndex();
  //...
}

ComboCheckBoxTest.java

import java.awt.*;
import java.awt.event.*;
import java.util.*;
import java.util.List;
import java.util.stream.Collectors;
import javax.accessibility.*;
import javax.swing.*;
import javax.swing.plaf.basic.*;

public class ComboCheckBoxTest {
  public JComponent makeUI() {
    CheckableItem[] m = {
      new CheckableItem("bum",   false),
      new CheckableItem("kabum", false),
      new CheckableItem("dabum", false)
    };
    JPanel p = new JPanel();
    p.add(new JComboCheckBox<CheckableItem>(m));
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame("Type the name of frame");
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new ComboCheckBoxTest().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class CheckableItem {
  public final String text;
  public boolean selected;
  protected CheckableItem(String text, boolean selected) {
    this.text = text;
    this.selected = selected;
  }
  @Override public String toString() {
    return text;
  }
}

class CheckBoxCellRenderer<E extends CheckableItem> implements ListCellRenderer<E> {
  private final JLabel label = new JLabel(" ");
  private final JCheckBox check = new JCheckBox(" ");
  @Override public Component getListCellRendererComponent(
      JList list, CheckableItem value, int index,
      boolean isSelected, boolean cellHasFocus) {
    ListModel model = list.getModel();
    if (index < 0) {
      label.setText(getDataStringRepresentation(model));
      return label;
    } else {
      check.setText(Objects.toString(value, "null"));
      check.setSelected(value.selected);
      if (isSelected) {
        check.setBackground(list.getSelectionBackground());
        check.setForeground(list.getSelectionForeground());
      } else {
        check.setBackground(list.getBackground());
        check.setForeground(list.getForeground());
      }
      return check;
    }
  }
  private String getDataStringRepresentation(ListModel model) {
    List<String> sl = new ArrayList<>();
    for (int i = 0; i < model.getSize(); i++) {
      Object o = model.getElementAt(i);
      if (o instanceof CheckableItem && ((CheckableItem) o).selected) {
        sl.add(o.toString());
      }
    }
    return sl.stream().sorted().collect(Collectors.joining(", "));
  }
}

class JComboCheckBox<E extends CheckableItem> extends JComboBox<E> {
  private boolean shouldntClose;
  private transient ActionListener listener;

  public JComboCheckBox() {
    super();
  }

  public JComboCheckBox(E[] m) {
    super(m);
  }

  @Override public Dimension getPreferredSize() {
    return new Dimension(200, 20);
  }

  @Override public void updateUI() {
    setRenderer(null);
    removeActionListener(listener);
    super.updateUI();
    listener = e -> {
      if (e.getModifiers() == InputEvent.BUTTON1_MASK) {
        System.out.println("LPM MASK: " + getSelectedIndex());
        updateItem(getSelectedIndex());
        shouldntClose = true;
      }
    };
    setRenderer(new CheckBoxCellRenderer<CheckableItem>());
    addActionListener(listener);
    getInputMap(JComponent.WHEN_FOCUSED).put(
        KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "space-key-select");
    getActionMap().put("space-key-select", new AbstractAction() {
      @Override public void actionPerformed(ActionEvent e) {
        System.out.println("JComboBox#getSelectedIndex()" + getSelectedIndex());
        Accessible a = getAccessibleContext().getAccessibleChild(0);
        if (a instanceof BasicComboPopup) {
          BasicComboPopup pop = (BasicComboPopup) a;
          int i = pop.getList().getSelectedIndex();
          System.out.println("JList#getSelectedIndex() " + i);
          updateItem(i);
        }
      }
    });
  }

  private void updateItem(int index) {
    if (isPopupVisible()) {
      E item = getItemAt(index);
      item.selected ^= true;
      removeItemAt(index);
      insertItemAt(item, index);
      setSelectedItem(item);
    }
  }

  @Override
  public void setPopupVisible(boolean v) {
    if (shouldntClose) {
      shouldntClose = false;
      return;
    } else {
      super.setPopupVisible(v);
    }
  }
}