【问题标题】:Decorate a JComboBox editor component with a JLayer使用 JLayer 装饰 JComboBox 编辑器组件
【发布时间】:2015-10-26 14:27:55
【问题描述】:

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

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

P.S.:我想绘制的信息是文本字段内某些文本偏移处的类似光标的行,这对于 JTextFieldJTextArea 来说是微不足道的,但对于可编辑的 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;
        }

    }
} 

【问题讨论】:

    标签: java swing jcombobox prompt jlayer


    【解决方案1】:

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

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

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

    【讨论】:

      【解决方案2】:

      或者更简单的选择是覆盖BasicComboBoxEditorgetEditorComponent()方法:

      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();
          }
        }
      }
      

      【讨论】:

      • 我在阅读 camickr 的回答后尝试了这个,但结果不是我所期望的。由于某种原因缺少组合框边框。你的代码也会发生同样的事情。
      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2016-08-18
        • 2017-09-27
        • 1970-01-01
        • 2023-03-28
        • 2016-08-20
        • 2012-10-06
        • 2010-11-25
        • 1970-01-01
        • 2015-01-27
        相关资源
        最近更新 更多