Размещение JToggleButton с JPanel внутри в ячейке JTable

Мне нужно иметь JToggleButton (с настраиваемым фоном), который содержит JPanel с несколькими JLabels внутри себя. Эта часть работает.

Эта кнопка впоследствии помещается в ячейку JTable и предназначена для нажатия пользователями. Проблема в том, что я могу нажать кнопку только при втором нажатии. Apperenty при первом нажатии фокус сначала переходит на панель с JLabels и только потом на саму кнопку.

Я пробовал несколько вещей, чтобы попытаться решить эту проблему, но та же проблема сохраняется. A) размещение JPanel с метками непосредственно на JToggleButton#add(). B) использование JLayeredPane для размещения Button и JPanel на разных слоях, где JToggleButton принимает ограничение Integer (-), чтобы JPanel с JLabels оставался видимым сверху

Есть ли у вас какие-либо советы? Спасибо

Ниже приведен пример кода, иллюстрирующий проблему. Нажатие на кнопку работает только второй раз.

public class ClickableCustomButtonInTable extends JToggleButton {

public ClickableCustomButtonInTable() {
    Dimension d = new Dimension(100, 100);
    JLabel lFirst = new JLabel("1st label");
    lFirst.setPreferredSize(d);

    JLabel lSecond = new JLabel("2nd label");
    lSecond.setPreferredSize(d);

    JPanel panel = new JPanel();
    panel.setOpaque(true);

    panel.setLayout(new BorderLayout());
    panel.add(lFirst, BorderLayout.NORTH);
    panel.add(lSecond, BorderLayout.SOUTH);
    add(panel);
    addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(ActionEvent e) {
            System.out.println("Button clicked");
        }
    });
}

private static class CustomButtonRenderer implements TableCellRenderer {

    private final ClickableCustomButtonInTable button = new ClickableCustomButtonInTable();

    @Override
    public Component getTableCellRendererComponent(JTable table,
            Object value, boolean isSelected, boolean hasFocus, int row,
            int column) {
        return button;
    }
}

private static class CustomButtonEditor extends AbstractCellEditor
        implements TableCellEditor {

    private final ClickableCustomButtonInTable button = new ClickableCustomButtonInTable();

    @Override
    public Object getCellEditorValue() {
        return button.getText();
    }

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

}

public static void main(String[] args) {
    JFrame frame = new JFrame();
    frame.setSize(new Dimension(200, 200));
    Container content = frame.getContentPane();
    TableModel model = new AbstractTableModel() {

        @Override
        public Object getValueAt(int rowIndex, int columnIndex) {
            return null;
        }

        @Override
        public int getRowCount() {
            return 1;
        }

        @Override
        public int getColumnCount() {
            return 1;
        }

        @Override
        public boolean isCellEditable(int rowIndex, int columnIndex) {
            return true;
        }

        @Override
        public Class<?> getColumnClass(int columnIndex) {
            return ClickableCustomButtonInTable.class;
        }
    };

    JTable table = new JTable(model);
    // table.setBounds(new Rectangle(0, 0, content.getWidth(), content
    // .getHeight()));
    table.setRowHeight(frame.getHeight());
    table.setDefaultRenderer(ClickableCustomButtonInTable.class,
            new CustomButtonRenderer());
    table.setDefaultEditor(ClickableCustomButtonInTable.class,
            new CustomButtonEditor());

    content.add(table);
    content.setVisible(true);
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setVisible(true);
}
}

person d56    schedule 06.03.2012    source источник
comment
не имеет прямого отношения к вашей проблеме: реализация редактора недействительна: она должна уведомлять своих слушателей, когда редактирование прекращается по внутренним причинам. Что касается вашей проблемы: в основном вы должны а) убедиться, что редактирование запускается при первом щелчке б) захватить этот щелчок, вычислить его положение в координатах компонента редактирования и отправить его реальному целевому компоненту (например, кнопке) на панели редактирования.   -  person kleopatra    schedule 12.03.2012
comment
Кстати: вы никогда не храните компоненты в своей tableModel... так как ваша реализация TableModel также подозрительна (она объявляет редактируемой, но отсутствует какой-либо API для изменения значений ячеек), возможно, вам следует прочитать и поймите некоторые основы работы с таблицами, прежде чем прыгать прямо в середину не столь тривиальных вещей :-)   -  person kleopatra    schedule 12.03.2012
comment
@kleopatra, спасибо за советы. Не могли бы вы дать ссылку/показать, как: а) убедиться, что редактирование запускается при первом щелчке; б) захватить этот щелчок, вычислить его положение в координатах компонента редактирования и отправить его реальному целевому компоненту на панели редактирования. Я не уверен, что вы имеете в виду. отправляя событие реальной цели, это doClick() вызывает то, что вы имеете в виду. если это так, я боюсь, что потеряю анимацию щелчка, которую обеспечивает jToggleButton.   -  person d56    schedule 12.03.2012
comment
Будет ли TablePopupEditor альтернативой?   -  person trashgod    schedule 12.03.2012
comment
дерьмо, не совсем так. Что мне нужно, так это интерактивный ToggleButton с некоторыми дополнительными виджетами, размещенными на нем, без всплывающих окон.   -  person d56    schedule 12.03.2012
comment
не уверен, но вы можете получить привязку ToggleButton и даже к mouseListener, проверить границы и выполнить свою операцию   -  person Abhishek Choudhary    schedule 12.03.2012


Ответы (2)


Когда таблица захватывает событие мыши для выбора ячейки, она передает событие мыши самому глубокому компоненту независимо от того, может ли этот компонент обрабатывать события мыши. В вашем примере первый щелчок заканчивается на одном из JLabels, полностью минуя JToggleButton. После того, как JToggleButton станет активным редактором ячеек, щелчки мыши будут работать с ним как обычно. Если бы он потерял фокус, для его активации снова потребовалось бы два щелчка.

Вы также можете увидеть это, если заметите, что в своей демонстрации вы нажимаете на границу кнопки, а не на содержащуюся в ней панель, кнопка работает по желанию.

Один из способов обойти это — убедиться, что любое событие мыши, направленное на любой компонент в JToggleButton. Вы можете сделать это, используя этот статический метод:

static void addEventBubble(final Container target, Container container) {
    for(Component comp:container.getComponents()) {
        if (comp instanceof Container) {
            addEventBubble(target, (Container) comp);
        }
        comp.addMouseListener(new MouseAdapter() {
            private MouseEvent retarget(MouseEvent e) {
                return new MouseEvent(target, e.getID(), e.getWhen(),
                        e.getModifiers(), e.getX(), e.getY(),
                        e.getClickCount(), e.isPopupTrigger(),
                        e.getButton());
            }


            public void mousePressed(MouseEvent e) {
                MouseEvent r = retarget(e);
                for(MouseListener listen:target.getMouseListeners()) {
                    listen.mousePressed(r);
                }
            }


            public void mouseReleased(MouseEvent e) {
                MouseEvent r = retarget(e);
                for(MouseListener listen:target.getMouseListeners()) {
                    listen.mouseReleased(r);
                }
            }


            public void mouseClicked(MouseEvent e) {
                MouseEvent r = retarget(e);
                for(MouseListener listen:target.getMouseListeners()) {
                    listen.mouseClicked(r);
                }
            }
        });
    }
}

а затем в конце вызова вашего конструктора:

addEventBubble(this,this);

После этого любое событие мыши на любом компоненте внутри кнопки также достигнет кнопки и, следовательно, изменит ее состояние. После этого я обнаружил, что кнопка реагировала на каждый щелчок так, как хотелось бы.

person Simon G.    schedule 13.03.2012
comment
Мне дали другое решение на coderanch.com/ t/570021/GUI/java/ Это хрупко, но меньше кода. Я думаю, что я бы придерживался этого тогда. Спасибо за объяснение и решение, мне так нужно было и то, и другое. - person d56; 14.03.2012
comment
Глядя, действительно хрупкое слово для этого :) Спасибо, что поделились. - person Simon G.; 14.03.2012

http://www.coderanch.com/t/570021/GUI/java/click-event-custom-JToggleButton-JTable

person d56    schedule 13.03.2012
comment
это, скорее всего, не спасет вашу карьеру ;-) Лучше научитесь правильно использовать таблицы/рендереры/редакторы... - person kleopatra; 15.03.2012
comment
все не так, начиная с каких-то галочек в моем комментарии к вашему вопросу... - person kleopatra; 19.03.2012
comment
Я имею в виду только эту часть ((ClickableCustomButtonInTable)(SwingUtilities.getAncestorOfClass(ClickableCustomButtonInTable.class,SwingUtilities.getDeepestComponentAt(table,me.getX(),me.getY())))).doClick(); Остальной код был только примером, реальный код отличается (но спасибо за советы по использованию компонента в табличной модели). - person d56; 19.03.2012