强制更新JTable不起作用
我有一个JTable,我可以在其上显示一些信息和颜色背景。然而,每当数据发生变化时,我都会遇到问题,表格不会更新它的颜色。展望槽SO我发现了几个问题,他说这些例子应该工作:强制更新JTable不起作用
table.revalidate();
或
((CustomTableModel)table.getModel()).fireTableDataChanged();
或
table.repaint();
或
tableModel.fireTableCellUpdated(row, col);
我已经试过这些方法在桌子里面eModel,在JPanel类中持有表格本身,甚至在tablerenderer内部,但似乎没有任何工作(表格不会改变一点)
我检查了更新表格时收到的数据是否变化,但表格本身不更新。它只有当我重新启动应用程序。
我试图做一个Minimal, Complete, and Verifiable example只是通过复制的代码,但我可以使它以同样的方式,它已成为办法漫长而复杂的人了解工作之前...
为此我决定要上传整个project to github,但你可以找到这些类在这里的错误:
package Interface;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.StringTokenizer;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
import DataBase.Booking;
import DataBase.Room;
import Logics.Globals;
public class TablePanel extends JPanel{
private static TablePanel MainLogicInctance;
private JTable table;
private CustomTableModel model;
private JPopupMenu popup;
private JMenuItem view;
private JMenuItem confirm;
private JMenuItem unconfirm;
private JMenuItem edit;
private JMenuItem delete;
int x=0,y=0;
public TablePanel(){
model = new CustomTableModel(getObjectArray(), 0);
popup = new JPopupMenu();
view = new JMenuItem("view");
confirm = new JMenuItem("confirm");
unconfirm = new JMenuItem("unconfirm");
edit = new JMenuItem("edit");
delete = new JMenuItem("delete");
popup.add(view);
popup.add(confirm);
popup.add(unconfirm);
popup.add(new JPopupMenu.Separator());
popup.add(edit);
popup.add(delete);
this.setLayout(new BorderLayout());
this.setBackground(new Color(255,255,255));
table = new JTable(model);
MouseAdapter adapter = new MouseAdapter() {
@Override
public void mousePressed(MouseEvent e) {
if(e.getSource() != view && e.getSource() != confirm && e.getSource() != unconfirm && e.getSource() != edit && e.getSource() != delete){
x=table.rowAtPoint(e.getPoint());
y=table.columnAtPoint(e.getPoint());
System.out.println(Integer.toString(x)+"|"+Integer.toString(y));
}
if(e.getSource() == view){
Booking booking = Globals.getBookingByDateAndRoom(x, y);
System.out.println(Integer.toString(x)+"|"+Integer.toString(y));
System.out.println(booking.toString());
if(booking!=null){
JFrame frame = new PopupFrame("view",booking);
}
}
if(e.getSource() == confirm){
Booking booking = Globals.getBookingByDateAndRoom(x, y);
System.out.println(Integer.toString(x)+"|"+Integer.toString(y));
if(booking!=null){
Globals.updateBooking(booking.getName(), booking.getSurname(), booking.getPersons(), booking.getBegin(), booking.getEnd(), booking.getRoomID(), booking.getId(), true);
TablePanel.getInctance().updateAll();
}
}
if(e.getSource() == unconfirm){
Booking booking = Globals.getBookingByDateAndRoom(x, y);
if(booking!=null){
Globals.updateBooking(booking.getName(), booking.getSurname(), booking.getPersons(), booking.getBegin(), booking.getEnd(), booking.getRoomID(), booking.getId(), false);
TablePanel.getInctance().updateAll();
}
}
if(e.getSource() == edit){
Booking booking = Globals.getBookingByDateAndRoom(x, y);
if(booking!=null){
JFrame frame = new PopupFrame("edit booking",booking);
TablePanel.getInctance().updateAll();
}
}
if(e.getSource() == delete){
Booking booking = Globals.getBookingByDateAndRoom(x, y);
if(booking!=null){
JFrame frame = new PopupFrame("delete booking",booking);
TablePanel.getInctance().updateAll();
}
}
}
};
table.addMouseListener(adapter);
view.addMouseListener(adapter);
confirm.addMouseListener(adapter);
unconfirm.addMouseListener(adapter);
edit.addMouseListener(adapter);
delete.addMouseListener(adapter);
table.setComponentPopupMenu(popup);
table.setEnabled(false);
table.getColumnModel().setColumnSelectionAllowed(true);
table.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getTableHeader().setReorderingAllowed(false);
table.getTableHeader().setResizingAllowed(false);
table.getColumnModel().getColumn(0).setMinWidth(0);
table.getColumnModel().getColumn(0).setMaxWidth(0);
table.getColumnModel().getColumn(0).setWidth(0);
this.add(table.getTableHeader(), BorderLayout.NORTH);
this.setAllColoumnsRenderer();
this.add(table,BorderLayout.CENTER);
}
private void setAllColoumnsRenderer(){
for(int i=0; i<368;i++){
table.getColumnModel().getColumn(i).setCellRenderer(new CustomRenderer());
}
}
private Object[] getObjectArray() {
try {
BufferedReader reader = new BufferedReader(new FileReader("Recources/TableData/HeaderData.txt"));
String s;
s = reader.readLine();
StringTokenizer str = new StringTokenizer(s,",");
String tmp;
int i=0;
Object[] resault = new Object[368];
while(str.hasMoreTokens() && i<368){
tmp=str.nextToken();
if(!tmp.isEmpty()){
resault[i]=tmp;
}else{
resault[i]=" - ";
}
i++;
}
return resault;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
private Object[] getRoomObjectArray(String room,int roomId) {
Object[] resault = new Object[549];
resault[0]=roomId;
resault[1]=room;
return resault;
}
public void addRoom(String roomname,int roomId){
this.remove(table);
((CustomTableModel) model).addRow(getRoomObjectArray(roomname,roomId));
table = new JTable(model);
((CustomTableModel) model).fireTableRowsInserted(0, ((CustomTableModel)model).getRowCount());
this.add(table);
this.revalidate();
this.repaint();
}
public static TablePanel getInctance() {
if (MainLogicInctance == null)
MainLogicInctance = new TablePanel();
return MainLogicInctance;
}
public JTable getTable(){
return table;
}
public void updateAll() {
System.out.println("forceUpdateRunning");
int rowCount = model.getRowCount();
for (int i = rowCount - 1; i >= 0; i--) {
((CustomTableModel) model).removeRow(i);
}
Room rooms[] = Globals.displayAllRooms();
for(Room room : rooms){
this.addRoom(room.getName(),room.getRoomId());
}
Booking bookings[] = Globals.displayAllBookings();
for(Booking booking : bookings){
this.addBooking(booking);
}
System.out.println("Time To Update!! :)");
table.revalidate();
((CustomTableModel)table.getModel()).fireTableDataChanged();
table.repaint();
}
public void addBooking(Booking booking){
int begin=0,end=0,current;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
LocalDate currentDate = LocalDate.parse(booking.getBegin(), formatter);
try {
begin = getDateNumber(booking.getBegin())+2;
end = getDateNumber(booking.getEnd())+2;
current = begin;
while(booking.getRoomID()>=model.getRowCount())
booking.setRoomID(booking.getRoomID()-1);
while(current<=end){
if(Globals.YEAR==currentDate.getYear()){
if(current == begin){
setCell(current,booking.getRoomID(),booking.getName());
}
if(current-1 == begin){
setCell(current,booking.getRoomID(),booking.getSurname());
}
}
currentDate.plusDays(1);
current++;
}
} catch (ParseException e) {
e.printStackTrace();
}
}
public int getDateNumber(String dateString) throws ParseException{
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
LocalDate date = LocalDate.parse(dateString, formatter);
return date.getDayOfYear();
}
public int getYear(String dateString){
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
LocalDate date = LocalDate.parse(dateString, formatter);
return date.getYear();
}
public void setCell(int x,int y,String name){
model.setValueAt(name, y, x);
}
}
CustomTableModel:
package Interface;
import java.text.ParseException;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import javax.swing.table.DefaultTableModel;
import DataBase.Booking;
import Logics.Globals;
public class CustomTableModel extends DefaultTableModel{
private Booking[] bookings;
public CustomTableModel(Object[] data, int i){
super(data,i);
bookings = Globals.displayAllBookings();
}
public int getStatus(int row, int col) {
for(Booking booking : bookings){
System.out.println(booking.toString());
int begin=0,end=0,current;
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
LocalDate currentDate = LocalDate.parse(booking.getBegin(), formatter);
try {
begin = getDateNumber(booking.getBegin())+2;
end = getDateNumber(booking.getEnd())+2;
current = begin;
while(booking.getRoomID()>=super.getRowCount())
booking.setRoomID(booking.getRoomID()-1);
while(current<=end){
if(Globals.YEAR==currentDate.getYear()){
if(current == col && booking.getRoomID() == row){
if(booking.isConfirmed())
return booking.getPersons();
return booking.getPersons()+10;
}
}
currentDate.plusDays(1);
current++;
}
} catch (ParseException e) {
e.printStackTrace();
}
}
return 0;
}
public int getDateNumber(String dateString) throws ParseException{
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("dd-MM-yyyy");
LocalDate date = LocalDate.parse(dateString, formatter);
return date.getDayOfYear();
}
public boolean isCellEditable(int rowIndex, int columnIndex){
return false;
}
}
CustomRenderer:
package Interface;
import java.awt.Color;
import java.awt.Component;
import javax.swing.JLabel;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
public class CustomRenderer extends DefaultTableCellRenderer {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int col) {
JLabel l = (JLabel) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, col);
CustomTableModel tableModel = (CustomTableModel) table.getModel();
//====================Confirmed================
if (tableModel.getStatus(row,col) == 1) {
l.setBackground(new Color(255, 128, 128));
} else if (tableModel.getStatus(row,col) == 2) {
l.setBackground(new Color(255, 102, 102));
} else if (tableModel.getStatus(row,col) == 3) {
l.setBackground(new Color(255, 77, 77));
} else if (tableModel.getStatus(row,col) == 4) {
l.setBackground(new Color(255, 51, 51));
} else if (tableModel.getStatus(row,col) == 5) {
l.setBackground(new Color(255, 26, 26));
} else if (tableModel.getStatus(row,col) == 6) {
l.setBackground(new Color(255, 0, 0));
} else if (tableModel.getStatus(row,col) == 7) {
l.setBackground(new Color(230, 0, 0));
} else if (tableModel.getStatus(row,col) == 8) {
l.setBackground(new Color(204, 0, 0));
//===================Not Confirmed=============
} else if (tableModel.getStatus(row,col) == 11) {
l.setBackground(new Color(255, 204, 128));
} else if (tableModel.getStatus(row,col) == 12) {
l.setBackground(new Color(255, 194, 102));
} else if (tableModel.getStatus(row,col) == 13) {
l.setBackground(new Color(255, 184, 77));
} else if (tableModel.getStatus(row,col) == 14) {
l.setBackground(new Color(255, 173, 51));
} else if (tableModel.getStatus(row,col) == 15) {
l.setBackground(new Color(255, 163, 26));
} else if (tableModel.getStatus(row,col) == 16) {
l.setBackground(new Color(255, 153, 0));
} else if (tableModel.getStatus(row,col) == 17) {
l.setBackground(new Color(230, 138, 0));
} else if (tableModel.getStatus(row,col) == 18) {
l.setBackground(new Color(204, 122, 0));
//=====================empty===================
} else if(col>1){
l.setBackground(new Color(102, 255, 51));
}
return l;
}
}
由于这个版本我想从TablePanel类更新表的软件。但我已经在所有这三个类中尝试过了。
这是一个video它的行为。我希望我已经解释了这个问题,你知道解决方案吗?
DefaultTableCellRenderer.getTableCellRendererComponent
作为参数视图索引。您继续执行以使用这些索引来检索模型中的值。您需要首先使用JTable.convertRowIndexToModel
和JTable.convertColumnIndexToModel
来转换这些索引。然后使用转换后的索引检索模型中的值。
等一等你能解释一下好吗? – BRHSM
@BRHSM屏幕上显示的是视图。存储在内存中的是模型。视图和模型之间有一个映射。例如。当你对行进行排序,重新排列柱子等等,这些都不是在模型中完成的,而是在视图中完成的。视图映射到模型。例如,模型包含{5,4,3,2,1},您按升序排序,视图将包含{1,2,3,4,5},但模型保持不变。该视图将具有行1映射到模型中的行5,视图中的行2到模型中的行4等等。这就是为什么你需要将索引从视图转换为模型或反之亦然。 –
我已经尝试在使用这些值之前将'convertRowIndexToModel'&'convertColumnIndexToModel'添加到GetStatus()方法中,但它使我在使用它们的行上出现'ArrayIndexOutOfBoundsException'(在CustomTableModel中的第35行) – BRHSM
看起来像你不打电话super.setValueAt()
。这是一个工作示例:
public class BookingColorChange {
private static final class AbstractActionExtension extends AbstractAction {
private final JTable table;
private final BookingState newState;
private AbstractActionExtension(String name, JTable table, BookingState newState) {
super(name);
this.table = table;
this.newState = newState;
}
@Override
public void actionPerformed(ActionEvent e) {
int[] selectedColumns = table.getSelectedColumns();
for (int column : selectedColumns) {
int[] selectedRows = table.getSelectedRows();
for (int row : selectedRows) {
if (table.isCellSelected(row, column))
table.setValueAt(newState, row, column);
}
}
table.clearSelection();
}
}
enum BookingState {
FREE {
@Override
Color getColor() {
return Color.GREEN;
}
},
RESERVED {
@Override
Color getColor() {
return Color.ORANGE;
}
},
BOOCKED {
@Override
Color getColor() {
return Color.RED;
}
};
abstract Color getColor();
}
class Boocking {
private BookingState bookingState = BookingState.FREE;
private final Date date;
private final int roomNumber;
public Boocking(Date date, int roomNumber) {
super();
this.date = date;
this.roomNumber = roomNumber;
}
public BookingState getBookingState() {
return bookingState;
}
public void setBookingState(BookingState bookingState) {
this.bookingState = bookingState;
}
public Date getDate() {
return date;
}
public int getRoomNumber() {
return roomNumber;
}
}
public static void main(String[] args) {
final TableModel tableModel = new DefaultTableModel(30, 15) {
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.");
Calendar c = Calendar.getInstance();
@Override
public String getColumnName(int column) {
c.setTime(new Date());
c.add(Calendar.DATE, column);
return sdf.format(c.getTime());
}
@Override
public boolean isCellEditable(int row, int column) {
return false;
}
@Override
public Class<?> getColumnClass(int column) {
return BookingState.class;
}
@Override
public Object getValueAt(int row, int column) {
Object valueAt = super.getValueAt(row, column);
if (null == valueAt) {
int roomNumber = row + 1;
c.setTime(new Date());
c.add(Calendar.DATE, column);
Object dbEntry = fetchEntryFromDataBase(roomNumber, new java.sql.Date(c.getTime().getTime()));
valueAt = translateDbValueToBookingState(dbEntry);
super.setValueAt(valueAt, row, column);
}
return null == valueAt ? BookingState.FREE : valueAt;
}
private BookingState translateDbValueToBookingState(Object dbEntry) {
return null;
}
private Object fetchEntryFromDataBase(int roomNumber, java.sql.Date date) {
// TODO Auto-generated method stub
return null;
}
@Override
public void setValueAt(Object value, int row, int column) {
super.setValueAt(value, row, column);
// update your database here
}
};
TableCellRenderer renderer = new DefaultTableCellRenderer() {
@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
Component rendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,
row, column);
Color color = ((BookingState) value).getColor();
if (isSelected)
color = color.darker();
rendererComponent.setBackground(color);
((JComponent) rendererComponent).setToolTipText(
"Room " + (row + 1) + " " + table.getModel().getColumnName(column) + " " + value);
return rendererComponent;
}
};
JComboBox<BookingState> comboBox = new JComboBox<>(BookingState.values());
TableCellEditor cellEditor = new DefaultCellEditor(comboBox);
JTable jTable = new JTable(tableModel);
jTable.setPreferredScrollableViewportSize(new Dimension(800, 400));
jTable.setDefaultRenderer(BookingState.class, renderer);
jTable.setDefaultEditor(BookingState.class, cellEditor);
jTable.getColumnModel().setColumnSelectionAllowed(true);
jTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
JPanel buttons = new JPanel(new GridLayout(1, 0));
buttons.add(new JButton(new AbstractActionExtension("reserve", jTable, BookingState.RESERVED)));
buttons.add(new JButton(new AbstractActionExtension("boock", jTable, BookingState.BOOCKED)));
buttons.add(new JButton(new AbstractActionExtension("cancel", jTable, BookingState.FREE)));
JPanel jPanel = new JPanel(new BorderLayout());
jPanel.add(new JScrollPane(jTable));
jPanel.add(buttons, BorderLayout.NORTH);
JOptionPane.showMessageDialog(null, jPanel);
}
}
this.add(table.getTableHeader(), BorderLayout.NORTH);
this.setAllColoumnsRenderer();
this.add(table,BorderLayout.CENTER);
首先并非用户通常会在面板上显示的表。通常一个表被添加到一个JScrollPane这样的基本代码将是:
JScrollPane scrollPane = new JScrollPane(table);
this.add(scrollPane, BorderLayout.CENTER);
的表头和表被自动添加到滚动窗格。
添加到它工作正常。这是检索不起作用的数据。
public void addRoom(String roomname,int roomId){
this.remove(table);
((CustomTableModel) model).addRow(getRoomObjectArray(roomname,roomId));
table = new JTable(model);
((CustomTableModel) model).fireTableRowsInserted(0, ((CustomTableModel)model).getRowCount());
this.add(table);
this.revalidate();
this.repaint();
}
每次创建新表或设置一个新的模式,以创建一个新的TableColumnModel
表。这样做的结果是,您将失去添加到表中的所有自定义渲染器/编辑器。
首先,永远不需要重新创建表格。相反,您可以使用表格的setModel(...)
方法。这样做你永远不用担心从框架中删除/添加组件。
但是这仍然不能解决问题。几种解决方案:
- 重置模型后重新添加到自定义渲染器/编辑器中。
- 请勿创建新模型。相反,您可以从模型中删除所有行,然后使用addRow(...)方法重置模型中的行。
- 在第一次创建表时,您可以使用:
table.setAutoCreateColumnsFromModel(false)
。这将防止在重置表格模型时重新创建TableColumnModel。
不要创建渲染器的多个实例:
for(int i=0; i<368;i++){
table.getColumnModel().getColumn(i).setCellRenderer(new CustomRenderer());
}
没有必要创建368个实例你渲染器。渲染器可以被所有列共享。
此外,您不需要将渲染器单独分配给每列。您可以使用表的setDefaultRenderer(...)方法来设置渲染器。
DateTimeFormatter formatter = DateTimeFormatter.ofPattern(“dd-MM-yyyy”);
就像上面的注释一样,这段代码在一个循环内执行。格式化程序可以再次共享。请继续创建可重用的对象。在循环外部创建格式化程序,以便只有一个实例。
你应该为每个GUI组件创建一个单独的MouseAdapter实例,而不是为所有人创建一个... –