Using the ComboBoxEditor interface with Custom JCo

2020-05-07 06:51发布

I was checking the documentation.

... and I'm trying to use my Custom ComboBox implementing the interface ComboBoxEditor.

Here my complete Code...

I hava one JPanel with JComponents...

class ThePanel extends JPanel {

  private Font intFont = new Font("Monospaced", Font.PLAIN, 8);

  private final JToggleButton jtbEnabled = new JToggleButton();
  private final JToggleButton jtbDefaultOperation = new JToggleButton();
  private final JTextField jtfText = new JTextField("", 0);

  private final JPanel jpThePanel = new JPanel();

  public ThePanel(Font extFont) {
    this(new TheModel(), extFont);
  }

  public ThePanel(TheModel data, Font extFont) {
    super();
    intFont = extFont;
    initEnabled();
    initDefaultOperation();
    initText();
    setData(data);

    jpThePanel.setLayout(new BoxLayout(jpThePanel, BoxLayout.LINE_AXIS));
    jpThePanel.add(Box.createRigidArea(new Dimension(2, 0)));
    jpThePanel.add(jtbEnabled);
    jpThePanel.add(jtbDefaultOperation);
    jpThePanel.add(jtfText);
    jpThePanel.add(Box.createRigidArea(new Dimension(2, 0)));
    init();
  }

  private void init() {
    setFont(intFont);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));
    setBackground(new Color(0, 0, 0, 0/*64*/));
    add(jpThePanel);
    Dimension d = getMinimumSize();
    setSize(d);
    setPreferredSize(d);
    setMaximumSize(d);
    setSize(d);
  }

  private void initEnabled() {
    jtbEnabled.setFont(intFont);
    jtbEnabled.setIcon(new ImageIcon(getClass().getResource("Unselected.png")));
    jtbEnabled.setSelectedIcon(new ImageIcon(getClass().getResource("Selected.png")));
  }

  private void initDefaultOperation() {
    jtbDefaultOperation.setFont(intFont);
    jtbDefaultOperation.setIcon(new ImageIcon(getClass().getResource("Insert.png")));
    jtbDefaultOperation.setSelectedIcon(new ImageIcon(getClass().getResource("Extract.png")));
    jtbDefaultOperation.setDisabledIcon(new ImageIcon(getClass().getResource("DisabledInsert.png")));
    jtbDefaultOperation.setDisabledSelectedIcon(new ImageIcon(getClass().getResource("DisabledExtract.png")));
  }

  private void initText() {
    jtfText.setFont(intFont);
  }

  public TheModel getData() {
    return new TheModel(
        jtbEnabled.isSelected(),
        jtbDefaultOperation.isSelected(),
        jtfText.getText()
    );
  }

  public void setData(TheModel data) {
    jtbEnabled.setSelected(data.getEnabled());
    jtbDefaultOperation.setEnabled(data.getEnabled());
    jtbDefaultOperation.setSelected(data.getDefaultOperation());
    jtfText.setText(data.getText());
  }

  public void selectAll() {
    jtfText.selectAll();
  }

}

Now, I have the Model Data for its inner components...

class TheModel {

  private Boolean enabled = true;
  private Boolean defaultOperation = true;
  private String text = "";

  public TheModel() {
    this("");
  }

  public TheModel(String text) {
    this(true, text);
  }

  public TheModel(Boolean defaultOperation, String text) {
    this(true, defaultOperation, text);
  }

  public TheModel(Boolean enabled, Boolean defaultOperation, String text) {
    this.enabled = enabled;
    this.defaultOperation = defaultOperation;
    this.text = text;
  }

  public Boolean getEnabled() {
    return enabled;
  }

  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }

  public Boolean getDefaultOperation() {
    return defaultOperation;
  }

  public void setDefaultOperation(Boolean defaultOperation) {
    this.defaultOperation = defaultOperation;
  }

  public String getText() {
    return text;
  }

  public void setText(String text) {
    this.text = text;
  }

  @Override
  public String toString() {
    return "TheModel{" + "enabled=" + enabled + ", defaultOperation=" + defaultOperation + ", text=" + text + '}';
  }

}

I want to use my Custom Component (ThePanel) in a JComboBox and manipulate the inner elements the JToggleButtons and the JTextField according to documentation... I need to implement the ComboBoxEditor interface. NOTE: The ListCellRenderer interface was trying to show the list... because implementng only ComboBoxEditor was not working.

class TheComboBoxEditor implements ComboBoxEditor, ListCellRenderer {

  protected ThePanel editor;
  private final EventListenerList listenerList = new EventListenerList();

  public TheComboBoxEditor(Font extFont) {
    editor = new ThePanel(extFont);
  }

  private void fireActionEvent(ActionEvent evt) {
    for (ActionListener al : listenerList.getListeners(ActionListener.class)) {
      al.actionPerformed(evt);
    }
  }

  @Override
  public void addActionListener(ActionListener l) {
    listenerList.add(ActionListener.class, l);
  }

  @Override
  public void removeActionListener(ActionListener l) {
    listenerList.remove(ActionListener.class, l);
  }

  @Override
  public void selectAll() {
    editor.selectAll();
    editor.requestFocus();
  }

  @Override
  public Object getItem() {
    return editor.getData();
  }

  @Override
  public void setItem(Object anObject) {
    if (anObject != null) {
      if (anObject instanceof TheModel) {
        editor.setData((TheModel) anObject);
      }
      if (anObject instanceof String) {
        editor.setData(new TheModel(anObject.toString()));
      }
    } else {
      editor.setData(new TheModel());
    }
  }

  @Override
  public Component getEditorComponent() {
    return editor;
  }

  @Override
  public Component getListCellRendererComponent(JList list, Object value,
      int index, boolean isSelected, boolean cellHasFocus) {
    TheModel data = (TheModel) value;
    editor.setData(data);
    return editor;
  }

}

Now, An Example of to use my TheComboBoxEditor class.

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

public class ExampleComboBoxEditor extends JFrame {

  public ExampleComboBoxEditor() {
    TheModel items[] = {
      new TheModel(true, true, "True, True"),
      new TheModel(true, false, "True, False"),
      new TheModel(false, true, "False, True"),
      new TheModel(false, false, "False, False")};

    String strings[] = {
      "True, True", "True, False", "False, True", "False, False"};

    JComboBox internalComboBox = new JComboBox(items);
    internalComboBox.setEditable(true);
    internalComboBox.setEditor(new TheComboBoxEditor(this.getFont()));
    internalComboBox.setMaximumRowCount(4);
    internalComboBox.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
      }
    });

    JComboBox stringsComboBox = new JComboBox(strings);
    stringsComboBox.setEditable(true);
    stringsComboBox.setMaximumRowCount(4);
    stringsComboBox.addActionListener(new ActionListener() {
      public void actionPerformed(ActionEvent e) {
        System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
      }
    });

    JPanel outer = new JPanel();
    outer.add(internalComboBox);
    outer.add(stringsComboBox);
    add(outer);
    setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  }

  public static void main(String[] args) {
    ExampleComboBoxEditor app = new ExampleComboBoxEditor();
    app.setSize(840, 240);
    app.setVisible(true);
  }
}

Now, I have my doubts (for me the objectives of these methods are not clear)...

1. When I override the selectAll() method what I have to do?

2. I have not only One JTextField component else two JToggleButton too, then. What are the recommendations for addActionListener(ActionListener l) and removeActionListener(ActionListener l) methods?

I was checking this example... http://www.java2s.com/Tutorials/Java/Swing_How_to/JComboBox/Add_JCheckBox_components_to_JComboBox.htm

enter image description here enter image description here

As you can see, the List is shown...

Now, in my example...

enter image description here enter image description here

3. I can't see my the list of my components displayed properly.

4. I can't edit my jtfText, jtbEnabled and jtbDefaultOperation components (storing the state/property permanently).

EDIT

Based on aterai's response, I change the Index porpouse preserving the original TheModel class. Now, I want to know the down side.

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

/*
//https://stackoverflow.com/posts/53046908/revisions

javac -Xlint:unchecked -Xlint:deprecation -encoding utf8 -d /Users/bz/Documentos/Java/classes /Users/jose/Documentos/Java/ExampleComboBoxEditorNOIndex.java
java -cp /Users/bz/Documentos/Java/classes ExampleComboBoxEditorNOIndex &

*/

public class ExampleComboBoxEditorNOIndex  {

  public Component makeUI() {
    TheModel[] items = {
      new TheModel(true, true, "00000"),
      new TheModel(true, false, "11111"),
      new TheModel(false, true, "22222"),
      new TheModel(false, false, "33333")
    };
    JComboBox<TheModel> internalComboBox = new JComboBox<>();
    internalComboBox.setModel(new InternalComboBoxModel<>(items));
    internalComboBox.setEditable(true);
    internalComboBox.setEditor(new TheComboBoxEditor(internalComboBox.getFont()));
    internalComboBox.setRenderer(new TheComboBoxRenderer<>(internalComboBox.getFont()));
    internalComboBox.setMaximumRowCount(4);

    InternalComboBoxModel internalComboBoxModel  = (InternalComboBoxModel)internalComboBox.getModel();
    internalComboBox.addItemListener(e -> {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        JComboBox combo = ((JComboBox) e.getSource());
        int lastValidIndex = combo.getSelectedIndex();
        if (lastValidIndex > -1) {
          internalComboBoxModel.setLastValidIndex(lastValidIndex);
          System.out.println("You chose " + combo.getSelectedItem() 
          + ", in position:" + lastValidIndex + "!");
        }
        combo.getEditor().selectAll();
      }
    });

    JPanel outer = new JPanel();
    outer.add(internalComboBox);
    outer.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
    return outer;
  }

  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new ExampleComboBoxEditorNOIndex().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class TheModel {
  private Boolean enabled = true;
  private Boolean defaultOperation = true;
  private String text = "";
  //public int index = -1;

  public TheModel(/*int idx, */Boolean enabled, Boolean defaultOperation, String text) {
    this.enabled = enabled;
    this.defaultOperation = defaultOperation;
    this.text = text;
    //this.index = idx;
  }
  public Boolean getEnabled() {
    return enabled;
  }
  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }
  public Boolean getDefaultOperation() {
    return defaultOperation;
  }
  public void setDefaultOperation(Boolean defaultOperation) {
    this.defaultOperation = defaultOperation;
  }
  public String getText() {
    return text;
  }
  public void setText(String text) {
    this.text = text;
  }
  @Override
  public String toString() {
    return "TheModel{" + "enabled=" + enabled + ", defaultOperation=" + defaultOperation
      + ", text=" + text + '}';
  }
}


class TheComboBoxRenderer<E extends TheModel> implements ListCellRenderer<E> {
  protected ThePanel renderer;
  private int lastSelected = -1;
  public TheComboBoxRenderer(Font extFont) {
    renderer = new ThePanel(new TheModel(/*-1, */false, false, ""), extFont);
  }
  @Override
  public Component getListCellRendererComponent(JList<? extends E> list, E value,
      int index, boolean isSelected, boolean cellHasFocus) {
    if (isSelected) {
      this.lastSelected = index;
      //System.out.println("value: " + value + ", index: " + index + ", isSelected:" + isSelected);
    }
    renderer.setData(value);
    renderer.setBackground(isSelected ? new Color(200, 200, 255) : Color.WHITE);
    return renderer;
  }
}

class TheComboBoxEditor implements ComboBoxEditor {
  protected ThePanel editor;
  public TheComboBoxEditor(Font extFont) {
    super();
    editor = new ThePanel(new TheModel(/*-1, */false, false, ""), extFont);
    editor.setBorder(BorderFactory.createLineBorder(Color.GRAY));
  }
  @Override
  public void selectAll() {
    editor.selectAll();
    // editor.requestFocus(); // <-- The focus moves from jtfText to editor.
  }
  @Override
  public Object getItem() {
  //  System.out.println("getItem(): " + editor.getData());
    return editor.getData();
  }
  @Override
  public void setItem(Object anObject) {
  //  System.out.println("setItem: " + anObject);
    if (anObject instanceof TheModel) {
      TheModel d = (TheModel) anObject;
      editor.setData(d);
    } else {
      editor.setData(new TheModel(/*-1, */false, false, ""));
    }
  }
  @Override
  public Component getEditorComponent() {
    return editor;
  }
  @Override
  public void addActionListener(ActionListener l) {
  //  System.out.println("TheComboBoxEditor.addActionListener: " + l.getClass().getName());
    editor.addActionListener(l);
  }
  @Override
  public void removeActionListener(ActionListener l) {
  //  System.out.println("TheComboBoxEditor.removeActionListener: " + l.getClass().getName());
    editor.removeActionListener(l);
  }
}


class ThePanel extends JPanel {
  private Font intFont = new Font("Monospaced", Font.PLAIN, 8);
  // Since there is no Unselected.png etc,
  // use JCheckBox instead of JToggleButton instead.
  public final JCheckBox jtbEnabled = new JCheckBox();
  public final JCheckBox jtbDefaultOperation = new JCheckBox();
  public final JTextField jtfText = new JTextField("", 10);
  private TheModel data;

  public ThePanel(TheModel data, Font extFont) {
    super();
    this.data = data;
    setData(data);

    jtbEnabled.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        if (combo.getItemCount() > 0) {
          InternalComboBoxModel internalComboBoxModel = (InternalComboBoxModel) combo.getModel();
          int lastValidIndex = internalComboBoxModel.getLastValidIndex();
      //  TheModel o = (TheModel) combo.getSelectedItem();
      //  int idx = positionItemList(getComboBoxItems(combo), o);//int idx = model.getIndexOf(data);
          TheModel rd = (TheModel) combo.getItemAt(lastValidIndex);//(TheModel) combo.getItemAt(idx);
          boolean b = ((JCheckBox) e.getSource()).isSelected();
          rd.setEnabled(b);
          jtbDefaultOperation.setEnabled(b);
        }
      }
    });
    jtbEnabled.setOpaque(false);
    jtbEnabled.setFocusable(false);

    jtbDefaultOperation.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        if (combo.getItemCount() > 0) {
          InternalComboBoxModel internalComboBoxModel = (InternalComboBoxModel) combo.getModel();
          int lastValidIndex = internalComboBoxModel.getLastValidIndex();
      //  TheModel o = (TheModel) combo.getSelectedItem();
      //  int idx = positionItemList(getComboBoxItems(combo), o);//int idx = model.getIndexOf(data);
          TheModel rd = (TheModel) combo.getItemAt(lastValidIndex);//(TheModel) combo.getItemAt(idx);
          rd.setDefaultOperation(((JCheckBox) e.getSource()).isSelected());
        }
      }
    });
    jtbDefaultOperation.setOpaque(false);
    jtbDefaultOperation.setFocusable(false);

    jtfText.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        if (combo.getItemCount() > 0) {
          InternalComboBoxModel internalComboBoxModel = (InternalComboBoxModel) combo.getModel();
          int lastValidIndex = internalComboBoxModel.getLastValidIndex();
      //  TheModel o = (TheModel) combo.getSelectedItem();
      //  int idx = positionItemList(getComboBoxItems(combo), o);//int idx = model.getIndexOf(data);
          TheModel rd = (TheModel) combo.getItemAt(lastValidIndex);//(TheModel) combo.getItemAt(idx);
          rd.setText(((JTextField) e.getSource()).getText());
        }
      }
    });
    jtfText.setBorder(BorderFactory.createEmptyBorder());
    jtfText.setOpaque(false);

    setOpaque(true);
    setBackground(Color.WHITE);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

    add(Box.createRigidArea(new Dimension(2, 0)));
    add(jtbEnabled);
    add(jtbDefaultOperation);
    add(jtfText);
    add(Box.createRigidArea(new Dimension(2, 0)));
  }

  private <T> java.util.List<T> getComboBoxItems(JComboBox comboBox) {
    if (comboBox != null) {
      java.util.List<String> list = new ArrayList<>();
      for (int i = 0; i < comboBox.getItemCount(); i++) {
        list.add(comboBox.getItemAt(i).toString());
      }
      return (java.util.List<T>) list;//(ArrayList<T>)list; // list; // (List<T>)list;
    }
    return null;
  }

  private Integer positionItemList(java.util.List list, Object object) {
    Integer position = -1;
    Integer last = list.size();
    for (int pos = 0; pos < last; pos++) {
      if (list.get(pos).toString().equals(object.toString())) {
        position = pos;
      }
    }
    return position;
  }

  public TheModel getData() {
    return new TheModel(
        //data.index,
        jtbEnabled.isSelected(),
        jtbDefaultOperation.isSelected(),
        jtfText.getText());
  }

  public void setData(TheModel data) {
    //this.data.index = data.index;
    jtbEnabled.setSelected(data.getEnabled());
    jtbDefaultOperation.setEnabled(data.getEnabled());
    jtbDefaultOperation.setSelected(data.getDefaultOperation());
    jtfText.setText(data.getText());
  }
  public void selectAll() {
      jtfText.requestFocusInWindow();
      jtfText.selectAll();
  }
  public void addActionListener(ActionListener l) {

    jtfText.addActionListener(l);
    jtbEnabled.addActionListener(l);
    jtbDefaultOperation.addActionListener(l);
  }
  public void removeActionListener(ActionListener l) {
    jtfText.removeActionListener(l);
    jtbEnabled.removeActionListener(l);
    jtbDefaultOperation.removeActionListener(l);
  }

}

class InternalComboBoxModel<E extends TheModel> extends DefaultComboBoxModel {
  private Integer lastValidIndex = 0;

  InternalComboBoxModel(final E items[]) {
    super(items);
  }

  InternalComboBoxModel(Vector<E> v) {
    super(v);
  }

  public Integer getLastValidIndex() {
    return lastValidIndex;
  }

  public void setLastValidIndex(Integer lastValidIndex) {
    this.lastValidIndex = lastValidIndex;
  }

}

And I want to solve all warnings if is it possible.

MacBook-Air:Users bz$ javac -Xlint:unchecked -Xlint:deprecation -encoding utf8 -d /Users/bz/Documentos/Java/classes /Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:24: warning: [unchecked] unchecked method invocation: method setModel in class JComboBox is applied to given types
    internalComboBox.setModel(new InternalComboBoxModel<>(items));
                             ^
  required: ComboBoxModel<E>
  found: InternalComboBoxModel<TheModel>
  where E is a type-variable:
    E extends Object declared in class JComboBox
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:24: warning: [unchecked] unchecked conversion
    internalComboBox.setModel(new InternalComboBoxModel<>(items));
                              ^
  required: ComboBoxModel<E>
  found:    InternalComboBoxModel<TheModel>
  where E is a type-variable:
    E extends Object declared in class JComboBox
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:257: warning: [unchecked] unchecked cast
      return (java.util.List<T>) list;//(ArrayList<T>)list; // list; // (List<T>)list;
                                 ^
  required: List<T>
  found:    List<String>
  where T is a type-variable:
    T extends Object declared in method <T>getComboBoxItems(JComboBox)
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:310: warning: [unchecked] unchecked call to DefaultComboBoxModel(E[]) as a member of the raw type DefaultComboBoxModel
    super(items);
         ^
  where E is a type-variable:
    E extends Object declared in class DefaultComboBoxModel
/Users/bz/Documentos/Java/ExampleComboBoxEditorNOIndex.java:314: warning: [unchecked] unchecked call to DefaultComboBoxModel(Vector<E>) as a member of the raw type DefaultComboBoxModel
    super(v);
         ^
  where E is a type-variable:
    E extends Object declared in class DefaultComboBoxModel
5 warnings
MacBook-Air:Users bz$ 

1条回答
Fickle 薄情
2楼-- · 2020-05-07 07:13

display the List

You need to add a cell renderer.

internalComboBox.setRenderer(new TheComboBoxRenderer<>(font));

allow edit

Need to add an action listener to each component of the combo editor.

editor.jtbDefaultOperation.addActionListener(e -> {
  System.out.println("jtbDefaultOperation" + editor.getData().index);
  Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, editor);
  if (c instanceof JComboBox) {
    JComboBox<?> combo = (JComboBox<?>) c;
    TheModel o = (TheModel) combo.getItemAt(editor.getData().index);
    o.setDefaultOperation(((JCheckBox) e.getSource()).isSelected());
  }
});

When I override the selectAll() method what I have to do?

Do not move the focus to the TheComboBoxEditor.

internalComboBox.addItemListener(e -> {
  if (e.getStateChange() == ItemEvent.SELECTED) {
    System.out.println("You chose ...");
    ((JComboBox) e.getSource()).getEditor().selectAll();
  }
});

class TheComboBoxEditor implements ComboBoxEditor {
  @Override
  public void selectAll() {
    editor.selectAll();
    // editor.requestFocus(); // <-- The focus moves from JTextField(jtfText) to JPanel(ThePanel).
  }

ExampleComboBoxEditor3.java

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

public class ExampleComboBoxEditor3 {
  public Component makeUI() {
    TheModel[] items = {
      new TheModel(0, true, true, "00000"),
      new TheModel(1, true, false, "11111"),
      new TheModel(2, false, true, "22222"),
      new TheModel(3, false, false, "33333")
    };
    JComboBox<TheModel> internalComboBox = new JComboBox<>(items);
    internalComboBox.setEditable(true);
    internalComboBox.setEditor(new TheComboBoxEditor(internalComboBox.getFont()));
    internalComboBox.setRenderer(new TheComboBoxRenderer<>(internalComboBox.getFont()));
    internalComboBox.setMaximumRowCount(4);
    // internalComboBox.addActionListener(e -> {
    //   System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
    // });
    internalComboBox.addItemListener(e -> {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
        ((JComboBox) e.getSource()).getEditor().selectAll();
      }
    });

    JPanel outer = new JPanel();
    outer.add(internalComboBox);
    outer.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
    return outer;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new ExampleComboBoxEditor3().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class TheModel {
  private Boolean enabled = true;
  private Boolean defaultOperation = true;
  private String text = "";
  public int index = -1;

  public TheModel(int idx, Boolean enabled, Boolean defaultOperation, String text) {
    this.enabled = enabled;
    this.defaultOperation = defaultOperation;
    this.text = text;
    this.index = idx;
  }
  public Boolean getEnabled() {
    return enabled;
  }
  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }
  public Boolean getDefaultOperation() {
    return defaultOperation;
  }
  public void setDefaultOperation(Boolean defaultOperation) {
    this.defaultOperation = defaultOperation;
  }
  public String getText() {
    return text;
  }
  public void setText(String text) {
    this.text = text;
  }
  @Override
  public String toString() {
    return "TheModel{" + "enabled=" + enabled + ", defaultOperation=" + defaultOperation
      + ", text=" + text + '}';
  }
}

class TheComboBoxRenderer<E extends TheModel> implements ListCellRenderer<E> {
  protected ThePanel renderer;
  public TheComboBoxRenderer(Font extFont) {
    renderer = new ThePanel(new TheModel(-1, false, false, ""), extFont);
  }
  @Override
  public Component getListCellRendererComponent(JList<? extends E> list, E value,
      int index, boolean isSelected, boolean cellHasFocus) {
    renderer.setData(value);
    renderer.setBackground(isSelected ? new Color(200, 200, 255) : Color.WHITE);
    return renderer;
  }
}

class TheComboBoxEditor implements ComboBoxEditor {
  protected ThePanel editor;
  public TheComboBoxEditor(Font extFont) {
    super();
    editor = new ThePanel(new TheModel(-1, false, false, ""), extFont);
    editor.setBorder(BorderFactory.createLineBorder(Color.GRAY));
  }
  @Override
  public void selectAll() {
    editor.selectAll();
    // editor.requestFocus(); // <-- The focus moves from jtfText to editor.
  }
  @Override
  public Object getItem() {
    return editor.getData();
  }
  @Override
  public void setItem(Object anObject) {
    if (anObject instanceof TheModel) {
      TheModel d = (TheModel) anObject;
      editor.setData(d);
    } else {
      editor.setData(new TheModel(-1, false, false, ""));
    }
  }
  @Override
  public Component getEditorComponent() {
    return editor;
  }
  @Override
  public void addActionListener(ActionListener l) {
    System.out.println("addActionListener: " + l.getClass().getName());
    editor.addActionListener(l);
  }
  @Override
  public void removeActionListener(ActionListener l) {
    System.out.println("removeActionListener: " + l.getClass().getName());
    editor.removeActionListener(l);
  }
}

class ThePanel extends JPanel {
  private Font intFont = new Font("Monospaced", Font.PLAIN, 8);
  // Since there is no Unselected.png etc,
  // use JCheckBox instead of JToggleButton instead.
  public final JCheckBox jtbEnabled = new JCheckBox();
  public final JCheckBox jtbDefaultOperation = new JCheckBox();
  public final JTextField jtfText = new JTextField("", 10);
  private TheModel data;

  public ThePanel(TheModel data, Font extFont) {
    super();
    this.data = data;
    setData(data);

    jtbEnabled.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(data.index);
        boolean b = ((JCheckBox) e.getSource()).isSelected();
        o.setEnabled(b);
        jtbDefaultOperation.setEnabled(b);
      }
    });
    jtbEnabled.setOpaque(false);
    jtbEnabled.setFocusable(false);

    jtbDefaultOperation.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(data.index);
        o.setDefaultOperation(((JCheckBox) e.getSource()).isSelected());
      }
    });
    jtbDefaultOperation.setOpaque(false);
    jtbDefaultOperation.setFocusable(false);

    jtfText.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(data.index);
        o.setText(((JTextField) e.getSource()).getText());
      }
    });
    jtfText.setBorder(BorderFactory.createEmptyBorder());
    jtfText.setOpaque(false);

    setOpaque(true);
    setBackground(Color.WHITE);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

    add(Box.createRigidArea(new Dimension(2, 0)));
    add(jtbEnabled);
    add(jtbDefaultOperation);
    add(jtfText);
    add(Box.createRigidArea(new Dimension(2, 0)));
  }

  public TheModel getData() {
    return new TheModel(
        data.index,
        jtbEnabled.isSelected(),
        jtbDefaultOperation.isSelected(),
        jtfText.getText());
  }

  public void setData(TheModel data) {
    this.data.index = data.index;
    jtbEnabled.setSelected(data.getEnabled());
    jtbDefaultOperation.setEnabled(data.getEnabled());
    jtbDefaultOperation.setSelected(data.getDefaultOperation());
    jtfText.setText(data.getText());
  }
  public void selectAll() {
      jtfText.requestFocusInWindow();
      jtfText.selectAll();
  }
  public void addActionListener(ActionListener l) {
    jtfText.addActionListener(l);
    jtbEnabled.addActionListener(l);
    jtbDefaultOperation.addActionListener(l);
  }
  public void removeActionListener(ActionListener l) {
    jtfText.removeActionListener(l);
    jtbEnabled.removeActionListener(l);
    jtbDefaultOperation.removeActionListener(l);
  }
}

ExampleComboBoxEditor4.java

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

public class ExampleComboBoxEditor4 {
  public Component makeUI() {
    TheModel[] items = {
      new TheModel(true, true, "00000"),
      new TheModel(true, false, "11111"),
      new TheModel(false, true, "22222"),
      new TheModel(false, false, "33333")
    };
    JComboBox<TheModel> internalComboBox = new JComboBox<>(items);
    internalComboBox.setEditable(true);
    internalComboBox.setEditor(new TheComboBoxEditor(internalComboBox.getFont()));
    internalComboBox.setRenderer(new TheComboBoxRenderer<>(internalComboBox.getFont()));
    internalComboBox.setMaximumRowCount(4);
    // internalComboBox.addActionListener(e -> {
    //   System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
    // });
    internalComboBox.addItemListener(e -> {
      if (e.getStateChange() == ItemEvent.SELECTED) {
        System.out.println("You chose " + ((JComboBox) e.getSource()).getSelectedItem() + "!");
        ((JComboBox) e.getSource()).getEditor().selectAll();
      }
    });

    JPanel outer = new JPanel();
    outer.add(internalComboBox);
    outer.setBorder(BorderFactory.createEmptyBorder(10, 0, 0, 0));
    return outer;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(() -> {
      JFrame f = new JFrame();
      f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
      f.getContentPane().add(new ExampleComboBoxEditor4().makeUI());
      f.setSize(320, 240);
      f.setLocationRelativeTo(null);
      f.setVisible(true);
    });
  }
}

class TheModel {
  private Boolean enabled = true;
  private Boolean defaultOperation = true;
  private String text = "";

  public TheModel(Boolean enabled, Boolean defaultOperation, String text) {
    this.enabled = enabled;
    this.defaultOperation = defaultOperation;
    this.text = text;
  }
  public Boolean getEnabled() {
    return enabled;
  }
  public void setEnabled(Boolean enabled) {
    this.enabled = enabled;
  }
  public Boolean getDefaultOperation() {
    return defaultOperation;
  }
  public void setDefaultOperation(Boolean defaultOperation) {
    this.defaultOperation = defaultOperation;
  }
  public String getText() {
    return text;
  }
  public void setText(String text) {
    this.text = text;
  }
  @Override
  public String toString() {
    return "TheModel{" + "enabled=" + enabled + ", defaultOperation=" + defaultOperation
      + ", text=" + text + '}';
  }
//   // TEST:
//   @Override
//   public boolean equals(Object obj) {
//     if (obj instanceof TheModel) {
//       return Objects.equals(text, ((TheModel) obj).getText());
//     } else {
//       return false;
//     }
//   }
}

class TheComboBoxRenderer<E extends TheModel> implements ListCellRenderer<E> {
  protected ThePanel renderer;
  public TheComboBoxRenderer(Font extFont) {
    renderer = new ThePanel(new TheModel(false, false, ""), extFont);
  }
  @Override
  public Component getListCellRendererComponent(JList<? extends E> list, E value,
      int index, boolean isSelected, boolean cellHasFocus) {
    renderer.setData(value);
    renderer.setBackground(isSelected ? new Color(200, 200, 255) : Color.WHITE);
    return renderer;
  }
}

class TheComboBoxEditor implements ComboBoxEditor {
  protected ThePanel editor;
  public TheComboBoxEditor(Font extFont) {
    super();
    editor = new ThePanel(new TheModel(false, false, ""), extFont);
    editor.setBorder(BorderFactory.createLineBorder(Color.GRAY));
  }
  @Override
  public void selectAll() {
    editor.selectAll();
    // editor.requestFocus(); // <-- The focus moves from jtfText to editor.
  }
  @Override
  public Object getItem() {
    return editor.getData();
  }
  @Override
  public void setItem(Object anObject) {
    EventQueue.invokeLater(() -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, getEditorComponent());
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        int idx = combo.getSelectedIndex();
        if (idx >= 0 && idx != editor.lastValidIndex) {
          System.out.println("setItem: " + idx);
          editor.lastValidIndex = idx;
        }
      }
    });
    if (anObject instanceof TheModel) {
      TheModel d = (TheModel) anObject;
      editor.setData(d);
    } else {
      editor.setData(new TheModel(false, false, ""));
    }
  }
  @Override
  public Component getEditorComponent() {
    return editor;
  }
  @Override
  public void addActionListener(ActionListener l) {
    editor.addActionListener(l);
  }
  @Override
  public void removeActionListener(ActionListener l) {
    editor.removeActionListener(l);
  }
}

class ThePanel extends JPanel {
  private Font intFont = new Font("Monospaced", Font.PLAIN, 8);
  // Since there is no Unselected.png etc,
  // use JCheckBox instead of JToggleButton instead.
  public final JCheckBox jtbEnabled = new JCheckBox();
  public final JCheckBox jtbDefaultOperation = new JCheckBox();
  public final JTextField jtfText = new JTextField("", 10);
  private TheModel data;
  public int lastValidIndex = -1;

  public ThePanel(TheModel data, Font extFont) {
    super();
    this.data = data;
    setData(data);

    jtbEnabled.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(lastValidIndex);
        boolean b = ((JCheckBox) e.getSource()).isSelected();
        o.setEnabled(b);
        jtbDefaultOperation.setEnabled(b);
        combo.setSelectedIndex(lastValidIndex);
      }
    });
    jtbEnabled.setOpaque(false);
    jtbEnabled.setFocusable(false);

    jtbDefaultOperation.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(lastValidIndex);
        o.setDefaultOperation(((JCheckBox) e.getSource()).isSelected());
        combo.setSelectedIndex(lastValidIndex);
      }
    });
    jtbDefaultOperation.setOpaque(false);
    jtbDefaultOperation.setFocusable(false);

    jtfText.addActionListener(e -> {
      Container c = SwingUtilities.getAncestorOfClass(JComboBox.class, this);
      if (c instanceof JComboBox) {
        JComboBox<?> combo = (JComboBox<?>) c;
        TheModel o = (TheModel) combo.getItemAt(lastValidIndex);
        o.setText(((JTextField) e.getSource()).getText());
        combo.setSelectedIndex(lastValidIndex);
      }
    });
    jtfText.setBorder(BorderFactory.createEmptyBorder());
    jtfText.setOpaque(false);

    setOpaque(true);
    setBackground(Color.WHITE);
    setLayout(new BoxLayout(this, BoxLayout.LINE_AXIS));

    add(Box.createRigidArea(new Dimension(2, 0)));
    add(jtbEnabled);
    add(jtbDefaultOperation);
    add(jtfText);
    add(Box.createRigidArea(new Dimension(2, 0)));
  }

  public TheModel getData() {
    return new TheModel(
        jtbEnabled.isSelected(),
        jtbDefaultOperation.isSelected(),
        jtfText.getText());
  }

  public void setData(TheModel data) {
    jtbEnabled.setSelected(data.getEnabled());
    jtbDefaultOperation.setEnabled(data.getEnabled());
    jtbDefaultOperation.setSelected(data.getDefaultOperation());
    jtfText.setText(data.getText());
  }
  public void selectAll() {
    jtfText.requestFocusInWindow();
    jtfText.selectAll();
  }
  public void addActionListener(ActionListener l) {
    jtfText.addActionListener(l);
    jtbEnabled.addActionListener(l);
    jtbDefaultOperation.addActionListener(l);
  }
  public void removeActionListener(ActionListener l) {
    jtfText.removeActionListener(l);
    jtbEnabled.removeActionListener(l);
    jtbDefaultOperation.removeActionListener(l);
  }
}
查看更多
登录 后发表回答