使用JLayer

时间:2015-10-26 14:27:55

标签: java swing jcombobox prompt jlayer

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

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

PS:我想要绘制的信息在文本字段中的某些文本偏移处是类似光标的线条,这对于JLayerJTextField来说是微不足道的,但对于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;
        }

    }
} 

3 个答案:

答案 0 :(得分:3)

  

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

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

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

答案 1 :(得分:1)

或者更简单的选项是覆盖getEditorComponent()的<{1}}方法:

BasicComboBoxEditor

答案 2 :(得分:0)

可以通过扩展CellRenderer

来完成