2

Trashgodの優れた例hereに従って、おそらく複雑な方法で単純なタスクを実行する小さなデモをまとめました。以下に示す GUI は、真/偽の値を表すアイコンの列を表示します。アイコンをクリックすると、値が実際とは逆に変更されます。チェックボックスによく似ていますが、外観が異なり、より拡張可能です (たとえば、将来的には、2 つのブール記号だけでなく、12 個の記号を循環するように変更できます)。

ここに画像の説明を入力

JComponent のダミー拡張機能であるカスタム エディタを使用して実行しました。ただし、このダミー コンポーネントが表示されることさえありません。これは、イベントを取得するとすぐにMousePressedエディターがfireEditingStopped(). 私が見つけた1つの奇妙なバグを除いて、それはうまく機能します。記号をクリックして変更し、マウスを画面上の別の場所に移動してキーボードのキーを押すと、最後にクリックしたセルにダミー エディターが表示され (効果的にセルが空白になります)、変更するまでそこにとどまります。マウスをセルに移動するか、別のセルをクリックします。

これに対するハックな修正として、レンダリング後にテーブル全体を常に選択解除する行をレンダラーに追加しました。これはうまく機能し、テーブル全体が実際に選択解除されていることを確認しました。ただし、それにもかかわらず、キーボードのキーを押すと、最後に編集されたセルのエディターが引き続き実行されます。この動作を防ぐにはどうすればよいですか? アプリケーションに他のキーボード リスナーがあり、セルが選択されていない場合は、それらのエディターを実行する必要はないと思います。

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;

import javax.imageio.ImageIO;
import javax.swing.AbstractCellEditor;
import javax.swing.ImageIcon;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;

public class JTableBooleanIcons {

    private JFrame frame;
    private DefaultTableModel tableModel;
    private JTable table;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    JTableBooleanIcons window = new JTableBooleanIcons();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public JTableBooleanIcons() {
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        tableModel = new DefaultTableModel(new Object[]{"Words", "Pictures"},0);
        table = new JTable(tableModel);
        table.setRowHeight(40);
        tableModel.addRow(new Object[]{"click icon to change", false});
        tableModel.addRow(new Object[]{"click icon to change", true});
        tableModel.addRow(new Object[]{"click icon to change", false});
        tableModel.addRow(new Object[]{"click icon to change", true});
        tableModel.addRow(new Object[]{"click icon to change", false});
        tableModel.addRow(new Object[]{"click icon to change", true});
        tableModel.addRow(new Object[]{"click icon to change", false});
        tableModel.addRow(new Object[]{"click icon to change", true});

        frame.getContentPane().add(table, BorderLayout.CENTER);

        table.getColumn("Pictures").setCellRenderer(new BooleanIconRenderer());
        table.getColumn("Pictures").setCellEditor(new BooleanIconEditor());

    }

    @SuppressWarnings("serial")
    private class BooleanIconRenderer extends DefaultTableCellRenderer implements TableCellRenderer {

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                                                                        boolean hasFocus, int row, int col) {
            String iconFilename = null;
            if ((boolean) value) {
                iconFilename = "yes.png";
                value = true;
            } else {
                iconFilename = "no.png";
                value = false;
            }
            try {
                setIcon(new ImageIcon(ImageIO.read(BooleanIconRenderer.class.getResourceAsStream(iconFilename))));
            } catch (Exception e) {
                System.err.println("Failed to load the icon.");
            }
            if (isSelected) {
                this.setBackground(Color.white);
            } else {
                this.setBackground(Color.white);
            }
            table.getSelectionModel().clearSelection();
            return this;
        }
    }

    @SuppressWarnings("serial")
    private class BooleanIconEditor extends AbstractCellEditor implements TableCellEditor, MouseListener {

        private BooleanComponent boolComp;

        public BooleanIconEditor() {
            boolComp = new BooleanComponent(false);
            boolComp.addMouseListener(this);
        }

        @Override
        public Object getCellEditorValue() {
            return boolComp.getValue();
        }

        @Override
        public Component getTableCellEditorComponent(JTable table,
                Object value, boolean isSelected, int row, int column) {
            boolComp.setValue(! (boolean) value);
            return boolComp;
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            this.fireEditingStopped();
        }

        @Override
        public void mousePressed(MouseEvent e) {
            this.fireEditingStopped();

        }

        @Override
        public void mouseReleased(MouseEvent e) {
            this.fireEditingStopped();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            this.fireEditingStopped();
        }

        @Override
        public void mouseExited(MouseEvent e) {
            this.fireEditingStopped();
        }

    }

    @SuppressWarnings("serial")
    private class BooleanComponent extends JComponent {
        private boolean value;

        public BooleanComponent(boolean value) {
            this.value = value;
        }

        public boolean getValue() {
            return value;
        }

        public void setValue(boolean value) {
            this.value = value;
        }
    }
}
4

2 に答える 2

5

問題の理由は、実際にはエディター内で何もレンダリングしていないことです。ユーザーがセルを「クリック」して値を変更するだけであるという事実に依存しています。これは少し近視眼的だと思いますが、それはあなたのプログラムです。

すぐに修正するには、追加するだけです...

@Override
public boolean isCellEditable(EventObject e) {
    return (e instanceof MouseEvent);
}

あなたにBooleanIconEditor

余談ですが。

セル レンダラーでは、画像を読み込んではいけません。これらは、コンストラクターの一部として事前にキャッシュされているか、staticフィールド変数として事前にキャッシュされている必要があります。あなたはこれをしているかもしれませんが、念のため。

更新しました

私が主題についている間。レンダラー内からテーブルの状態を変更することは避けてください。これは本当に安全ではなく、テーブルが何度も何度も変更を再レンダリングしようとするため、地獄の無限ループに陥る可能性があります...

本当に選択を非表示にしたい場合 (理由はわかりません)、テーブルの選択をテーブルの背景色と一致するように設定するか、透明な色にすることができます。前景の選択も忘れずに変更してください ;)

アップデート #2

キーボードをサポートする例 ;) - 抵抗できませんでした...

public class JTableBooleanIcons {

    private JFrame frame;
    private DefaultTableModel tableModel;
    private JTable table;
    private ImageIcon yesIcon;
    private ImageIcon noIcon;

    /**
     * Launch the application.
     */
    public static void main(String[] args) {
        EventQueue.invokeLater(new Runnable() {
            public void run() {
                try {
                    JTableBooleanIcons window = new JTableBooleanIcons();
                    window.frame.setVisible(true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        });
    }

    /**
     * Create the application.
     */
    public JTableBooleanIcons() {
        try {
            yesIcon = (new ImageIcon(ImageIO.read(BooleanIconRenderer.class.getResourceAsStream("/yes.png"))));
            noIcon = (new ImageIcon(ImageIO.read(BooleanIconRenderer.class.getResourceAsStream("/no.png"))));
        } catch (Exception e) {
            e.printStackTrace();
        }
        initialize();
    }

    /**
     * Initialize the contents of the frame.
     */
    private void initialize() {
        frame = new JFrame();
        frame.setBounds(100, 100, 450, 400);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        tableModel = new DefaultTableModel(new Object[]{"Words", "Pictures"}, 0);
        table = new JTable(tableModel);
        table.setRowHeight(40);
        tableModel.addRow(new Object[]{"click icon to change", false});
        tableModel.addRow(new Object[]{"click icon to change", true});
        tableModel.addRow(new Object[]{"click icon to change", false});
        tableModel.addRow(new Object[]{"click icon to change", true});
        tableModel.addRow(new Object[]{"click icon to change", false});
        tableModel.addRow(new Object[]{"click icon to change", true});
        tableModel.addRow(new Object[]{"click icon to change", false});
        tableModel.addRow(new Object[]{"click icon to change", true});

        frame.getContentPane().add(table, BorderLayout.CENTER);

        table.getColumn("Pictures").setCellRenderer(new BooleanIconRenderer());
        table.getColumn("Pictures").setCellEditor(new BooleanIconEditor());

    }

    @SuppressWarnings("serial")
    private class BooleanIconRenderer extends DefaultTableCellRenderer implements TableCellRenderer {

        public BooleanIconRenderer() {
        }

        @Override
        public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
                boolean hasFocus, int row, int col) {
            super.getTableCellRendererComponent(table, null, isSelected, hasFocus, row, col);
            if ((boolean) value) {
                setIcon(yesIcon);
            } else {
                setIcon(noIcon);
            }
            return this;
        }
    }

    @SuppressWarnings("serial")
    private class BooleanIconEditor extends AbstractCellEditor implements TableCellEditor, MouseListener {

        private BooleanComponent boolComp;
        private boolean isMouseEvent;

        public BooleanIconEditor() {
            boolComp = new BooleanComponent(false);
            boolComp.addMouseListener(this);
            InputMap im = boolComp.getInputMap(JComponent.WHEN_FOCUSED);
            ActionMap am = boolComp.getActionMap();

            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_SPACE, 0), "click");
            im.put(KeyStroke.getKeyStroke(KeyEvent.VK_ENTER, 0), "click");

            am.put("click", new AbstractAction() {
                @Override
                public void actionPerformed(ActionEvent e) {
                    System.out.println("Clicked");
                    boolComp.setValue(!boolComp.getValue());
                }
            });

        }

        @Override
        public boolean isCellEditable(EventObject e) {
            isMouseEvent = e instanceof MouseEvent;
            return true; //(e instanceof MouseEvent);
        }

        @Override
        public Object getCellEditorValue() {
            return boolComp.getValue();
        }

        @Override
        public Component getTableCellEditorComponent(JTable table,
                Object value, boolean isSelected, int row, int column) {
            boolean state = (boolean) value;
            if (isMouseEvent) {
                state = !state;
            }
            boolComp.setValue(state);
            boolComp.setOpaque(isSelected);
            boolComp.setBackground(table.getSelectionBackground());
            return boolComp;
        }

        @Override
        public void mouseClicked(MouseEvent e) {
            this.fireEditingStopped();
        }

        @Override
        public void mousePressed(MouseEvent e) {
            this.fireEditingStopped();

        }

        @Override
        public void mouseReleased(MouseEvent e) {
            this.fireEditingStopped();
        }

        @Override
        public void mouseEntered(MouseEvent e) {
            this.fireEditingStopped();
        }

        @Override
        public void mouseExited(MouseEvent e) {
            this.fireEditingStopped();
        }
    }

    @SuppressWarnings("serial")
    private class BooleanComponent extends JLabel {

        private boolean value;

        public BooleanComponent(boolean value) {
            this.value = value;
        }

        public boolean getValue() {
            return value;
        }

        public void setValue(boolean value) {
            this.value = value;
            if (value) {
                setIcon(yesIcon);
            } else{
                setIcon(noIcon);
            }
        }
    }
}
于 2013-01-23T00:32:26.433 に答える
4

@Mad の回答では、マウス処理とキー バインドを実装する方法が示されていますが、別の方法としてJToggleButton、 の親でJCheckBoxある と適切なを使用することもできますIcon。トグル ボタンは、選択したイベントを表示してイベントを処理する方法を既に認識していIconます。変更されたものValueRendererを以下に示します。変更されてValueEditorいません。テキストはオプションです。

画像

private static class ValueRenderer extends JToggleButton
    implements TableCellRenderer {

    private static final Color hilite = new Color(0xE8E8E8);
    private static final Icon YES = UIManager.getIcon("InternalFrame.maximizeIcon");
    private static final Icon NO = UIManager.getIcon("InternalFrame.closeIcon");

    public ValueRenderer() {
        this.setOpaque(true);
        this.setIcon(NO);
        this.setSelectedIcon(YES);
    }
    ...
}
于 2013-01-23T11:19:06.600 に答える