JTable icon linked to object - java

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.

Related

Iterating changes over multiple labels at once

I am trying to make a function in my program that will read a string, and then set every jlabel on my page to that string. I am aware of .setText(); but I am intending to do this to 100's of labels at once. Using the below code I intended to use a for loop and call the method for as many times as I will have labels and send the label name within the function. i.e.
button.pressed() {
updateLabel(labelName);
}
public void updateLabel(String name) {
…
}
But to test the code initially I just hardcoded the name of one label in and ran it. the first time it worked but now I get a null pointer exception every time. please someone help. The actual code below:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
labeltest();
}
public void labeltest() {
for (Component c : this.getContentPane().getComponents()) {
if (c.getName().equals("Jlabel")) {
// do the modifications...
((JLabel) c).setText("Updated");
}
}
}
By default, Component.name is null, therefore to select only label, you should use instance of:
private void updateLabels(String str) {
for (Component component : getContentPane().getComponents())
if (component instanceof JLabel)
((JLabel)component).setText(str);
}
I think in general case, there're many component on the panel and a few labels. Therefore
using for (Component component : getComponents()) {} is not the best solution.
In case you want to set the same text to all the labels, then is is better to create a List<JLabel> with all labels that should be updated and us it to set a text:
private final List<JLabel> labels = new ArrayList<>();
private void init() {
add(createLabel());
}
// hide add to labels within create method
private JLabel createLabel() {
JLabel label = new JLabel():
labels.add(label);
return label;
}
In case you want to set different text to the labels, then it is better to set correct name for the labels and use Map<String, JLabel> with all labels:
private final Map<String, JLabel> labels = new HashMap<>();
private final static String NAME_ACCOUNT = "account";
private final static String NAME_SCORE = "score";
private void int() {
add(createLabel(NAME_ACCOUNT));
add(createLabel(NAME_SCORE);
}
// hide add to labels within create method
private JLabel createLabel(String name) {
JLabel label = new JLabel():
label.setName(name);
labels.put(name, label);
return label;
}
Here's a potential problem:
if (c.getName().equals("Jlabel")) {
as most Swing components don't have the name property set unless you, the coder, explicitly set it, and I have to wonder if this is the source of your NullPointerException.
Better to do:
if (c instanceof JLabel) {
// ....
}
So yes, you could recursively iterate through all components held in all Containers on your GUI, checking each if instanceof JLabel, and then casting and setting, but ... no, this is not clean, not good.
Why not simply put the JLabels into a collection such as
List<JLabel> labels = new ArrayList<>();
and then use a for loop, iterate over the list and change the state of the labels.
.... or I wonder if you're looking to use other components to display your Strings, such as a JList<String> perhaps?

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

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);

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.

Create GUI components with states

I was asking about the right way to make a component that holds some state. Like a Jbutton that saves a color in it, or a list item that saves a certain object. So when those GUI components fire an event I can use the saved states to do something with it.
My way was like that:
1- Make a subclass of the required component, like a subclass from Jbutton.
2- Make a Listener for this new subclass : in the listener check if the event source is the subclass, convert it then use the stored data.
Example:
class ColorButton extends JButton
{
static class Listener implements ActionListener{
#Override
public void actionPerformed(ActionEvent actionEvent) {
Object source = actionEvent.getSource();
if( source.getClass() == ColorButton.class)
{
ColorButton t = (ColorButton) source;
t.getComponent().setBackground(t.getColor());
}
}
}
//states i want to be saved
private Color c;
private Component comp;
ColorButton(Component comp, Color c) {
setColorChanger(comp, c);
}
/* ......
......
rest of constructors added with those additions
......
*/
private void setColorChanger(Component comp, Color c)
{
this.comp = comp;
this.c = c;
}
Color getColor() {
return c;
}
Component getComponent() {
return comp;
}
}
And I use it this way:
JPanel panel = new JPanel();
ColorButton.Listener l = new ColorButton.Listener();
JButton b = new ColorButton("Blue", panel, Color.BLUE);
JButton r = new ColorButton("Red", panel, Color.RED);
r.addActionListener(l);
b.addActionListener(l);
panel.add(b);
panel.add(r);
add(panel);
So I was wondering if this way is okay or what, I feel it is very boring to make this for every component that should hold a certain states, is there a better way?
Yes, there is a better way. Every single component object should have its own separate ActionListener, so that you don't have to check if( source.getClass() == ColorButton.class), and you can directly access the fields of the component by name, without having to go through the source at all. For that to work, you have to use a non-static inner class, or an anonymous inner class. That if statement is a very old-fashioned and non-OOP way of doing things.
In fact, the component object itself can be its own ActionListener - but that style only allows you to have one ActionListener, and is a bit less well-organised.
The better way is dependent on what kind of state you want to hold and what use you want to make of it. Without thinking that through so that you can state it, it isn't possible to make an overall plan for a better way to do it. Is setting color the only thing you want to do? Do you need to mix regular JButtons with ColorButtons in your application?

Adding picture in jlist

I am writing a simple text based chat application using applets in java, which consists of few components & one of them is my Jlist which provides list of online users at that particular point of time.
What i want is that i need to set a small picture besides online user name in Jlist.
Does anyone have any idea based on this. Please feel free to ask if you have any questions.
Thanks,
Puneet
The JList documentation has an example that loads icons into the JList. You should be able to use this to insert your small pictures into the JList.
http://docs.oracle.com/javase/6/docs/api/javax/swing/JList.html
Here is the relevant code from that link:
// Display an icon and a string for each object in the list.
class MyCellRenderer extends JLabel implements ListCellRenderer {
final static ImageIcon longIcon = new ImageIcon("long.gif");
final static ImageIcon shortIcon = new ImageIcon("short.gif");
// This is the only method defined by ListCellRenderer.
// We just reconfigure the JLabel each time we're called.
public Component getListCellRendererComponent(
JList list, // the list
Object value, // value to display
int index, // cell index
boolean isSelected, // is the cell selected
boolean cellHasFocus) // does the cell have focus
{
String s = value.toString();
setText(s);
setIcon((s.length() > 10) ? longIcon : shortIcon);
if (isSelected) {
setBackground(list.getSelectionBackground());
setForeground(list.getSelectionForeground());
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
setEnabled(list.isEnabled());
setFont(list.getFont());
setOpaque(true);
return this;
}
}
myList.setCellRenderer(new MyCellRenderer());
Assuming your JList contains usernames, you could put your usernames in a HashMap
setIcon(userHashMap.get(s));
If your JLIst is actually store other parts than just the username (dynamic components such as status, group name, etc.) you may need to parse out the username from the String passed into the value object.

Categories