Может ли JRadioButton находиться в нескольких группах кнопок?

Я пытаюсь настроить JRadioButton-Matrix, чтобы в каждом столбце и в каждой строке одновременно можно было выбрать только одну кнопку. У меня есть следующий код:

JRadioButton[][] button = new JRadioButton[names.length][names.length];
ButtonGroup[] r = new ButtonGroup[names.length];
ButtonGroup[] c = new ButtonGroup[names.length];
for (int i = 0; i < names.length; i++) {
    r[i] = new ButtonGroup();
    c[i] = new ButtonGroup();
}
for (int i = 0; i < names.length; i++) {
        for (int j = 0; j < names.length; j++) {
                    button[i][j] = new JRadioButton();
                    r[i].add(button[i][j]);
                    c[j].add(button[i][j]);
        }
}

Но когда я его выполняю, правильно ведут себя только столбцы (т.е. кнопки в группах c). Однако, когда я комментирую части с помощью c, строки ведут себя правильно.

Чтобы немного прояснить ситуацию (спасибо peeskillet):

Допустим, у меня есть эта матрица 4 x 4 JRadioButtons:

O   O   O   O

O   O   O   O

O   O   O   O

O   O   O   O

И я хочу, чтобы можно было делать такие выборы:

X   O   O   O       X   O   O   O      O   X   O   O

O   X   O   O       O   O   X   O      X   O   O   O

O   O   X   O       O   X   O   O      O   O   O   X

O   O   O   X       O   O   O   X      O   O   X   O

В приведенном выше примере в каждом столбце есть только один, а в каждой строке — только один. Следующие примеры НЕ были бы возможны:

X   X   O   O       X   O   O   O

O   O   O   O       O   X   O   O

O   O   X   O       O   X   O   O

O   O   O   X       O   O   O   X

Однако проблема в том, что я МОГУ выбрать, как в приведенной выше левой матрице, но не в правой. Если я прокомментирую следующие части:

ButtonGroup[] c = new ButtonGroup[names.length]; 
c[i] = new ButtonGroup();
c[j].add(button[i][j]);

тогда матрица сверху справа возможна, но не слева.


person Zuerill    schedule 25.12.2013    source источник
comment
Извините, если имена сбивают с толку.. Изменение их на «не путающие» более продуктивно, чем извинения..   -  person Andrew Thompson    schedule 25.12.2013
comment
Класс ButtonGroup предназначен для группировки переключателей. Вам придется написать свои собственные прослушиватели действий, чтобы проверять переключатели в группах кнопок (строках).   -  person Gilbert Le Blanc    schedule 25.12.2013
comment
@AndrewThompson Да, это было глупо.   -  person Zuerill    schedule 25.12.2013
comment
чтобы в каждом столбце и в каждой строке одновременно можно было выбрать только одну кнопку. Мне кажется, что с этой логикой можно выбрать только одну кнопку из общего числа. Это правильно?   -  person Paul Samsotha    schedule 25.12.2013
comment
@peeskillet Хороший вопрос. Если это так, добавление их всех к одному ButtonGroup должно быть решением.   -  person Andrew Thompson    schedule 25.12.2013
comment
Вы должны возиться, но если бы я был, Сначала я изменю этот путь во внутреннем цикле... JRadioButton jrb = new JRadioButton(); r[i].add(jrb); c[j].add(jrb);   -  person Siva Tumma    schedule 25.12.2013
comment
Я вижу, вы отредактировали, чтобы удалить извинения и внести несколько незначительных правок в вопрос, который даже не пытался исправить то, за что вы извинялись. Имеет ли это на самом деле смысл для вас? Потому что это точно не имеет для меня никакого смысла..   -  person Andrew Thompson    schedule 25.12.2013
comment
@AndrewThompson, простите, на это нужно время.   -  person Siva Tumma    schedule 25.12.2013
comment
@peeskillet Нет, за один раз можно выбрать кнопки до name.length (по одной в каждой строке/по одной в каждом столбце).   -  person Zuerill    schedule 25.12.2013
comment
Пожалуйста, взгляните на мой пост ниже. Я хотел бы лучше понять, что вы пытаетесь сделать   -  person Paul Samsotha    schedule 25.12.2013


Ответы (3)


Нет, любой подкласс AbstractButton, использующий класс ButtonModel по умолчанию (неудивительно, что он назван DefaultButtonModel), может быть только в одном классе ButtonGroup.

Для получения дополнительной информации см. ButtonGroup.add(...) и ButtonModel.setGroup(...).

Можно создать специальный подкласс ButtonGroup, который знает вашу матрицу и допускает множественный выбор (я думаю, с некоторыми хаками). Однажды я создал группу из одного переключателя (взаимоисключающего) и нескольких флажков (разрешен множественный выбор), и это сработало для меня. :-) Сейчас у меня нет доступа к коду, но вы можете обновить код позже, если вам интересно.

person Harald K    schedule 25.12.2013
comment
Так что я бы не обойти это, не переписав в основном все эти классы? - person Zuerill; 25.12.2013
comment
@Zuerill Я обновил ответ некоторыми предложениями о том, что вы можете сделать. Я предлагаю один пользовательский файл ButtonGroup. - person Harald K; 25.12.2013
comment
Эх, у меня нет достаточно времени, чтобы сосредоточиться на действительно сложном графическом интерфейсе, это поможет, если я просто сгруппирую столбцы. - person Zuerill; 25.12.2013

Является ли функциональность, которую вы ищете, выполнимой? да. Ниже приведен пример того, что я имею в виду. Я использовал много утверждений is. Может быть, есть рекурсивный способ сделать это, но это еще больше уму непостижимо. Взгляните на пример. К сожалению, он использовал только 9 кнопок. Таким образом, если вы хотите использовать больше, потребуется гораздо больше кода. В основном все, что я сделал, это отменил выбор определенных кнопок для каждой выбранной.

import java.awt.BorderLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.SwingUtilities;

public class MultiButtonGroup extends JPanel implements ActionListener {

    JRadioButton rb1 = new JRadioButton("rb1");
    JRadioButton rb2 = new JRadioButton("rb2");
    JRadioButton rb3 = new JRadioButton("rb3");
    JRadioButton rb4 = new JRadioButton("rb4");
    JRadioButton rb5 = new JRadioButton("rb5");
    JRadioButton rb6 = new JRadioButton("rb6");
    JRadioButton rb7 = new JRadioButton("rb7");
    JRadioButton rb8 = new JRadioButton("rb8");
    JRadioButton rb9 = new JRadioButton("rb9");

    public MultiButtonGroup() {

        JRadioButton[][] buttons = {
            {rb1, rb2, rb3},
            {rb4, rb5, rb6},
            {rb7, rb8, rb9}
        };

        JPanel panel = new JPanel(new GridLayout(4, 4));
        for (JRadioButton[] rbs : buttons) {
            for (JRadioButton rbz : rbs) {
                rbz.addActionListener(new RadioListener());
                panel.add(rbz);
            }
        }

        JButton doSomething = new JButton("Do SOmething");
        setLayout(new BorderLayout());
        add(panel, BorderLayout.CENTER);
        add(doSomething, BorderLayout.SOUTH);

    }

    public void actionPerformed(ActionEvent e) {

    }

    private class RadioListener implements ActionListener {

        public void actionPerformed(ActionEvent e) {
            JRadioButton source = (JRadioButton) e.getSource();
            if (source == rb1) {
                if (rb1.isSelected()) {
                    rb2.setSelected(false);
                    rb3.setSelected(false);
                    rb4.setSelected(false);
                    rb7.setSelected(false);
                }
            } else if (source == rb2) {
                if (rb2.isSelected()) {
                    rb1.setSelected(false);
                    rb3.setSelected(false);
                    rb5.setSelected(false);
                    rb8.setSelected(false);
                }
            } else if (source == rb3) {
                if (rb3.isSelected()) {
                    rb2.setSelected(false);
                    rb1.setSelected(false);
                    rb6.setSelected(false);
                    rb9.setSelected(false);
                }
            } else if (source == rb4) {
                if (rb4.isSelected()) {
                    rb1.setSelected(false);
                    rb7.setSelected(false);
                    rb5.setSelected(false);
                    rb6.setSelected(false);
                }
            } else if (source == rb5) {
                if (rb5.isSelected()) {
                    rb4.setSelected(false);
                    rb6.setSelected(false);
                    rb2.setSelected(false);
                    rb8.setSelected(false);
                }
            } else if (source == rb6) {
                if (rb6.isSelected()) {
                    rb3.setSelected(false);
                    rb9.setSelected(false);
                    rb4.setSelected(false);
                    rb5.setSelected(false);
                }
            } else if (source == rb7) {
                if (rb7.isSelected()) {
                    rb1.setSelected(false);
                    rb4.setSelected(false);
                    rb8.setSelected(false);
                    rb9.setSelected(false);
                }
            } else if (source == rb8) {
                if (rb8.isSelected()) {
                    rb7.setSelected(false);
                    rb9.setSelected(false);
                    rb5.setSelected(false);
                    rb2.setSelected(false);
                }
            } else if (source == rb9) {
                if (rb9.isSelected()) {
                    rb6.setSelected(false);
                    rb3.setSelected(false);
                    rb8.setSelected(false);
                    rb7.setSelected(false);
                }
            }
        }
    }

    public static void createAndShowGui() {
        JFrame frame = new JFrame();
        frame.add(new MultiButtonGroup());
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationByPlatform(true);
        frame.pack();
        frame.setVisible(true);

    }

    public static void main(String[] args) {
        SwingUtilities.invokeLater(new Runnable() {
            public void run() {
                createAndShowGui();
            }
        });
    }
}

введите здесь описание изображения

person Paul Samsotha    schedule 25.12.2013
comment
Правильно, но, конечно, допускаются и другие варианты (достигнутые в вашем примере путем замены двух строк). Редактировать: вы поняли. - person Zuerill; 25.12.2013
comment
Вот и все! Но, ну, мне нужно 14 строк/столбцов... Спасибо, но это, наверное, не стоит реализовывать, я могу жить без этого. - person Zuerill; 25.12.2013

Пользовательская ButtonGroup (как уже было предложено @Harald) определенно является подходящим вариантом.

Не совсем тривиально из-за немного странной смеси обязанностей buttonModel и группы: основная настройка, о которой следует помнить, заключается в том, что группа должна сохранять свое собственное состояние выбора (вместо того, чтобы полагаться на выбранное в модели).

Приведенная ниже реализация POC хранит его в матрице (list-of-(lists-of-buttonModels)), которая содержит null или модель, которую он считает выбранной. Внутреннее обновление сохраняет (должно, формально не проверять :-) эту матрицу таким образом, чтобы она содержала ровно один ненулевой элемент в каждой строке и каждом столбце. Конечно, есть много возможностей для очистки ...

/**
 * A buttonGroup that organizes selections in a matrix and guarantees 
 * to have at most one selection in each row and each column.
 */
public static class MatrixButtonGroup extends ButtonGroup {
    // matrix of the buttons
    private List<List<AbstractButton>> buttonMatrix;
    // sparse matrix of the selected models, contains nulls
    // everywhere except the unique selection for each row/column
    private List<List<ButtonModel>> selectionMatrix;

    public MatrixButtonGroup(List<AbstractButton> buttons, int columnCount) {
        if (buttons.size() % columnCount != 0) {
            throw new IllegalStateException("buttons count must be a multiple of columnCount");
        }
        int rowCount = buttons.size() / columnCount;
        buttonMatrix = new ArrayList<>();
        selectionMatrix = new ArrayList<>();
        int counter = 0;
        for (int row = 0; row < rowCount; row++) {
            List<AbstractButton> buttonsInRow = new ArrayList<>();
            List<ButtonModel> modelsInRow = new ArrayList<>();
            for (int column = 0; column < columnCount; column++) {
                modelsInRow.add(null);
                buttons.get(counter).getModel().setGroup(this);
                buttonsInRow.add(buttons.get(counter++));
            }
            selectionMatrix.add(modelsInRow);
            buttonMatrix.add(buttonsInRow);
        }
    }

    @Override
    public boolean isSelected(ButtonModel m) {
        for (int row = 0; row < selectionMatrix.size(); row++) {
            List<ButtonModel> modelsInRow = selectionMatrix.get(row);
            if (modelsInRow.contains(m)) return true;
        }
        return false;
    }

    /**
     * Implemented to select the model such that it is the
     * uniquely selected in the row/column of its button.
     */
    @Override
    public void setSelected(ButtonModel model, boolean selected) {
        if (model == null || !selected) return;
        if (isSelected(model)) return;
        int row = getRow(model);
        int column = getColumn(model);
        ButtonModel rowSelected = getSelectedForRow(row);
        ButtonModel columnSelected = getSelectedForColumn(column);
        // update internal selection state
        select(model, row, column);
        // unselect the old selection if necessary
        if (rowSelected != null) {
            rowSelected.setSelected(false);
        }
        if (columnSelected != null) {
            columnSelected.setSelected(false);
        }
        // select the new model
        model.setSelected(true);
    }


    /**
     * Update internal selection state to select the model such 
     * that there is exactly one model selected in the given 
     * row and column.
     */
    private void select(ButtonModel model, int row, int column) {
        // clear all in column
        for (int index = 0; index < selectionMatrix.size(); index++) {
            selectionMatrix.get(index).set(column, null);
        }
        List<ButtonModel> selectionRow = selectionMatrix.get(row);
        for (int index = 0; index < selectionRow.size(); index++) {
            selectionRow.set(index, null);
        }
        selectionRow.set(column, model);
    }

    /**
     * @return the column of the given model
     */
    private int getColumn(ButtonModel model) {
        for (int row = 0; row < buttonMatrix.size(); row++) {
            int column = getColumnInRow(buttonMatrix.get(row), model);
            if (column >= 0) return column;
        }
        throw new IllegalStateException("model not managed by this group");
    }

    /**
     * @return the row of the given model
     */
    private int getRow(ButtonModel model) {
        for (int row = 0; row < buttonMatrix.size(); row++) {
            if (getColumnInRow(buttonMatrix.get(row), model) >= 0) return row;
        }
        throw new IllegalStateException("model not managed by this group");
    }

    /**
     * @return the column of the model in the list
     */
    private int getColumnInRow(List<AbstractButton> list, ButtonModel model) {
        for (int column = 0; column < list.size(); column++) {
            if (list.get(column).getModel() ==  model) return column;
        }
        return -1;
    }


    /**
     * @return the selected buttonModel in the column or null if none
     * selected
     */
    private ButtonModel getSelectedForColumn(int column) {
        for (List<ButtonModel> selectionRow : selectionMatrix) {
            if (selectionRow.get(column) != null) return selectionRow.get(column);
        }
        return null;
   }

    /**
     * @return the selected buttonModel in the row or null if none
     * selected
     */
    private ButtonModel getSelectedForRow(int row) {
        List<ButtonModel> selectionRow = selectionMatrix.get(row);
        for (ButtonModel model : selectionRow) {
            if (model != null) return model;
        }
        return null;
    }


    /**
     * Implemented to return the first selected model, traversing
     * rows from first to last column.
     */
    @Override
    public ButtonModel getSelection() {
        for (List<ButtonModel> selectionRow : selectionMatrix) {
            for (ButtonModel model : selectionRow) {
                if (model != null) return model;
            }
        }
        return null;
    }

    @Override
    public int getButtonCount() {
        return buttonMatrix.size() * buttonMatrix.get(0).size();
    }

    // super overrides that still need to be done or are not supported 

    @Override
    public Enumeration<AbstractButton> getElements() {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public void clearSelection() {
        throw new UnsupportedOperationException("not yet implemented");
    }

    @Override
    public void add(AbstractButton b) {
       throw new UnsupportedOperationException("this button group is unmodifiable");
    }

    @Override
    public void remove(AbstractButton b) {
        throw new UnsupportedOperationException("this button group is unmodifiable");
    }
}

Это использование:

List<AbstractButton> buttons = new ArrayList<>();
for (int row = 0; row < 4; row++) {
    for (int column = 0; column < 4; column++) {
        buttons.add(new JRadioButton("row " + row + " col " + column));
    }
}
ButtonGroup p = new MatrixButtonGroup(buttons, 4);
JComponent content = new JPanel(new GridLayout(0, 4));
for (AbstractButton button : buttons) {
    content.add(button);
}
person kleopatra    schedule 25.12.2013