Может ли JRadioButton быть в нескольких ButtonGroups?
Я пытаюсь настроить 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, строки ведут себя правильно.
Чтобы прояснить ситуацию (спасибо 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]);
тогда возможна матрица справа вверху, но не слева.
3 ответа
Никакой AbstractButton
подкласс, используя по умолчанию ButtonModel
(Неудивительно, что имя DefaultButtonModel
) может быть только в одном ButtonGroup
,
Для получения дополнительной информации см. ButtonGroup.add(...) и ButtonModel.setGroup (...).
Можно создать специальный ButtonGroup
подкласс, который знает вашу матрицу и допускает множественный выбор (с некоторыми взломами, я думаю). Однажды я создал группу из одного радиокнопки (взаимоисключающие) и нескольких флажков (разрешен множественный выбор), и это сработало для меня.:-) У вас нет доступа к коду сейчас, но вы можете обновить код позже, если вам интересно.
Определенная группа ButtonGroup (как уже было предложено @Harald) определенно является подходящим вариантом.
Не совсем тривиально из-за немного странного сочетания обязанностей buttonModel и группы: основной трюк, о котором нужно помнить, состоит в том, что группа должна сохранять свое собственное состояние выбора (вместо того, чтобы полагаться на выбранную модель).
Реализация POC ниже сохраняет его в матрице (list-of-(lists-of-buttonModels)), которая содержит ноль или модель, которую он считает выбранной. Внутреннее обновление сохраняет (должно быть, формально не проверено:-) эту матрицу так, чтобы в ней было ровно один ненулевой элемент в каждой строке и каждом столбце. Конечно, есть много возможностей для очистки...
/**
* 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);
}
Является ли функциональность, которую вы ищете, выполнимой? Да. Ниже приведен пример того, что я имею в виду. Я использовал много утверждений. Может быть, есть рекурсивный способ сделать это, но это просто поражает разум еще больше. Посмотрите на пример. К сожалению, он использовал только 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();
}
});
}
}