Repainting problem when scrolling nested table in Java 9+ - java

I have a Swing UI element that uses nested JTables. That is, the TableCellRenderer of the outer table returns JTable objects as the Component to render for each cells. This code works great under Java 8. Using the exact same code under Java 9 or Java 10 causes the inner table to not be repainted properly when scrolling the table. The table IS repainted properly when the window is resized.
Edit:
I did some more digging and traced the problem to lines 1862:1877 in BasicTableUI.java - these lines were added in Java 9. If I make my own TableUI without these lines, the problem disappears. From debugging, it appears that the issue is that in my case rMin and rMax are equal and setting rMax = rMax - 1 makes rMax < rMin which is obviously is bad. The added code seems to be there to deal with issues printing a table. Not sure why nested tables makes this break.
See the SSCE below:
package testing.test_painting;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.plaf.basic.BasicTableHeaderUI;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
import javax.swing.table.TableModel;
import java.awt.Component;
import java.awt.Dimension;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
class InnerTable extends JTable {
public InnerTable(TableModel dm) {
super(dm);
}
}
class InnerTableModel extends AbstractTableModel {
#Override
public int getRowCount() {
return 500;
}
#Override
public int getColumnCount() {
return 10;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return rowIndex + "," + columnIndex;
}
}
class OuterTableModel extends AbstractTableModel {
private final List<JTable> termTables;
public OuterTableModel() {
this.termTables = Arrays.asList(new InnerTable(new InnerTableModel()));
}
#Override
public int getRowCount() {
return 1;
}
#Override
public int getColumnCount() {
return termTables.size();
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
return termTables.get(columnIndex);
}
public JTable getTermTable(int modelColumn) {
return termTables.get(modelColumn);
}
}
class OuterTable extends JTable {
private final List<TableRenderer> renderers;
private class TableRenderer implements TableCellRenderer {
private final OuterTableModel tableModel;
public TableRenderer(OuterTableModel tableModel) {
this.tableModel = tableModel;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
int modelColumn = convertColumnIndexToModel(column);
JTable termTable = tableModel.getTermTable(modelColumn);
termTable.setVisible(true);
return termTable;
}
}
private final OuterTableModel tableModel;
public OuterTable(OuterTableModel tableModel) {
super(tableModel);
renderers = new ArrayList<>(tableModel.getColumnCount());
for (int i = 0; i < tableModel.getColumnCount(); i++) {
renderers.add(new TableRenderer(tableModel));
}
this.tableModel = tableModel;
setCellDimensions();
}
#Override
public void setTableHeader(JTableHeader tableHeader) {
tableHeader.setUI(new BasicTableHeaderUI());
super.setTableHeader(tableHeader);
}
private void setCellDimensions() {
Dimension preferredSize = tableModel.getTermTable(0).getPreferredSize();
if (getRowHeight() != preferredSize.height) {
setRowHeight(preferredSize.height);
}
TableColumnModel columnModel = getColumnModel();
for (int i = 0; i < columnModel.getColumnCount(); i++) {
TableColumn column = columnModel.getColumn(i);
column.setMinWidth(preferredSize.width);
column.setMaxWidth(preferredSize.width);
column.setPreferredWidth(preferredSize.width);
column.setWidth(preferredSize.width);
}
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
}
#Override
public TableCellRenderer getCellRenderer(int row, int column) {
return renderers.get(column);
}
}
public class TestNestedTable {
public static void main(String[] args) {
OuterTableModel mainTableModel = new OuterTableModel();
OuterTable mainTable = new OuterTable(mainTableModel);
JFrame jFrame = new JFrame();
JScrollPane scrollPane = new JScrollPane(mainTable);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
jFrame.getContentPane().add(scrollPane);
jFrame.pack();
jFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
jFrame.setVisible(true);
}
}

make my own TableUI without these lines, the problem disappears.
Or simpler, you might be able to use JViewport#SIMPLE_SCROLL_MODE:
JScrollPane scrollPane = new JScrollPane(mainTable);
scrollPane.getVerticalScrollBar().setUnitIncrement(10);
scrollPane.getViewport().setScrollMode(JViewport.SIMPLE_SCROLL_MODE);

I know its and old question, but I had the same issue and maybe someone else with the same problem finds this helpful.
This is an JDK bug reported several times (so a lot of duplicates) but most information can be found in the following ticket:
https://bugs.openjdk.java.net/browse/JDK-8202702
There has been added these lines to the BasicTableUI:
// Do not decrement rMax if rMax becomes
// less than or equal to rMin
// else cells will not be painted
if (rMax - rMin > 1) {
rMax = rMax - 1;
}
(s. http://hg.openjdk.java.net/jdk/client/rev/3ba3d39b91c7
or
https://github.com/openjdk/jdk/commit/bb9fed1008dee377725dc669401c389da685f618#diff-ae77528c5d554a9870371e7075801adc4ecd8f992ff484804f164b692b388858)
It is fixed for JDK 12 onward. Unfortunaltey, it is not backported to JDK 11, so if you are forced to use this LTS version one can either do it like #MattWallace and use own TableUI or you can overwrite your Table extending JTable with
#Override
public int getSelectedRow()
{
final int i = super.getSelectedRow();
return i == -1 ? -2 : i;
}
This might bring the problems back mentioned in BasicTableUI comments:
// We did rMax-1 to paint the same number of rows that are drawn on console
// otherwise 1 extra row is printed per page than that are displayed
// when there is no scrollPane and we do printing of table
// but not when rmax is already pointing to index of last row
// and if there is any selected rows
But if you can live with that, this can be also a workaround. Other workarounds like wrapping the Table in a JViewPort or a JScrollPane I cannot recommend as I got size issues with tables which can have changing row heights due to long texts.

Related

Java JTextArea dynamic column and row numbering

I've had a quick Google around, and I can't seem to find a good solution to this, mostly because I'm not sure how to describe it.
Essentially, I need to display an arbitrary amount of hex characters in a JTextArea, and I'd like to have them spaced evenly, and have the positions of the characters shown at the top and left of the characters.
This is an example of what I'd like to achieve, this is the hex viewer WinHex.
I've been playing around with converting a byte array to a String, and then text-wrapping it, but I've had some odd results. Any advice on how to achieve something similar to this would be appreciated.
Another option I've considered is using a JTable, but I'm wondering if that's over complicating the matter slightly. Maybe.
Thanks
I've considered is using a JTable, but I'm wondering if that's over complicating the matter slightly
A decade ago, when I was trying to understand JTable I created myself a simple hex editor to try to understand table models, renderers and editors.
Check out Hex Editor for my result. Just unzip the file and compile all the java files and then execute the Hex class.
I haven't looked at the code in 10 years so I don't know if I followed all best coding practices, but have fun anyway.
This should get you started, using a very simple implementation of AbstractTableModel. This only took me 15 minutes to write (in response to "overcomplicating the issue").
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
public class HexText extends JFrame {
public static void main(String... args) {
final HexText window = new HexText();
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
window.setVisible(true);
}
});
}
private static class HexTableModel extends AbstractTableModel {
List<Integer> data = new ArrayList<>();
#Override
public int getRowCount() {
return data.size();
}
#Override
public int getColumnCount() {
return 9;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return Integer.toHexString(rowIndex << 5);
} else {
int row = data.get(rowIndex);
int theByte = 0xFF & (row >> (columnIndex * 2));
String output = Integer.toHexString(theByte);
if (output.length() == 1)
output = "0" + output;
return output;
}
}
public void addRow(int rowElement) {
data.add(rowElement);
fireTableRowsInserted(data.size() - 1, data.size() - 1);
}
}
public HexText() {
JPanel contentPane = new JPanel(new BorderLayout());
HexTableModel theModel = new HexTableModel();
JTable theTable = new JTable(theModel);
Random r = new Random();
for (int i = 0; i < 20; i++) {
theModel.addRow(r.nextInt());
}
contentPane.add(theTable, BorderLayout.CENTER);
this.add(theTable);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.pack();
}
}

JTable, how can I put icons in the header?

i'm trying to render some icons in a JTable header without success. I've already tried something like this: http://www.java2s.com/Tutorial/Java/0240__Swing/CustomizingColumnHeaderswithIcons.htm
but it doesn't show any icon in the header. How can I reach this goal?
Your link is good code , but in fact when you load a picture using absolute path or relative path , this code is not portable .So it is better to load picture from a class. Levels :
1) Create a empty class with this name : ImageResources
2) Please copy your picture
3) Please find ImageResources class in package explorer (in eclipse) and do right click on it (ImageResources) and press "Paste" menuItem.
4) Finally you need a small change in your code like this
ImageIcon blueIcon = new ImageIcon(ImageResources.class.getResource(yourPictureName));
It is working :)
Below method worked for me.
MyTable.getColumn("ColumnName").setHeaderRenderer(new TableCellRenderer() {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
return new JLabel(new ImageIcon(getClass().getResource("/project/img/ImgName.png")));
}
});
The question is a bit older, but I was only now faced with the same question.
Another approach is to override the TableModel's getColumnName function. HTML formatted text is supported.
Here is the sample code:
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.TableModel;
public class TableColumnHeaderIcon {
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
TableModel tableModel = new AbstractTableModel() {
#Override
public String getColumnName(int columnIndex) {
// Build the html formatted text
StringBuilder sb = new StringBuilder();
sb.append("<html>");
sb.append("<img src=\"https://via.placeholder.com/16/007AAE/\">");
sb.append(" Column Title");
sb.append("</html>");
return sb.toString();
}
#Override
public int getColumnCount() {
return 1;
}
#Override
public int getRowCount() {
return 0; // No data required
}
#Override
public Object getValueAt(int i, int i1) {
return null; // No data required
}
};
// Setup UI
JScrollPane scrollPaneTable = new JScrollPane();
JTable table = new JTable(tableModel);
scrollPaneTable.setViewportView(table);
JFrame mdi = new JFrame("Test JTable Column Header with Icon");
mdi.setSize(600, 400);
mdi.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
mdi.add(scrollPaneTable);
mdi.setVisible(true);
});
}
}

JTable - how do i insert a new JLabel for each row in a specific column?

im been looking around on the internet for about 5 hours now to debug on this issue ive been having. and Basically i haven't been able to find anywhere where a person tries to add a new JLabel for each row in a specific column.
functionality explanation: i get a file url, i take the postFix which is usually xlsx or
doc - this postfix i want to display in a JLabel in coalition with a Excel or Doc
Icon -.- but what my current code does is just paint the same JLabel over and over again because it only sets the CellRenderer one time and uses it on all the rows dispite the fact that im setting it in a for-loop for each iteration - all my code is correct for this functionality up until the renderer only gets called once.
so my question goes as follows - how do i add a new JLabel for each row
in a Column ? -
my code follows.
my TableCellRenderer:
public class JLabelRenderer extends DefaultTableCellRenderer {
private static final long serialVersionUID = -166379583761969293L;
// private String fileExtension;
// private JLabel label;
private LogEntry log;
private JLabel label;
public JLabelRenderer(LogEntry log) {
label = new JLabel();
System.out.println("makeing a new JLabelRenderer");
this.log = log;
}
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
label = (JLabel) super.getTableCellRendererComponent(table, value,
isSelected, hasFocus, row, column);
System.out.println(log.getFileExtension());
if (log.getFileExtension().equalsIgnoreCase("xlsx")) {
label.setIcon((ImageIcon) Pictures.getXlsx());
label.setText(log.getFileExtension());
} else if (log.getFileExtension().equalsIgnoreCase("doc")) {
label.setIcon((ImageIcon) Pictures.getDoc());
label.setText(log.getFileExtension());
} else if (log.getFileExtension().equalsIgnoreCase("docx")) {
label.setIcon((ImageIcon) Pictures.getDoc());
label.setText(log.getFileExtension());
} else if (log.getFileExtension().equalsIgnoreCase("pdf")) {
label.setIcon((ImageIcon) Pictures.getPdf());
label.setText(log.getFileExtension());
}
value = label;
return label;
}
#Override
public void setHorizontalAlignment(int alignment) {
super.setHorizontalAlignment(alignment);
}
public void setLog(LogEntry log) {
this.log = log;
}
}
where i make my model:
(i have alot more code adding actionListeners to right-click functionality and what not
but thats hardly relevant)
public void makeLogModel()
{
logModel = new DefaultTableModel();
//addCellEditorListener(this);
logModel.setColumnIdentifiers(new String[]{"Lavet Dato", "Lavet Af", "Beskrivelse", "Sidst Redigeret Dato", "Sidst Redigeret Af", "Fil Type"});
setAutoCreateRowSorter(true);//allows to sort through the information.
setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
setModel(logModel);
}
Where the magic was suppose to happen(update the log table after a search on logs)
public void updateLogTable(ArrayList<LogEntry> entryList)
{
logModel.setRowCount(entryList.size());
for(int i = 0; i < logModel.getRowCount(); i++)
{
setRowHeight(i, 30);
}
int row = 0;
for(LogEntry log : entryList)
{
logModel.setValueAt(log.getCreateDate(), row, 0);
logModel.setValueAt(log.getMadeBy(), row, 1);
logModel.setValueAt(log.getDescription(), row, 2);
logModel.setValueAt(log.getLastEdited(), row, 3);
logModel.setValueAt(log.getLastEditedBy(), row, 4);
labelRenderer = new JLabelRenderer(log);
getColumn("Fil Type").setCellRenderer(labelRenderer);
logModel.setValueAt(new JLabel(), logRow, 5);
row++;
}
}
i have read the Java documentation for components and Editors- but none of the
code examples are for a JLabel - probably because CellRenderer extends JLabel..
i have allso sniffed up the info that the 'value' parameter in the getTableCellRendererComponent() method is the one that is supposed to be set dynamically somehow... any surgestions would be greatly apriciatet, feel free to ask any questions.
thanks
logModel.setValueAt(new JLabel(), logRow, 5);
don't put JLabel, any JComponent to the model, XxxTableModel is designated to nest value for Renderer or Editor only, more in Oracle tutorial How to use Tables - Creating a Table Model
Renderer or Editor visually represents real JComponents, more in Oracle tutorial How to use Tables - Concepts: Editors and Renderers
label.setIcon((ImageIcon) Pictures.getXlsx());
Icon should be placed into local variables or array, list, whatever of Icons, don't load any FileIO from Renderer, renderer can be called many times per one second, e.g. from all mouse, keys and methods inplemented in APIs
Seems your problem in DefaultTableCellRenderer implementation.
You create renderer like JLabelRenderer(LogEntry log) because of, for all cells you have one instance of LogEntry, and log.getFileExtension() in getTableCellRendererComponent() returns same result for all rows.
In case of TableCellRenderer you need to use value parametr from getTableCellRendererComponent() method to determine extension and LogEntry instance.
Examine Concepts: Editors and Renderers.
Here is simple example for you, I use color instead of icons:
import java.awt.Color;
import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.TableCellRenderer;
public class TestFrame extends JFrame{
public TestFrame(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
init();
pack();
setVisible(true);
}
private void init() {
JTable table = new JTable(new Object[][]{
{1,"doc"},
{2,"xlsx"},
{3,"abc"}
},new Object[]{"nmb","extension"});
table.getColumnModel().getColumn(1).setCellRenderer(getRenderer());
add(new JScrollPane(table));
}
private TableCellRenderer getRenderer() {
return new DefaultTableCellRenderer(){
#Override
public Component getTableCellRendererComponent(JTable table,
Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
Component tableCellRendererComponent = super.getTableCellRendererComponent(table, value, isSelected, hasFocus,row, column);
if("doc".equals(value)){
tableCellRendererComponent.setBackground(Color.YELLOW);
} else if("xlsx".equals(value)){
tableCellRendererComponent.setBackground(Color.GREEN);
} else {
tableCellRendererComponent.setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
}
return tableCellRendererComponent;
}
};
}
public static void main(String... strings) {
new TestFrame();
}
}
Also don't put Component's to TableModel like here logModel.setValueAt(new JLabel(), logRow, 5); just value.

JTable doesn't update, calling the setValueAt method doesn't seem to work

I'm creating a sudoku game and I'm having quite some trouble with JTables ...
I just can't get it to fire events, for some reason even when calling the setValueAt from the main class nothing happens. It does work when doing it inside the model itself though ...
As you can see I've tried a tableModelListener but that doesn't work either.
To sum up my question: why doesn't my table fire events and the setValuesAt method doesn't do anything?
The abstractModel:
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.table.AbstractTableModel;
class SudokuTableModel extends AbstractTableModel
{
private int[][] data;
private int[][] originalBoard;
/**
*
* #param board the board to be played with
*/
public SudokuTableModel(int[][] board)
{
data = board;
originalBoard = board;
}
#Override
public int getColumnCount()
{
return 9;
}
#Override
public int getRowCount() {
// this
return 9;
}
#Override
public boolean isCellEditable(int row, int col)
{
System.out.println("Is it editable?");
if(originalBoard[row][col]<0) //if the value is -1 -> it is not one of the hints and thus can be edited
//it has to be checked with the original board because the user might want to change something they did
return true;
return false;
}
/**
* #return the value at row, col
*/
#Override
public Object getValueAt(int arg0, int arg1)
{//if its a "sudoku" number, return it, otherwise return null
//^thta doesn't seem to work so we'll just return the value. With the size of the grid, number that is not exactly 1 char will be displayed as 3 dots so its fine
return (data[arg0][arg1]<0)?-1:data[arg0][arg1];
}
/**
*
* #param value changes to this value #
* #param row
* #param col
*/
public boolean setValueAt(int value, int row, int col, int randomValue)
{
originalBoard[row][col]=value;
System.out.println("Setting value");
fireTableCellUpdated(row, col);
return true;
}
public Class getColumnClass(int c) {
return getValueAt(0, c).getClass();
}
}
And the main code:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.*;
import javax.swing.*;
import javax.swing.table.*;
import javax.swing.text.*;
import javax.swing.border.*;
import javax.swing.event.TableModelEvent;
import javax.swing.event.TableModelListener;
/**
* #Program Sudoku
*
* #author Tomas Svitil
* #date November 2013
* #school CTU
* #hardware MacBook Pro 17", mid 2010, i7, 8GiB RAM
* #IDE eclipse SDK 4.3.1
* #purpose this is the main playing board. Options such as save and load will be handled by a helper class -SudokuTable Helper
*
*/
public class SudokuTablePreRenderer extends JFrame implements TableModelListener
{
JTable table;
int[][] board;
/**
*
* #param toBeUsed the board to be used
* #param isSolution if true the 'close' button just disposes the window
*/
public SudokuTablePreRenderer(int[][] toBeUsed,boolean isSolution)
{
//lets use our sudoku model
SudokuTableModel model = new SudokuTableModel(toBeUsed);
//make a copy of the passed
board=toBeUsed;
table = new JTable( model )
{
public Component prepareRenderer(TableCellRenderer renderer, int row, int column)
{
Component c = super.prepareRenderer(renderer, row, column);
JComponent jc = (JComponent)c;
if(!isRowSelected(row))
{
c.setBackground(toBeGray(row,column) ? Color.WHITE : Color.LIGHT_GRAY);
boolean top = top(row);
boolean left = left(column);
boolean bottom = bottom(row);
boolean right = right(column);
jc.setBorder(new MatteBorder(top?1:0,left?1:0,bottom?1:0,right?1:0, Color.BLACK));
}
return c;
}
//each returns true if the cell should have a *method name* border
private boolean top(int row)
{
if(row==0||row==3||row==6)
return true;
return false;
}
private boolean left(int col)
{
if(col==0||col==3||col==6)
return true;
return false;
}
private boolean bottom(int row)
{
if(row==8)
return true;
return false;
}
private boolean right(int col)
{
if(col==8)
return true;
return false;
}
//returns true if cell should be shaded in gray
private boolean toBeGray(int row, int col)
{
ArrayList<Integer> SetA = new ArrayList<Integer>();
ArrayList<Integer> SetB = new ArrayList<Integer>();
int[] a = {0,1,2,6,7,8};
int[] b = {3,4,5};
for(int i=0;i<a.length;i++ )
SetA.add(a[i]);
for(int i=0;i<b.length;i++)
SetB.add(b[i]);
if((SetA.contains(row)&&SetA.contains(col))||(SetB.contains(row)&&SetB.contains(col)))
return true;
return false;
}
};
//we don't need the table header
table.setTableHeader(null);
//set the base GOOEY stuff
table.setPreferredScrollableViewportSize(table.getPreferredSize());
table.changeSelection(0, 0, false, false);
JScrollPane scrollPane = new JScrollPane( table );
getContentPane().add( scrollPane );
//we're going to store only single digits, so the size we want is exactly one digit
table.getColumnModel().getColumn(0).setMaxWidth(15);
table.getColumnModel().getColumn(1).setMaxWidth(15);
table.getColumnModel().getColumn(2).setMaxWidth(15);
table.getColumnModel().getColumn(3).setMaxWidth(15);
table.getColumnModel().getColumn(4).setMaxWidth(15);
table.getColumnModel().getColumn(5).setMaxWidth(15);
table.getColumnModel().getColumn(6).setMaxWidth(15);
table.getColumnModel().getColumn(7).setMaxWidth(15);
table.getColumnModel().getColumn(8).setMaxWidth(15);
//if this table is used to display the result, "closing" the window will just dispose it, and not quit the program
if(isSolution)
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
else setDefaultCloseOperation( EXIT_ON_CLOSE );
pack();
//put it somewhere nice
setLocationRelativeTo( null );
//set the size so that its only the size of the
setSize(143,176);
//and we want it at one size only
setResizable(false);
setVisible(true);
table.getModel().addTableModelListener(this);
TableModelListener tableModelListener = new TableModelListener()
{
#Override
public void tableChanged(TableModelEvent e)
{
if(e.getType()==TableModelEvent.UPDATE)
{
System.out.println("YA?");
}
}
};
}
#Override
public void tableChanged(TableModelEvent e)
{
int row = e.getFirstRow();
int column = e.getColumn();
TableModel model = (TableModel)e.getSource();
String columnName = model.getColumnName(column);
Object data = model.getValueAt(row, column);
table.setValueAt(4, row, column);
System.out.println("Something happened yay");
// Do something with the data...
}
//
/**
*
* #return a copy of the board
*/
public int[][] getBoard()
{
int[][] returnee = new int[9][9];
for(int i = 0; i < board.length; i++)
returnee[i] = board[i].clone();
return returnee;
}
/**
* #return reference to the "real" board
* #WARNING - By manipulating the array from this method you're manipulating the methods array itself! IF NOT SURE USE THE GetBoard METHOD!
*
*/
public int[][] getRealBoard()
{
return board;
}
//------debugging---------------------------
private void setValue()
{
System.out.println(table.getValueAt(3,4));
}
public void doStuffs()
{
System.out.println(table.isCellEditable(2,2));
}
//----------main just for debugging----------
public static void main(String[] args)
{
int[][] hi = new int[9][9];
for(int i=0;i<9;i++)
for(int j=0;j<9;j++)
hi[i][j]=-1;
hi[3][3]=0;
SudokuTablePreRenderer frame = new SudokuTablePreRenderer(hi,false);
frame.setValue();
frame.doStuffs();
}
}
For some reason you have two 2-dimensional int arrays in your model, data and originalBoard.
The first array, data, you display information from in the JTable via getValueAt(...)
The second array, originalBoard, you add data to the model via setValueAt(...).
This disparity means that adding data to the model will have no effect on the data displayed in the JTable which begs the question -- why is your model set up this way?
You state,
To sum my question, why doesn't my table fire events and can't setValuesAt.
and I'll bet that the table events do in fact get fired, but since the data held by the data array never changes, the JTable will never change its display.
If you want new data added to update the display, then the array that gets the data should be the same as the one that displays the data. At least that seems to make the most sense to me.
Edit:
Regarding:
Sadly I don't think thats the case. This design is something I did before I found out that arrays are just 'references', and so in reality the two arrays should be the one and same. The other problem is that I put a System.out.print into the setValueAt, and when I called it from inside the model (called get valueAt from outside, and that called setValueAT) it worked perfectly, it set the value, it even fired When I call the method from outside though it doesn't work
You are right, sorry. I retract my answer above.
However, another potential problem: your setValueAt method is not a true override, is never called, and the actual AbstractTableModel setValueAt method which you call is never overridden and will have no effect.
Get rid of the TableModelListener, and change your setValueAt to:
#Override
public void setValueAt(Object value, int row, int col) {
originalBoard[row][col] = ((Integer) value).intValue();
System.out.println("Setting value");
fireTableCellUpdated(row, col);
// return true;
}

Swing GUI : Scrolling not updated when a JTable gets bigger

I got a Java Swing GUI and got a problem with a JTable in a JScrollPane. For some reason, when the rows of the table model are increased during the program execution, the JScrollPane isn't updated - that is, if the rows are increased so that the height of the table is over the height of the scroll view, the scroll panes aren't updated as supposed. (The new rows are shown at the screen as expected). If the window is resized, scrolling is updated as expected.
The vertical scrolling policy is VERTICAL_SCROLLBAR_AS_NEEDED, table models fireTableDataChanged is called..
Unfortunately the code's a bit complex so I can't provide an code sample causing the problem. But thought to ask if somebody got some ideas straight..
EDIT: Still a bit more confusing : horizontal scrolling policy is HORIZONTAL_SCROLLBAR_AS_NEEDED, and if the table width if over the view width (that is, the horizontal scrollbar is used), this problem doesn't occur...
EDIT2: The problem isn't that the table should be scrolled but that the scrollbar's aren't activated as they should.
You might need to post some of your code. I've just knocked up the following test and it works as advertised, i.e. vertical scrollbars are activated when the number of rows exceeds the viewport height:
import java.awt.GridLayout;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.ScrollPaneConstants;
import javax.swing.table.AbstractTableModel;
public class JTableTest {
public static void main(String[] args) {
final MyTableModel tm = new MyTableModel();
tm.addData(new Data("R1C1", "R1C2"));
JTable table = new JTable(tm);
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
JFrame frame = new JFrame();
frame.setLayout(new GridLayout());
frame.add(scrollPane);
frame.pack();
frame.setSize(400, 150);
frame.setVisible(true);
Thread t = new Thread(new Runnable() {
private int count = 2;
public void run() {
for ( ; ; ) {
tm.addData(new Data("R" + count + "C1", "R" + count + "C2"));
count++;
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
});
t.start();
}
private static class MyTableModel extends AbstractTableModel {
private List<Data> dataList = new ArrayList<Data>();
public int getColumnCount() {
return 2;
}
public void addData(Data data) {
dataList.add(data);
fireTableRowsInserted(dataList.size()-1, dataList.size()-1);
}
public int getRowCount() {
return dataList.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
Data data = dataList.get(rowIndex);
return columnIndex == 0 ? data.data1 : data.data2;
}
}
private static class Data {
public String data1;
public String data2;
public Data(String data1, String data2) {
this.data1 = data1;
this.data2 = data2;
}
}
}
Hmm.. after returning to the issue I found out that our row header customizing in JScrollPane was causing the problem. (Some preferredsizes were set with not-so-sensible values etc)..

Categories