Мне нужно создать сложный графический интерфейс на JAVA с помощью Swing (на данный момент у меня около 80 классов). Графическая часть приложения разделена следующим образом: первая серия вкладок (например, «Управление», «Администрирование», «Конфигурация»), затем второй уровень (например, «Пользователь», «Группа», «Игра» ). На данный момент у меня два уровня обучения (по одному на каждый уровень вкладок). Следующий уровень — это JPanel, которая управляет бизнес-объектом (весь мой графический интерфейс построен вокруг моей бизнес-модели), на этом уровне есть 2 типа JPanel: кто управляет простыми объектами (например, «Пользователь», «Категория», «Игра»). ", "Уровень") и те, которые управляют объектами "составного первичного ключа" (например, "User_Game", которые представляют форму таблицы с двойной записью для каждого игрового уровня для всех пользователей). Мой второй уровень вкладок может содержать несколько JPanel. Когда моя JPanel управляет одним объектом, состоящим из JTable и двух кнопок (Добавить и Удалить), на которые я помещаю события, если нет, то это простой JTable. Когда у меня есть внешние ключи (например, «Группа» для «Пользователь» и «Категория» для «Игра» или «Уровень» для «User_Game»), это JComboBox, который берет свою информацию непосредственно из JTableModel. Когда дело доходит до управления объектом JTable для «составного первичного ключа», столбцы и строки также напрямую зависят от моделей (например, «Игра» и «Пользователь», «User_Game»). Каждый из них имеет свою собственную модель JTable, которая имеет дело с уровнем сохраняемости (Hibernate для информации) и другой TableModel. Для управления изменениями (такими как добавление, изменение или удаление «Пользователя») я использую следующий код:
import java.awt.event.*;
import javax.swing.*;
import java.beans.*;
/*
* This class listens for changes made to the data in the table via the
* TableCellEditor. When editing is started, the value of the cell is saved
* When editing is stopped the new value is saved. When the oold and new
* values are different, then the provided Action is invoked.
*
* The source of the Action is a TableCellListener instance.
*/
public class TabCellListener implements PropertyChangeListener, Runnable
{
private JTable table;
private Action action;
private int row;
private int column;
private Object oldValue;
private Object newValue;
/**
* Create a TableCellListener.
*
* @param table the table to be monitored for data changes
* @param action the Action to invoke when cell data is changed
*/
public TabCellListener(JTable table, Action action)
{
this.table = table;
this.action = action;
this.table.addPropertyChangeListener( this );
this.table.getModel().addTableModelListener(new ModelListenerTableGui(this.table, this.action));
}
/**
* Create a TableCellListener with a copy of all the data relevant to
* the change of data for a given cell.
*
* @param row the row of the changed cell
* @param column the column of the changed cell
* @param oldValue the old data of the changed cell
* @param newValue the new data of the changed cell
*/
private CellListenerTableGui(JTable table, int row, int column, Object oldValue, Object newValue)
{
this.table = table;
this.row = row;
this.column = column;
this.oldValue = oldValue;
this.newValue = newValue;
}
/**
* Get the column that was last edited
*
* @return the column that was edited
*/
public int getColumn()
{
return column;
}
/**
* Get the new value in the cell
*
* @return the new value in the cell
*/
public Object getNewValue()
{
return newValue;
}
/**
* Get the old value of the cell
*
* @return the old value of the cell
*/
public Object getOldValue()
{
return oldValue;
}
/**
* Get the row that was last edited
*
* @return the row that was edited
*/
public int getRow()
{
return row;
}
/**
* Get the table of the cell that was changed
*
* @return the table of the cell that was changed
*/
public JTable getTable()
{
return table;
}
//
// Implement the PropertyChangeListener interface
//
@Override
public void propertyChange(PropertyChangeEvent e)
{
// A cell has started/stopped editing
if ("tableCellEditor".equals(e.getPropertyName()))
{
if (table.isEditing())
processEditingStarted();
else
processEditingStopped();
}
}
/*
* Save information of the cell about to be edited
*/
private void processEditingStarted()
{
// The invokeLater is necessary because the editing row and editing
// column of the table have not been set when the "tableCellEditor"
// PropertyChangeEvent is fired.
// This results in the "run" method being invoked
SwingUtilities.invokeLater( this );
}
/*
* See above.
*/
@Override
public void run()
{
row = table.convertRowIndexToModel( table.getEditingRow() );
column = table.convertColumnIndexToModel( table.getEditingColumn() );
oldValue = table.getModel().getValueAt(row, column);
newValue = null;
}
/*
* Update the Cell history when necessary
*/
private void processEditingStopped()
{
newValue = table.getModel().getValueAt(row, column);
// The data has changed, invoke the supplied Action
if ((newValue == null && oldValue != null) || (newValue != null && !newValue.equals(oldValue)))
{
// Make a copy of the data in case another cell starts editing
// while processing this change
CellListenerTableGui tcl = new CellListenerTableGui(
getTable(), getRow(), getColumn(), getOldValue(), getNewValue());
ActionEvent event = new ActionEvent(
tcl,
ActionEvent.ACTION_PERFORMED,
"");
action.actionPerformed(event);
}
}
}
И следующее действие:
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
public class UpdateTableListener<N> extends AbstractTableListener implements Action
{
protected boolean enabled;
public UpdateTableListener(AbstractTableGui<N> obs)
{
super(obs);
this.enabled = true;
}
@Override
public void actionPerformed(ActionEvent e)
{
if (null != e && e.getSource() instanceof CellListenerTableGui)
{
TabCellListener tcl = (TabCellListener)e.getSource();
this.obs.getModel().setValueAt(tcl.getNewValue(), tcl.getRow(), tcl.getColumn());
int sel = this.obs.getModel().getNextRequiredColumn(tcl.getRow());
if (sel == -1)
this.obs.getModel().save(tcl.getRow());
}
}
@Override
public void addPropertyChangeListener(PropertyChangeListener arg0)
{
}
@Override
public Object getValue(String arg0)
{
return null;
}
@Override
public boolean isEnabled()
{
return this.enabled;
}
@Override
public void putValue(String arg0, Object arg1)
{
}
@Override
public void removePropertyChangeListener(PropertyChangeListener arg0)
{
}
@Override
public void setEnabled(boolean arg0)
{
this.enabled = arg0;
}
}
Этот код работает хорошо, данные хорошо сохраняются. Затем я добавляю этот код для обновления зависимых компонентов:
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
public class ChangeTableListener implements Action
{
protected AbstractTableGui table;
public ChangeTableListener(AbstractTableGui table)
{
this.table = table;
}
@Override
public void actionPerformed(ActionEvent arg0)
{
this.table.getModel().fireTableDataChanged();
this.table.repaint();
}
@Override
public void addPropertyChangeListener(PropertyChangeListener arg0)
{
}
@Override
public Object getValue(String arg0)
{
return null;
}
@Override
public boolean isEnabled()
{
return false;
}
@Override
public void putValue(String arg0, Object arg1)
{
}
@Override
public void removePropertyChangeListener(PropertyChangeListener arg0)
{
}
@Override
public void setEnabled(boolean arg0)
{
}
}
My TableModel.fireTableDataChanged перестраивает содержимое JTable (вызывает super.fireTableDataChanged и fireTableStructureChanged), JTable.repaint сбрасывает визуализаторы, и он работает для Combobox (внешние ключи) и хорошо обновляет заголовок в таблицах с двойной записью, но он не может добавить или удалять столбцы или строки в таблицах с двойной записью. Более того, я вижу более высокую задержку, если есть малейшее изменение.
Мой вопрос прост: как вы управляете взаимозависимыми компонентами?
За вашу помощь, заранее, спасибо.
Изменить: вот пример TableCellEditor.
import javax.swing.DefaultCellEditor;
import javax.swing.JTextField;
public class TextColumnEditor extends DefaultCellEditor
{
public TextColumnEditor()
{
super(new JTextField());
}
public boolean stopCellEditing()
{
Object v = this.getCellEditorValue();
if(v == null || v.toString().length() == 0)
{
this.fireEditingCanceled();
return false;
}
return super.stopCellEditing();
}
}
Пример TableModel:
import java.util.ArrayList;
public class GroupModelTable extends AbstractModelTable<Groups>
{
protected GroupsService service;
public GroupModelTable(AbstractTableGui<Groups> content)
{
super(content, new ArrayList<String>(), new ArrayList<Groups>());
this.headers.add("Group");
this.content.setModel(this);
this.service = new GroupsService();
this.setLines(this.service.search(new Groups()));
}
public Object getValueAt(int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case 0:
return this.lines.get(rowIndex).getTitle();
default:
return "";
}
}
public void setValueAt(Object aVal, int rowIndex, int columnIndex)
{
switch (columnIndex)
{
case 0:
this.lines.get(rowIndex).setTitle(aVal.toString());
break;
default:
break;
}
}
@Override
public Groups getModel(int line, int column)
{
return null;
}
@Override
public Groups getModel(int line)
{
return this.lines.get(line);
}
public boolean isCellEditable(int row, int column)
{
return true;
}
@Override
public GroupModelTableGui newLine()
{
this.lines.add(new Groups());
return this;
}
@Override
public int getNextRequiredColumn(int row)
{
Groups g = this.getModel(row);
if (g != null && g.getTitle() != null && g.getTitle().length() > 0)
return -1;
return 0;
}
@Override
public void save(int row)
{
Groups g = this.getModel(row);
if (g != null)
{
try
{
if (g.getId() == null)
this.service.create(g);
else
this.service.update(g);
}
catch (Exception e)
{
}
}
}
@Override
public void removeRow(int row)
{
Groups g = this.getModel(row);
if (g != null)
{
try
{
if (g.getId() != null)
this.service.delete(g);
super.removeRow(row);
}
catch (Exception e)
{
}
}
}
}
Пример таблицы:
public class GroupTable extends AbstractTable<Groups>
{
public GroupTable()
{
new GroupModelTableGui(this);
new CellListenerTableGui(this.getContent(), new UpdateTableListenerGui<Groups>(this));
this.getContent().getColumnModel().getColumn(0).setCellEditor(new TextColumnEditorGui());
}
}
Надеюсь, это поможет вам понять :/