1

我想在用作 a 的默认编辑器组件的文本字段中绘制一些附加信息,JComboBox使用JLayer. 为此,我需要将图层设置为组合框的编辑器,JComboBox.setEditor(ComboBoxEditor)但这似乎是不可能的,因为JLayer它是最终的,因此无法实现ComboBoxEditor接口。

有没有办法用 装饰JComboBox编辑器组件JLayer

PS:我想绘制的信息是文本字段内某些文本偏移处的类似光标的行,这对于 aJTextField或 a来说是微不足道的,JTextArea但对于可编辑JComboBox(其编辑器)来说却不是。

编辑:这是我最接近的,在阅读@camickr 的答案后,但对它不满意。相关部分正在扩展BasicComboBoxEditor

import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
import javax.swing.plaf.basic.BasicComboBoxEditor;
import javax.swing.text.*;

public class GenericDecorateWithJLayer extends JFrame {

    private static final String SAMPLE_TEXT = "Hello, world!";

    private final Map<JComponent, List<Integer>> componentToPositions;

    public GenericDecorateWithJLayer() {
        setDefaultCloseOperation(EXIT_ON_CLOSE);
        setLayout(new GridBagLayout());

        componentToPositions = new HashMap<JComponent, List<Integer>>();

        GridBagConstraints gbc;

        JLabel label1 = new JLabel("label1:");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 0;
        add(label1, gbc);

        JTextField textfield1 = new JTextField(20);
        textfield1.setText(SAMPLE_TEXT);
        componentToPositions.put(textfield1, Arrays.asList(new Integer[]{5}));
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 0;
        add(textfield1, gbc);

        JLabel label2 = new JLabel("label2:");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 1;
        add(label2, gbc);

        JTextField textfield2 = new JTextField(20);
        textfield2.setText(SAMPLE_TEXT);
        componentToPositions.put(textfield2, Arrays.asList(new Integer[]{6}));
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 1;
        add(textfield2, gbc);

        JLabel label3 = new JLabel("label3:");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 2;
        add(label3, gbc);

        JTextArea textarea1 = new JTextArea(5, 20);
        textarea1.setText(SAMPLE_TEXT);
        componentToPositions.put(textarea1, Arrays.asList(new Integer[]{7}));
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 2;
        JScrollPane scroll1 = new JScrollPane(textarea1);
        add(scroll1, gbc);

        JLabel label4 = new JLabel("label4:");
        gbc = new GridBagConstraints();
        gbc.gridx = 0;
        gbc.gridy = 3;
        add(label4, gbc);

        JComboBox combobox1 = new JComboBox(new Object[]{SAMPLE_TEXT, "one", "two", "three"});
        combobox1.setEditable(true);
        combobox1.setSelectedItem(SAMPLE_TEXT);
        componentToPositions.put(combobox1, Arrays.asList(new Integer[]{8}));
        gbc = new GridBagConstraints();
        gbc.gridx = 1;
        gbc.gridy = 3;
        gbc.fill = GridBagConstraints.HORIZONTAL;
        add(combobox1, gbc);

        pack();
        setLocationRelativeTo(null);

        replaceWithJLayer(textfield1);
        replaceWithJLayer(textfield2);
        replaceWithJLayer(textarea1);
        replaceWithJLayer(combobox1);
    }

    /**
     * Intended to decorate legacy components.
     * 
     * @param component 
     */
    private void replaceWithJLayer(JComponent component) {
        Container parent = component.getParent();
        if (component instanceof JComboBox) {
            JComboBox cbb = (JComboBox) component;
            cbb.setEditor(new MyComboBoxEditor(componentToPositions.get(cbb)));
        } else if (parent.getLayout() instanceof GridBagLayout) {
            GridBagLayout layout = (GridBagLayout) parent.getLayout();
            for (int i = 0; i < parent.getComponentCount(); i++) {
                Component candidate = parent.getComponent(i);
                if (candidate == component) {
                    GridBagConstraints gbc = layout.getConstraints(component);
                    parent.remove(i);
                    JLayer<JComponent> layer = new JLayer<JComponent>(
                            component,
                            new MyLayerUI(
                                    component,
                                    componentToPositions.get(component)));
                    parent.add(layer, gbc, i);
                    break;
                }
            }
        } else if (parent instanceof JViewport) {
            JViewport viewport = (JViewport) parent;
            JLayer<JComponent> layer = new JLayer<JComponent>(
                    component,
                    new MyLayerUI(
                            component, componentToPositions.get(component)));
            viewport.setView(layer);
        }
    }

    public static void main(String[] args)
            throws ClassNotFoundException, InstantiationException,
            IllegalAccessException, UnsupportedLookAndFeelException {
        for (UIManager.LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) {
            if ("Nimbus".equals(info.getName())) {
                UIManager.setLookAndFeel(info.getClassName());
            }
            System.out.println(info.getName());
        }

        SwingUtilities.invokeLater(new Runnable() {

            public void run() {
                new GenericDecorateWithJLayer().setVisible(true);
            }
        });
    }

    private static class MyLayerUI extends LayerUI<JComponent> {

        private final JComponent component;
        private final List<Integer> positions;

        public MyLayerUI(JComponent component, List<Integer> positions) {
            this.component = component;
            this.positions = positions;
        }

        @Override
        public void paint(Graphics g, JComponent c) {
            // paint the layer as is
            super.paint(g, c);

            // fill it with the translucent green
            g.setColor(new Color(0, 128, 0, 128));

            // paint positions
            JTextComponent textComponent = (JTextComponent) component;
            for (Integer position : positions) {
                try {
                    Rectangle rect = textComponent.modelToView(position);
                    g.fillRect(rect.x, rect.y, rect.width, rect.height);
                } catch (BadLocationException ex) {
                    // no-op
                }
            }
        }
    }

    private static class MyComboBoxEditor extends BasicComboBoxEditor {

        private final JLayer<JComponent> layer;

        public MyComboBoxEditor(List<Integer> positions) {
            super();

            layer = new JLayer<JComponent>(editor, new MyLayerUI(editor, positions));
        }

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

    }
} 
4

3 回答 3

3

我想绘制的信息是文本字段中某些文本偏移处的光标状行,这对于 JTextField 来说是微不足道的

setEditor(...)您可以使用该方法为组合框设置自己的编辑器。

因此,您可以扩展BasicComboBoxEditor并覆盖该createEditorComponent()方法以返回执行自定义绘画的自定义 JTextField 的实例。

于 2015-10-26T14:59:56.750 回答
1

或更简单的选择是覆盖以下getEditorComponent()方法BasicComboBoxEditor

import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.LayerUI;
import javax.swing.plaf.basic.BasicComboBoxEditor;
//import javax.swing.plaf.metal.MetalComboBoxEditor;
import javax.swing.text.*;

public class ComboEditorJLayerTest {
  public JComponent makeUI() {
    JComboBox<String> comboBox = new JComboBox<>(new String[] {"aaaaaaa", "bbb"});
    comboBox.setEditable(true);
    comboBox.setEditor(new BasicComboBoxEditor() {
      private Component editorComponent;
      //@see javax/swing/plaf/synth/SynthComboBoxUI.java
      @Override public JTextField createEditorComponent() {
        JTextField f = new JTextField("", 9);
        f.setName("ComboBox.textField");
        return f;
      }
      @Override public Component getEditorComponent() {
        if (editorComponent == null) {
          JTextComponent tc = (JTextComponent) super.getEditorComponent();
          editorComponent = new JLayer<JTextComponent>(tc, new ValidationLayerUI());
        }
        return editorComponent;
      }
    });
    JPanel p = new JPanel();
    p.add(comboBox);
    return p;
  }
  public static void main(String[] args) {
    EventQueue.invokeLater(new Runnable() {
      @Override public void run() {
        createAndShowGUI();
      }
    });
  }
  public static void createAndShowGUI() { 
    try {
      for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
        if ("Nimbus".equals(laf.getName())) {
          UIManager.setLookAndFeel(laf.getClassName());
        }
      }
    }catch(Exception e) {
      e.printStackTrace();
    }
    JFrame f = new JFrame();
    f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
    f.getContentPane().add(new ComboEditorJLayerTest().makeUI());
    f.setSize(320, 240);
    f.setLocationRelativeTo(null);
    f.setVisible(true);
  }
}

//@see http://docs.oracle.com/javase/tutorial/uiswing/examples/misc/FieldValidatorProject/src/FieldValidator.java
class ValidationLayerUI extends LayerUI<JTextComponent> {
  @Override public void paint(Graphics g, JComponent c) {
    super.paint(g, c);
    JLayer jlayer = (JLayer) c;
    JTextComponent tc = (JTextComponent) jlayer.getView();
    if (tc.getText().length() > 6) {
      Graphics2D g2 = (Graphics2D) g.create();
      g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
      int w = c.getWidth();
      int h = c.getHeight();
      int s = 8;
      int pad = 4;
      int x = w - pad - s;
      int y = (h - s) / 2;
      g2.setPaint(Color.RED);
      g2.fillRect(x, y, s + 1, s + 1);
      g2.setPaint(Color.WHITE);
      g2.drawLine(x, y, x + s, y + s);
      g2.drawLine(x, y + s, x + s, y);
      g2.dispose();
    }
  }
}
于 2015-10-27T07:25:20.347 回答
0

可能这可以通过扩展CellRenderer来完成

于 2015-10-26T16:43:58.227 回答