Why don't revalidate() & repaint() work like I expect? - java

I expect once combobox has been selected, the JTable will change.
Here is my part of code:
……
chooseAccoutingItemComboBox.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
changeTable();
jScrollpane.revalidate();
jScrollpane. repaint();
}
private void changeTable() {
JTable accountTable2 = new JTable(accountBook.getRowData(startYear, startMonth, endYear, endMonth, (AccountingItem) chooseAccoutingItemComboBox.getSelectedItem()), accountBook.getColumnNames());
accountTable = accountTable2;
}
});
accountTable = new JTable(accountBook.getRowData(startYear, startMonth, endYear, endMonth, accountintItem), accountBook.getColumnNames());
jScrollpane = new JScrollPane(accountTable);
add(jScrollpane, BorderLayout.CENTER);
……
And now when I selected item in combobox, the JTable didn't change. WHY?

Yours is a basic core Java mistake, one which has nothing to do with Swing, revalidate or repaint, and all to do with the core distinction of what is the difference between a Java reference variable and a reference (or object):
Changing the object referenced by a variable will have no effect on the original object. For example, your original displayed JTable object, the one initially referenced by the accountTable variable is completely unchanged by your changing the reference that the accountTable variable holds, and for this reason your GUI will not change. Again understand that it's not the variable that's displayed, but rather the object
To achieve your goal you will instead want to change the state of the displayed JTable. Do this by changing its model.
i.e., by doing something like:
private void changeTable() {
// create a new table model
MyTableModel newModel = new MyTableModel(pertinentParameters);
// use the new model to set the model of the displayed JTable
accountTable.setModel(newModel);
}
Use the parameters that you're currently passing into your new JTable:
accountBook.getRowData(startYear, startMonth, endYear, endMonth,
(AccountingItem) chooseAccoutingItemComboBox.getSelectedItem()),
accountBook.getColumnNames()
to create the new TableModel instead.
In fact you might even be able to create a DefaultTableModel directly with this data, something like:
DefaultTableModel model = new DefaultTableModel(accountBook.getRowData(
startYear, startMonth, endYear, endMonth,
(AccountingItem) chooseAccoutingItemComboBox.getSelectedItem()),
accountBook.getColumnNames());
accountTable.setModel(model);

Related

JTable icon linked to object

I have the following problem: i currently have a few classes representing various plants and animals, which all have base class "Organism", they are part of a world (another class, which contains a "2D" container of Organisms or empty fields - kinda like a 2D map of a world). Only 1 organism can occupy a world slot at the same time.
What i want is to draw a map of this world. In order to do this i have created a JTable, which uses world's 2D container as a data source. Now the problem is, i don't know how to make the JTable use class's own methods in order to draw cells.
Each class has it's method which returns an image representing it:
#Override public ImageIcon drawing(){
return new ImageIcon("Guarana.png");
}
my table has following components available:
private String[] columnNames;
private Object[][] data;
I've created my own table model (following the tutorial here: https://docs.oracle.com/javase/tutorial/uiswing/components/table.html)
So the creation of table looks like this:
model = new MyTableModel(testowyString, organizmy);
JTable table = new JTable(model);
table.setFillsViewportHeight(true);
table.setDefaultRenderer(Organizm.class, new IconRenderer());
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setBounds(210, 0, 780, 900);
add(scrollPane);
As you can see i have tried to create my own IconRenderer (but for some reason the JTable still doesn't draw the icons):
public class IconRenderer extends DefaultTableCellRenderer {
ImageIcon sheepIcon = new ImageIcon("Owca.png");
ImageIcon wolfIcon = new ImageIcon("Wilk.png");
ImageIcon mouseIcon = new ImageIcon("Mysz.png");
ImageIcon chickenIcon = new ImageIcon("Kurka.png");
ImageIcon turtleIcon = new ImageIcon("Zolw.png");
ImageIcon grassIcon = new ImageIcon("Trawa.png");
ImageIcon guaranaIcon = new ImageIcon("Guarana.png");
ImageIcon emptyIcon = new ImageIcon("Pusty.png");
public IconRenderer() { super(); }
public void setValue(Organizm organizm) {
setIcon((organizm == null) ? ikonkaPusty : organizm.rysowanie());
}
}
(yes, the icons here are a part of my old code, when each class didn't return it's icon)
To sum up: i want to make it so when JTable draws a map it draws an icon using an existing method of a class which is linked to cell.
Instead of overriding setValue() you need to override getTableCellRendererComponent() and call setIcon() from there. The value argument will be the Organizm that is the table cell's value, so you can do this:
public Component getTableCellRendererComponent(JTable table,
Object value,
boolean isSelected,
boolean hasFocus,
int row,
int column) {
Organizm o = (Organizm) value;
setIcon((organizm == null) ? ikonkaPusty : organizm.rysowanie());
return this;
}
I'm assuming that organizm.rysowanie() returns the ImageIcon. Although if that's true, then your setValue() implementation probably should have worked. Another thought I have is to change your setValue() signature to public void setValue(Object value) and cast value in the method like in my example above. This would avoid method overloading issues that would result in calling the setValue() method with an Object argument instead of your setValue() method with the Organizm argument.

Updating JTable on button click

I am working with a JTable with a Custom Table Model found here. I have updated my code with the suggestions provided in that post and have run into a new problem. The change I have made to my code was to inject an ArrayList<CompletedPlayer> into my JTable to avoid issues with threads. After doing that, the code to update my table by pressing a button has stopped working.
The code used to initialize the JTable is the following:
TableModel model = new PlayerTableModel(FileHandler.getCompletedPlayers());
JTable table = new JTable(model);
The code I used to update the JTable is the following:
JButton btnRefreshAllPlayers = new JButton("Refresh");
btnRefreshAllPlayers.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
PlayerTableModel model = (PlayerTableModel) table.getModel();
model.fireTableDataChanged();
}
});
I have also tried using repaint() but this does not work either. As of right now, the only way to get the JTable to update is to close and reopen the program. FileHandler has the ArrayList I am using for the JTable which increases in size as the user adds more players.
Why doesn't fireTableDataChanged() detect any changes?
I have searched on stackoverflow and a couple of people have said to use that method.
No, you should not call any fireTableXxx methods outside of the context of the TableModel itself, people suggesting otherwise are simply wrong and it will cause you issues in the future. From the looks of your code, nothing has changed. If you've updated the TableModel according to the answer provided in your previous question, then there is no relationship with the data in the model to the external source. You need to manually reload the data from the external source, create a new TableModel and apply it to the table
For example...
JButton btnRefreshAllPlayers = new JButton("Refresh");
btnRefreshAllPlayers.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
TableModel model = new PlayerTableModel(FileHandler.getCompletedPlayers());
table.setModel(model);
}
});
I have also tried setting a new model with the updated ArrayList and it worked but did not keep the table row widths I previously set.
This is a reasonable thing for the table to do, because it has no idea if the new model has the same properties/columns as the old, so it resets them.
You could walk the ColumnModel, storing the column widths in a List or Map before you apply the model and reapply the widths
Is there a proper way to update the JTable?
You could provide your TableModel with a refresh method, which could load the data itself and trigger a tableDataChanged event
public class PlayerTableModel extends AbstractTableModel {
private final List<PlayerSummary.Player> summaries;
public PlayerTableModel(List<PlayerSummary.Player> summaries) {
this.summaries = new ArrayList<PlayerSummary.Player>(summaries);
}
// Other TabelModel methods...
public void refresh() {
summaries = new ArrayList<>(FileHandler.getCompletedPlayers());
fireTableDataChanged();
}
}
Then you would need to call this method in your ActionListener...
PlayerTableModel model = (PlayerTableModel)table.getMode();
model.refresh();

Refreshing the java Jtable to show updated data

I am trying to refresh my Jtable shown in the UI whenever I query the mysql database. The idea was to show whatever new data updated in the UI JTable.
The UI class is below.
public class DBView {
private JFrame frame = new JFrame();
private JScrollPane tableScrollPane = new JScrollPane();
private DefaultTableModel dbTable = new DefaultTableModel();
public void setDbTable(DefaultTableModel dbTable) {
this.dbTable = dbTable;
//this.dbTable.repaint();
paintDBTable();
}
public DefaultTableModel getDbTable() {
return dbTable;
}
public DBView() {
initializeFrame();
paintDBTable();
}
private void paintDBTable() {
tableScrollPane.setBounds(20, 350, 400, 80);
frame.getContentPane().add(tableScrollPane);
JTable DBTable = new JTable(dbTable);
tableScrollPane.add(DBTable);
DBTable.setFillsViewportHeight(true);
tableScrollPane.setViewportView(DBTable);
}
/**
* Initialize the contents of the frame.
*/
private void initializeFrame() {
frame.setVisible(true);
frame.setBounds(100, 100, 451, 525);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(null);
frame.setTitle("MySQL Database");
}
From another Model class I am calling the setDbTable() method. I input a new Jtable object to the setDbTable() method with data read from the database input into the new Jtable object.
The issue is inside the setDbTable() method, I am using paintDBTable() method again.
I tried using dbTable.fireTableDataChanged() method to refresh the view, didnt work.
The way it is now, it is working. But using the setDbTable() method to refresh seems like a very inefficient way to do it.
Question is Do you see anyway I could use another method defined for use of refreshing Jtables?
P.S. I am very new to java and programming in general. Sorry if the code is messy and the question is unclear. I can give all the code if its helpful. I removed most of the methods and other classes in the original code to make the question clearer.
tableScrollPane.add(DBTable);
JScrollPane isn't designated as container, you have to add child to JViewport
there are two options
a) tableScrollPane = new JScrollPane(myTable);
b) tableScrollPane.setViewportView(myTable);
DefaultTableModel dbTable = new DefaultTableModel();
DefaultTableModel is model that hold value for presentations layer for the JTable
rename this local variable (that make the sence) to dbTableModel instead of dbTable
you have to create a JTables view, f.e. two basics options
a) JTable myTable = new JTable(dbTableModel)
b) myTable.setModel(dbTableModel)
dbTable.fireTableDataChanged() is implemented in DefaultTableModel and correctly, not reason to call this method, nor outside of models definition (class, void, interface that returns XxxTableModel)
more informations in linked Oracle tutorials, ... for working code examples in SSCCE / MCVE form too
refresh data for JTable by
removing all rows in dbTableModel.setRowsCount(0);, then add a new row(s) to dbTableModel.addXxx
re_creating dbTableModel, note then must be added back to JTable e.g. myTable.setModel(dbTableModel)
It is not so confusing to refresh the JTable data and refreshing the UI after that, because:
Swing components implemented MVC and Observer in a very fantastic way. That means whenever you change the data in TableModel, the UI will be notified and repainted as you wanted.
So you should change you code in a way that you keep the JTable variable not the TableModel variable in your class. After that in setDbTable call the setModel method of the JTable, it means:
public class DBView {
private JTable jtable = new JTable();
public void setDbTable(DefaultTableModel dbTable) {
this.jtable.setModel(dbTable);
//this.dbTable.repaint();
//paintDBTable();
}
.
.
.
}
Hope this would be helpful,
Good Luck.

JList value changes when clicked

New to the forum and to Java. I am trying to have my JList respond when double-clicked, which I have accomplished. The JList is being populated by a SQL query which is ran when a button in the GUI is pressed. Based on the SQL query, the JList is populated, this is also working.
The issue comes about if I try to update the JList by clicking the button to query SQL again. When I click that, the change initially shows up in the JList, however when I click on that option in the JList it immediately switches back to what it was initially. When I double-click on what appears to be the incorrect name, the value that I have printing in the console reports correctly. So it has the value correct in the console but the rendering in the JList is not correct.
I appreciate any responses, I have combed the forums without any luck. I am new to Java so I'm sure there is quite a bit that isn't perfect with my code. Code is below please let me know if you need more. Thank you.
public JPanel results(StringBuilder message)
{
StringBuilder[] options = {message};
showOption = new JList(options);
showOption.setLocation(300, 50);
showOption.setSize(140,100);
showOption.setVisibleRowCount(10);
textPanel.add(showOption);
showOption.revalidate();
showOption.repaint();
MouseListener mouseListener = new MouseAdapter() {
public void mouseClicked(MouseEvent e) {
//JList showOption = (JList) mouseEvent.getSource();
if (e.getClickCount() == 2) {
int index = showOption.locationToIndex(e.getPoint());
Object o = showOption.getModel().getElementAt(index);
System.out.println("Double-clicked on: " + o.toString());
}
}
};
showOption.addMouseListener(mouseListener);
return totalGUI;
}
public static void main ( String args[] )
{
//JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("[=] JTextField of Dreams [=]");
GUI_TextField demo = new GUI_TextField();
frame.setContentPane(demo.createContentPane());
//frame.setContentPane(demo.results(message));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(510, 400);
frame.setVisible(true);
}
Three things jump out at me immediately.
You're creating a new JList each time
You're manually setting the size and position of the JList
You're not removing the previous JList
For example...
public JPanel results(StringBuilder message)
{
StringBuilder[] options = {message};
// Create new JList
showOption = new JList(options);
// This is ill advised
showOption.setLocation(300, 50);
showOption.setSize(140,100);
showOption.setVisibleRowCount(10);
// What about the last JList?
textPanel.add(showOption);
This raises a number of possibilities, the likely one is that you are covering over the previous list, which is being brought to the front when textPanel is validated and painted.
Swing follows (loosly) the MVC paradigm (and for more details)
So instead of re-creating the view each time, you should simply re-create the model, for example...
public JPanel results(StringBuilder message)
{
DefaultListModel model = new DefaultListModel();
model.addElement(message);
showOption.setModel(model);
If showOption isn't created initially before this method is called, you should consider putting in a if statement to detect when showOption is null and initialise it appropriately.
You should also avoid using setLocation and setSize. Swing has being designed to operate with the use of layout managers, these make it possible to define workflow and general layout that can be used across multiple platforms.
Take a look at How to use lists and Laying Out Components Within a Container

Row Delete in table

my whole code is this, but my code has one error and not run:
read from file is correctly , but dont delet row.
public class AllUserTable extends AbstractTableModel{
Vector data;
Vector columns;
public AllUserTable() {
String line;
data = new Vector();
columns = new Vector();
try {
FileInputStream fis = new FileInputStream("D:\\AllUserRecords.txt");
BufferedReader br = new BufferedReader(new InputStreamReader(fis));
StringTokenizer st1 = new StringTokenizer(br.readLine(), " ");
while (st1.hasMoreTokens())
columns.addElement(st1.nextToken());
while ((line = br.readLine()) != null) {
StringTokenizer st2 = new StringTokenizer(line, " ");
while (st2.hasMoreTokens())
data.addElement(st2.nextToken());
}
br.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public int getRowCount() {
return data.size() / getColumnCount();
}
public int getColumnCount() {
return columns.size();
}
public Object getValueAt(int rowIndex, int columnIndex) {
return (String) data.elementAt((rowIndex * getColumnCount())
+ columnIndex);
}
public static void main(String[] args){
final AllUserTable aut1=new AllUserTable();
final JFrame frame1=new JFrame();
final JTable table=new JTable();
final JPanel panel=new JPanel();
JButton button1=new JButton("Delete");
button1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
DefaultTableModel model=new DefaultTableModel(data,columns); //Error!
model.removeRow(table.getSelectedRow());
table.setModel(model);
table.setModel(aut1);
panel.add(table);
}
});
JScrollPane scroolpane=new JScrollPane();
scroolpane.setViewportView(table);
panel.add(scroolpane);
panel.add(button1);
frame1.add(panel);
frame1.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame1.setBounds(200, 80, 600, 500);
frame1.setVisible(true);
}
}
please repair my code!
This code doesn't compile. You're referencing instance fields (data, columns) from a static method (main). The error message of the compiler should tell you that. And if you don't understand it, googling for the error message should lead you to explanations of the error message.
And even if it did compiler, here's an explanation of each line of the event listener:
DefaultTableModel model=new DefaultTableModel(data,columns);
Create a new table model. Why? Your table already has one. All you need is to modify the existing table model
model.removeRow(table.getSelectedRow());
Remove a row from this new table model. Why modify a new model instead of modifying the existing one?
table.setModel(model);
Replace the old model by a new one. This is very inefficient. The table already has a model. Modify it.
table.setModel(aut1);
Replace the new model by the old one. Why?
panel.add(table);
Add the table to the main panel of the frame. Why? The table is already there and visible. This makes no sense.
You should implement a deleteRow() method in your AbstractTableModel.
Using a List of objects, where each row represents a row, would also be much clearer than using a single list to represent all the rows.
I have the feeling that you're copying and pasting code without understanding at all how it works and what the architecture of Swing is. Read the Swing tutorial. And even before that, read an introductory book about Java, variables and scopes.
Firstly, you never set the table model (aut1) to the table BEFORE your button action.
Secondly, I don't see where data and columns are defined when you construct the new DefaultTableModel in your action handler - the you try and delete a row from this "new" table model and the apply it to the table and then replace to with aut1, which effectively has done....nothing.
When constructing your UI, don't forget to set the table model to the table ... table.setModel(aut1)
Remove the DefaultTableModel from the action handler and use aut1 instead.
Updated with suggestions
You should avoid constructing your UI in the static main method, instead use a class instance instead, it will make you code cleaner and will present less issues into the future
You should only ever construct/manipulate UI/Swing components from within the context of the Event Dispatching Thread, use EventQueue#invokeLater

Categories