作为JTable单元格编辑器的自定义JComponent子类不会获得鼠标/键盘事件

时间:2014-07-21 11:06:54

标签: java swing jtable tablecelleditor

最近,我创建了两个非常简单但非常有用的组件,名为 AttributesEditableView AttributesEditor 。 AttributesEditableView旨在显示任意数量的属性,而AttributesEditor则可以编辑属性。

那么,假设我们有三个属性可以取值'Y','N'和'?' (在哪里'?'我们的意思是“未指明”)。该视图类似于[[Y][N][?]]

在编辑器中,用户可以使用箭头键在属性之间左右跳转,并通过按 SPACE 键切换特定(当前选定)属性的值。

(我想那些熟悉JTable及其特点的人已经明白我什么时候才能到达目的地。)

一切都在表单中运行良好,但是当我想从这个AttributesEditor中创建一个TableCellEditor时,我会卡住,因为JTable要么“吃掉”所有事件,要么就是我做错了所以我的AttributesEditor根本没有得到事件...... :(

我的逻辑如下:当编辑器组件DefaultCellEditor获取这些事件时,JTextField怎么来( left -key或 right - 按键,例如),而我的单元格编辑器没有?

我知道我不是在这里给所有课程,但我相信下面的两个课程应该给那些已经遇到过这个问题的人提供足够的信息,告诉我如何使这个单元格编辑器按预期工作?

UPDATE :我设法通过重构AttributesEditor类来解决问题。现在它使用键绑定而不是KeyListener,一切都按预期工作。获得的经验......

你可以在下面的屏幕截图中看到AttributesEditableView,AttributesEditor和AttributesCellEditor(我也有渲染器,但是无论如何都打不开它,它看起来像是AttributesEditableView):

attributes-in-action

在上面的屏幕截图中,第一个属性可能包含'Y','N'和'?'值,第二个'A','B','C'和'D',第三个属性可能有'T'和'F'作为值。

以下是课程:

AttributesCellEditor

/**
 * Authors: Dejan Lekic , http://dejan.lekic.org
 * License: MIT
 */
public class AttributesCellEditor extends AbstractCellEditor
    implements TableCellEditor, TreeCellEditor {

    private AttributesEditor attributesEditor;
    private AttributesColumnModel attrColModel;
    private AttributesModel model;
    private int clickCountToStart = 2;
    DefaultCellEditor dfe;


    public AttributesCellEditor(AttributesColumnModel argModel) {
        super();
        attrColModel = argModel;
        model = new AttributesModel(attrColModel.getAllowedValues());
        model.setDefaultValues(attrColModel.getDefaultValues());
        attributesEditor = new AttributesEditor(model);
        attributesEditor.setBorder(new BevelBorder(BevelBorder.LOWERED));
    }

    // ::::: CellEditor method implementations :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    @Override
    public Object getCellEditorValue() {
        System.out.println("getCellEditorValue()");
        // before we return the value, we should also modify the attributes column model and set the 
        // selected attribute index to the index of what we last edited.
        attrColModel.setSelectedAttributeIndex(model.getSelectedAttributeIndex());
        return model.getAttributes();
    }

    // ::::: TableCellEditor method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    @Override
    public Component getTableCellEditorComponent(JTable table, Object value, boolean isSelected, int row, 
            int column) {

        String val = (value == null) ? "" : value.toString();
        model.setAttributes(val);
        model.setSelectedAttributeIndex(attrColModel.getSelectedAttributeIndex());
        attributesEditor.setModel(model);
        return attributesEditor;
    }

    // ::::: TreeCellEditor method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    // TODO: implement these

    @Override
    public Component getTreeCellEditorComponent(JTree tree, Object value, boolean isSelected, 
            boolean expanded, boolean leaf, int row) {
        //To change body of generated methods, choose Tools | Templates.
        throw new UnsupportedOperationException("Not supported yet."); 
    }

    // ::::: AbstractCellEditor method overrides ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    @Override
    public boolean isCellEditable(EventObject e) {
        if (e instanceof MouseEvent) {
            return ((MouseEvent) e).getClickCount() >= clickCountToStart;
        }
        return true;
    }

    /**
     * {@inheritDoc}
     * @return 
     */
    @Override
    public boolean stopCellEditing() {
        boolean ret = super.stopCellEditing();

        return ret;
    }

} // AttributesCellEditor class

AttributesEditor

/**
 * Authors: Dejan Lekic , http://dejan.lekic.org
 * License: MIT
 */
public class AttributesEditor 
        extends AttributesEditableView
        implements PropertyChangeListener, FocusListener, KeyListener {

    public AttributesEditor() {
        super();
        editorComponent = true;
        setFocusable(true);
        setBackground(Color.white);

        // Let's copy the border from the TextField
        Border b = (Border) UIManager.getLookAndFeelDefaults().get("TextField.border");
        setBorder(b);

        // Let's copy the insets from the TextField
        Insets insets = (Insets) UIManager.getLookAndFeelDefaults().get("TextField.margin");
        getInsets().set(insets.top, insets.left, insets.bottom, insets.right);

        addKeyListener(this);

        // listeners...
        addFocusListener(this);
    }

    public AttributesEditor(AttributesModel argModel) {
        this();
        setOpaque(true);
        setFocusable(true);
        setBackground(Color.white);
        repaint();

        setModel(argModel);
    }

    // :::: PropertyChangeListener method implementations :::::::::::::::::::::::::::::::::::::::::::::::::

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        String str = (String) evt.getNewValue();
        System.out.println("CALL: AttributesEditor.propertyChange() : " + str);
        updateView();
    }

    // :::: FocusListener method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    @Override
    public void focusGained(FocusEvent e) {
        int sel = model.getSelectedAttributeIndex();
        if (sel < 0) {
            model.setSelectedAttributeIndex(0);
        }
        sel = model.getSelectedAttributeIndex();
        labels[sel].setBackground(model.getSelectedBackgroundColor());
    }

    @Override
    public void focusLost(FocusEvent e) {
        model.setSelectedAttributeIndex(-1);
        updateView();
    }

    // :::: KeyListener method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    @Override
    public void keyTyped(KeyEvent e) {
        // Weird thing is, arrow keys do not trigger keyTyped() to be called, 
        // so we have to use keyPressed here, or keyReleased
    }

    /**
     * Weird thing is, arrow keys do not trigger keyTyped() to be called, so we have to use keyPressed/keyReleased here.
     * @param e 
     */
    @Override
    public void keyPressed(KeyEvent e) {
        int key = e.getKeyCode();

        switch (key) {
            case KeyEvent.VK_UP:
                model.previous(model.getSelectedAttributeIndex());
                break;
            case KeyEvent.VK_DOWN:
            case KeyEvent.VK_SPACE:
                model.next(model.getSelectedAttributeIndex());
                break;
            case KeyEvent.VK_LEFT:
                model.previousAttribute();
                updateView();
                break;                
            case KeyEvent.VK_RIGHT:
                model.nextAttribute();
                updateView();
                break;
            default:
                int idx = model.getSelectedAttributeIndex();
                char chr = Character.toUpperCase(e.getKeyChar());
                if (model.isValid(idx, chr)) {
                    model.setValue(idx, chr);
                }
        } // switch
    } // keyPressed() method

    @Override
    public void keyReleased(KeyEvent e) {
        // nothing
    }

} // AttributesEditor class

AttributesEditableView

/**
 * Authors: Dejan Lekic , http://dejan.lekic.org
 * License: MIT
 */
public class AttributesEditableView 
        extends JComponent
        implements PropertyChangeListener, MouseListener {

    protected AttributesModel model;
    protected JLabel[] labels;
    protected boolean editorComponent;
    protected boolean inTable;

    public AttributesEditableView() {
        super();
        FlowLayout fl = new FlowLayout(FlowLayout.LEFT);
        fl.setVgap(0);
        setLayout(fl);
        editorComponent = false;
        inTable = false;
    }

    public AttributesEditableView(AttributesModel argModel) {
        this();
        setModel(argModel);
    }

    // :::: JComponent method overrides :::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    /**
     * I had to do this because it is a common problem with JComponent subclasses.
     * More about it: http://docs.oracle.com/javase/tutorial/uiswing/painting/problems.html
     * 
     * @param g 
     */
    @Override
    protected void paintComponent(Graphics g) {
        super.paintComponent(g);
        g.setColor(getBackground());
        g.fillRect(0, 0, getWidth(), getHeight());
        g.setColor(getForeground());
    }

    // :::: PropertyChangeListener mthod implementations ::::::::::::::::::::::::::::::::::::::::::::::::::

    @Override
    public void propertyChange(PropertyChangeEvent evt) {
        String str = (String) evt.getNewValue();
        updateView();
    }

    // :::: <Interface> method implementations ::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::

    @Override
    public void mouseClicked(MouseEvent e) {
        // labels have names in form of "attributelbl-3", we need to extract the ID from the name
        String num = e.getComponent().getName().split("-")[1];
        int idx = Integer.parseInt(num);

        if (editorComponent) {
            model.setSelectedAttributeIndex(idx);
        }

        model.next(idx);
    } // mouseClicked() method

    @Override
    public void mousePressed(MouseEvent e) {
        // do nothing
    }

    @Override
    public void mouseReleased(MouseEvent e) {
        // do nothing
    }

    @Override
    public void mouseEntered(MouseEvent e) {
        // labels have names in form of "attributelbl-3", we need to extract the ID from the name
        String num = e.getComponent().getName().split("-")[1];
        int idx = Integer.parseInt(num);
        model.setSelectedAttributeIndex(idx);
        updateView();
    }

    @Override
    public void mouseExited(MouseEvent e) {
        // labels have names in form of "attributelbl-3", we need to extract the ID from the name
        String num = e.getComponent().getName().split("-")[1];
        int idx = Integer.parseInt(num);

        if (!editorComponent) {
            model.setSelectedAttributeIndex(-1);
        }

        updateView();
    }

    /**
     * Use this method to forcefully highlight specific attribute.
     * @param argIndex 
     */
    public final void highlight(int argIndex) {
        for (int i = 0; i < model.getNumberOfAttributes(); i++) {
            if (i == argIndex) {
                labels[i].setBackground(model.getSelectedBackgroundColor());
            } else {
                labels[i].setBackground(model.getBackgroundColor());
            }
        } // for
    } // highlight() method

    /**
     * Extremely important method. Here we set the model, and generate JLabel objects for the attributes.
     * We also set the values, and initialise the listeners.
     * @param argModel 
     */
    public final void setModel(AttributesModel argModel) {
        if (model != null) {
            model.removePropertyChangeListener(this);
        }

        labels = null;
        removeAll();
        labels = new JLabel[argModel.getNumberOfAttributes()];

        int w = 0;
        int h = 0;
        for (int i = 0; i < argModel.getNumberOfAttributes(); i++) {
            String txt = "" + argModel.getValue(i);
            System.out.println(txt);

            JLabel lbl = new JLabel(txt);
            labels[i] = lbl;
            lbl.setName("attributelbl-" + i); // very important, because we will use the name to get the idx
            lbl.setHorizontalAlignment(SwingConstants.CENTER);
            lbl.setOpaque(true);
            lbl.setBackground(argModel.getBackgroundColor());
            if (isInTable()) {
                lbl.setBorder(BorderFactory.createMatteBorder(0, 0, 0, 1, Color.GRAY));
            }
            int nh = lbl.getPreferredSize().height;
            int nw = nh; // make the width to be equal to the height
            lbl.setPreferredSize(new Dimension(nw, nh));
            lbl.addMouseListener(this);

            h = Math.max(h, lbl.getPreferredSize().height);
            w = w + lbl.getPreferredSize().width;
            add(lbl);
        } // for
        Dimension ps = new Dimension(w, h);

        model = argModel;
        model.addPropertyChangeListener(this);
    } // setModel() method

    public final AttributesModel getModel() {
        return model;
    }

    public boolean isInTable() {
        return inTable;
    }

    public void setInTable(boolean inTable) {
        this.inTable = inTable;
    }

    /**
     * Updates the view
     */
    protected void updateView() {
        String attrs = model.getAttributes();
        for (int i = 0; i < model.getNumberOfAttributes(); i++) {
            labels[i].setText("" + model.getValue(i));
            if (model.getSelectedAttributeIndex() == i) {
                labels[i].setBackground(model.getSelectedBackgroundColor());
            } else {
                labels[i].setBackground(model.getBackgroundColor());
            }
        }
    }

} // AttributesEditableView class

0 个答案:

没有答案