Okay, so what I have is a GUI with a table set up, (mostly, there are still a few problems) but what I need to know is this. Will the following code:
public void removeSelectedRows(JTable table)
{
DefaultTableModel model = (DefaultTableModel) table.getModel();
int[] rows = table.getSelectedRows();
for (int x=0;x<rows.length;++x){
model.removeRow(rows[x]-x);
}
for (int x=0;x<animals;++x)
{
if (Pets[x][0].equalsIgnoreCase(table.getValueAt(table.getSelectedRow(),0)+""))
{
Pets[x][0]=null;
Pets[x][1]=null;
Pets[x][2]=null;
Pets[x][3]=null;
Pets[x][4]=null;
}
}
animals=animals-1;
}
Delete the row that the user has selected? (By that I mean simply clicking on the row and then clicking the delete button). I don't want the row itself to be deleted but I want the values contained within the row the user has selected to be deleted.
Get ready to get you hands dirty.
At some point, DefaultTableModel will no longer meet your needs and you should be prepared to roll your own implementation.
First things first. You're working in an Object Orientated language, you should take advantage of this fact and represent your data as objects.
Secondly, when deleting multiple values from a table, it becomes really tricky quickly. Once you remove the first row, the indices no longer match, you need some way to map the values back to the index that they appear within the model.
Thirdly, the visible row indices may not be the same as those of the model, this is especially true when the table is sorted.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
public class TestJTable {
public static void main(String[] args) {
new TestJTable();
}
public TestJTable() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
List<Pet> pets = new ArrayList<>(25);
pets.add(new Pet("Tyrannosauridae", "TYRANNOSAURUS", 20, 35));
pets.add(new Pet("Dromaeosauridae", "VELOCIRAPTOR", 45, 90));
pets.add(new Pet("Ceratopsidae", "TRICERATOPS", 15, 30));
pets.add(new Pet("Stegosauridae", "STEGOSAURUS", 22, 25));
pets.add(new Pet("Titanosauridae", "MALAWISAURUS", 22, 25));
pets.add(new Pet("Compsognathidae", "COMPSOGNATHUS", 8, 25));
pets.add(new Pet("Brachiosauridae", "BRACHIOSAURUS", 8, 25));
pets.add(new Pet("Diplodocidae", "DIPLODOCUS", 8, 25));
final PetTableModel model = new PetTableModel(pets);
final JTable table = new JTable(model);
InputMap im = table.getInputMap(JTable.WHEN_FOCUSED);
ActionMap am = table.getActionMap();
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DELETE, 0), "delete");
am.put("delete", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
int[] indices= table.getSelectedRows();
// Convert the view's row indices to the models...
int[] mapped = new int[indices.length];
for (int index = 0; index < indices.length; index++) {
mapped[index] = table.convertRowIndexToModel(indices[index]);
}
model.removePets(mapped);
}
});
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PetTableModel extends AbstractTableModel {
private List<Pet> pets;
public PetTableModel() {
pets = new ArrayList<>(25);
}
public PetTableModel(List<Pet> pets) {
this.pets = pets;
}
#Override
public int getRowCount() {
return pets.size();
}
public void removePets(int... indicies) {
// Build a tempory list of Pet objects based
// on the supplied indices...
List<Pet> old = new ArrayList<>(indicies.length);
for (int index : indicies) {
old.add(pets.get(index));
}
// For each pet, get it's index in the model
// remove it from the model
// notify any listeners of the change to the model...
for (Pet pet : old) {
int index = pets.indexOf(pet);
pets.remove(pet);
fireTableRowsDeleted(index, index);
}
}
#Override
public Class<?> getColumnClass(int columnIndex) {
Class clazz = String.class;
switch (columnIndex) {
case 2:
case 3:
clazz = Float.class;
}
return clazz;
}
#Override
public String getColumnName(int column) {
String name = "??";
switch (column) {
case 0:
name = "Breed";
break;
case 1:
name = "Category";
break;
case 2:
name = "Buy Price";
break;
case 3:
name = "Sell Price";
break;
}
return name;
}
#Override
public int getColumnCount() {
return 4;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Pet pet = pets.get(rowIndex);
Object value = null;
switch (columnIndex) {
case 0:
value = pet.getBreed();
break;
case 1:
value = pet.getCategory();
break;
case 2:
value = pet.getBuyPrice();
break;
case 3:
value = pet.getSellPrice();
break;
}
return value;
}
public void add(Pet pet) {
pets.add(pet);
fireTableRowsInserted(getRowCount() - 1, getRowCount() - 1);
}
}
public class Pet {
private String breed;
private String category;
private float buyPrice;
private float sellPrice;
public Pet(String breed, String category, float buyPrice, float sellPrice) {
this.breed = breed;
this.category = category;
this.buyPrice = buyPrice;
this.sellPrice = sellPrice;
}
public String getBreed() {
return breed;
}
public float getBuyPrice() {
return buyPrice;
}
public String getCategory() {
return category;
}
public float getSellPrice() {
return sellPrice;
}
}
}
Related
I have created a JTable with some information about formula 1 racing car drivers. The below code is for the JTable
import java.awt.LayoutManager;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
import java.awt.event.*;
public class Test1 extends JFrame implements ActionListener {
JButton button;
Test1() {
//setBounds(100, 100, 500, 400);
JFrame frame = new JFrame();
button = new JButton();
//button.setBounds(50, 50, 20, 10);s
button.setText("Random Race");
button.addActionListener(this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTable table = new JTable(new Model1Data());
frame.setBounds(100, 100, 500, 400);
JPanel panel = new JPanel();
frame.add(panel);
panel.add(new JScrollPane(table));
panel.add(button);
//add(new J
frame.setVisible(true);
//this.add(button);
pack();
}
#Override
public void actionPerformed(ActionEvent e) {
Formula1ChampionsManager d= new Formula1ChampionsManager();
d.button();
}
}
This is the code for the Model1Data. This is the code for the table to update its cells.
import javax.swing.table.AbstractTableModel;
public class Model1Data extends AbstractTableModel implements ChampionsManager {
String colNames[] = { "Name", "Team", "No of first Places", "Total Points" };
Class<?> colClasses[] = { String.class, String.class, Integer.class, Integer.class };
public int getRowCount() {
return myDrivers.size();
}
public int getColumnCount() {
return colNames.length;
}
public Object getValueAt(int rowIndex, int columnIndex) {
if (columnIndex == 0) {
return myDrivers.get(rowIndex).getName();
}
if (columnIndex == 1) {
return myDrivers.get(rowIndex).getTeam();
}
if (columnIndex == 2) {
return myDrivers.get(rowIndex).getfirstPlace();
}
if (columnIndex == 3) {
return myDrivers.get(rowIndex).totalPoints();
}
return null;
}
public String getColumnName(int columnIndex) {
return colNames[columnIndex];
}
public Class<?> getColumnClass(int columnIndex) {
return colClasses[columnIndex];
}
public boolean isCellEditable(int rowIndex, int columnIndex) {
return false;
}
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
if (columnIndex == 0) {
myDrivers.get(rowIndex).setName((String) aValue);
}
if (columnIndex == 1) {
myDrivers.get(rowIndex).setTeam((String) aValue);
}
if (columnIndex == 2) {
myDrivers.get(rowIndex).setfirstPlace((Integer) aValue);
}
if (columnIndex == 3) {
myDrivers.get(rowIndex).settotalPoints((Integer) aValue);
}
//fireTableCellUpdated(rowIndex, columnIndex);
}
}
This what the table GUI looks like Table GUI. When I click the button the values for No of first places and Total Points are changed randomly. But the table doesn't get updated with the values. If I pull on the side of the frame it gets updated. How do I get it to update when I click the button?
The JTable listens for changes in the TableModel. Hence class AbstractTableModel has methods such as fireTableCellUpdated(rowIndex, columnIndex) (which you commented out in your code - why?). If you override method setValueAt then you need to call that method, otherwise the JTable will not update when its data is changed. I'm guessing that changing the size of the JFrame causes a repaint of the JTable which causes the JTable to reload the data from its model (but I could be wrong).
Your class Model1Data should extend DefaultTableModel, rather than AbstractTableModel, since that class already contains all the functionality you require. The only method that class Model1Data needs to override is getColumnClass (which it already does). You also need to override at least one constructor. Since you didn't post a minimal, reproducible example, I'm guessing that this constructor may be appropriate. Hence the code for class Model1Data should be:
public class Model1Data extends javax.swing.table.DefaultTableModel {
Class<?> colClasses[] = { String.class, String.class, Integer.class, Integer.class };
public Model1Data(Object[] columnNames, int rowCount) {
super(columnNames, rowCount);
}
public Class<?> getColumnClass(int columnIndex) {
return colClasses[columnIndex];
}
}
And in class Test1 (which does not need to extend JFrame, by the way) you can create the JTable like so:
JTable table = new JTable(new Model1Data(new String[]{ "Name", "Team", "No of first Places", "Total Points" }, 0));
Edit
Here's my take on what you are trying to do. The below code can be copied as is, compiled and run. Just click on the Random Race button and the data in the JTable will update.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
public class Test1 {
private JTable table;
private void buildAndDisplayGui() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createTable(), BorderLayout.CENTER);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JButton createButton(String text, int mnemonic, ActionListener listener) {
JButton button = new JButton(text);
button.setMnemonic(mnemonic);
button.addActionListener(listener);
return button;
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
buttonsPanel.add(createButton("Random Race", KeyEvent.VK_R, this::randomRace));
return buttonsPanel;
}
private JScrollPane createTable() {
table = new JTable(new Model1Data());
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
JScrollPane scrollPane = new JScrollPane(table);
return scrollPane;
}
/**
* Simulates a race and randomly determines placings of the drivers and assigns appropriate
* points scores.
*
* #param event - encapsulates button press event
*/
private void randomRace(ActionEvent event) {
Formula1ChampionsManager.simulate();
TableModel tm = table.getModel();
if (tm instanceof Model1Data) {
Model1Data model = (Model1Data) tm;
model.setRowCount(0);
model.init();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
new Test1().buildAndDisplayGui();
});
}
}
class Model1Data extends DefaultTableModel {
private static final String[] COLUMN_NAMES = {"Name", "Team", "No of first Places", "Total Points"};
public Model1Data() {
super(COLUMN_NAMES, 0);
init();
}
public void addDriver(F1Driver driver) {
addRow(new Object[]{driver.getName(),
driver.getTeam(),
driver.getFirstPlace(),
driver.getTotalPoints()});
}
public void init() {
Formula1ChampionsManager.getDrivers()
.stream()
.forEach(this::addDriver);
}
}
class Formula1ChampionsManager {
private static List<F1Driver> drivers;
static {
drivers = List.of(new F1Driver("Lewis Hamilton", F1Teams.MERCEDES),
new F1Driver("Max Verstappen",F1Teams.RED_BULL),
new F1Driver("Charles Leclerc",F1Teams.FERRARI),
new F1Driver("Lando Norris",F1Teams.MCLAREN),
new F1Driver("Fernando Alonso",F1Teams.ALPINE),
new F1Driver("Pierre Gasly",F1Teams.ALPHATAURI),
new F1Driver("Sebastian Vettel",F1Teams.ASTON_MARTIN),
new F1Driver("Nicholas Latifi",F1Teams.WILLIAMS),
new F1Driver("Valtteri Bottas",F1Teams.ALFA_ROMEO),
new F1Driver("Mick Schumacher",F1Teams.HAAS));
}
public static List<F1Driver> getDrivers() {
return drivers;
}
public static void simulate() {
List<F1Driver> result = new ArrayList<>(drivers);
Collections.shuffle(result);
F1Driver first = result.get(0);
first.update(F1Driver.FIRST);
F1Driver second = result.get(1);
second.update(F1Driver.SECOND);
F1Driver third = result.get(2);
third.update(F1Driver.THIRD);
F1Driver fourth = result.get(3);
fourth.update(F1Driver.FOURTH);
F1Driver fifth = result.get(4);
fifth.update(F1Driver.FIFTH);
F1Driver sixth = result.get(5);
sixth.update(F1Driver.SIXTH);
F1Driver seventh = result.get(6);
seventh.update(F1Driver.SEVENTH);
F1Driver eighth = result.get(7);
eighth.update(F1Driver.EIGHTH);
F1Driver ninth = result.get(8);
ninth.update(F1Driver.NINTH);
F1Driver tenth = result.get(9);
tenth.update(F1Driver.TENTH);
}
}
enum F1Teams {
MERCEDES("Mercedes"),
RED_BULL("Red Bull"),
FERRARI("Ferrari"),
MCLAREN("McLaren"),
ALPINE("Alpine"),
ALPHATAURI("AlphaTauri"),
ASTON_MARTIN("Aston Martin"),
WILLIAMS("Williams"),
ALFA_ROMEO("Alfa Romeo"),
HAAS("Haas");
private String name;
private F1Teams(String name) {
this.name = name;
}
#Override // java.lang.Enum
public String toString() {
return name;
}
}
class F1Driver {
public static final int FIRST = 25;
public static final int SECOND = 18;
public static final int THIRD = 15;
public static final int FOURTH = 12;
public static final int FIFTH = 10;
public static final int SIXTH = 8;
public static final int SEVENTH = 6;
public static final int EIGHTH = 4;
public static final int NINTH = 2;
public static final int TENTH = 1;
private String name;
private F1Teams team;
private int firstPlace;
private int totalPoints;
public F1Driver(String name, F1Teams team) {
this(name, team, 0, 0);
}
public F1Driver(String name, F1Teams team, int firstPlace, int totalPoints) {
Objects.requireNonNull(name, "Missing name.");
this.name = name;
this.team = team;
this.firstPlace = firstPlace;
this.totalPoints = totalPoints;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public F1Teams getTeam() {
return team;
}
public void setTeam(F1Teams team) {
this.team = team;
}
public int getFirstPlace() {
return firstPlace;
}
public void setFirstPlace(int firstPlace) {
this.firstPlace = firstPlace;
}
public int getTotalPoints() {
return totalPoints;
}
public void setTotalPoints(int totalPoints) {
this.totalPoints = totalPoints;
}
#Override // java.lang.Object
public boolean equals(Object obj) {
boolean equal = this == obj;
if (!equal) {
if (obj instanceof F1Driver) {
F1Driver other = (F1Driver) obj;
equal = name != null &&
name.equals(other.name) &&
team == other.team;
}
}
return equal;
}
#Override // java.lang.Object
public int hashCode() {
return (String.valueOf(name) + String.valueOf(team)).hashCode();
}
#Override // java.lang.Object
public String toString() {
return String.format("%s [%s] %d [%d]", name, team, totalPoints, firstPlace);
}
public void update(int points) {
totalPoints += points;
if (points == FIRST) {
firstPlace++;
}
}
}
Here's how it looks when I run the above code.
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 1 year ago.
Improve this question
Can anyone help me?
I try to use key released for searching in the table with check box in java swing, but when I type a character the check box disappears
I use this code for the key released:
Jtable.setModel(new DefaultTableModel(null, new Object[]{"Name","code","section","credit","type","hours","semestre","null"}));
How can I keep the check box when I search for a value in this table?????
Don't use a KeyListener on a text component, it's just an inappropriate usage.
Instead, use DocumentListener on the underlying Document, for example...
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.AbstractTableModel;
import javax.swing.text.BadLocationException;
import javax.swing.text.Document;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
private JTable table;
private JTextField searchField;
private SubjectTableModel tableModel;
public TestPane() {
setLayout(new BorderLayout());
JPanel searchPanel = new JPanel(new GridBagLayout());
searchField = new JTextField(20);
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.insets = new Insets(8, 8, 8, 8);
searchPanel.add(new JLabel("Enter Value to Search:"), gbc);
gbc.fill = gbc.HORIZONTAL;
gbc.weightx = 1;
gbc.gridx++;
searchPanel.add(searchField, gbc);
add(searchPanel, BorderLayout.NORTH);
List<SubjectTabelItem> items = new ArrayList<>(25);
items.add(new SubjectTabelItem("Chemistry", "CH3305", "English", 6, "Mandatory", 20, "S3"));
items.add(new SubjectTabelItem("Data", "I2206", "French", 5, "Mandatory", 50, "S4"));
items.add(new SubjectTabelItem("Java", "I2211", "French", 5, "Mandatory", 45, "S4"));
items.add(new SubjectTabelItem("Theorie", "Th3306", "French", 6, "Optional", 20, "S3"));
items.add(new SubjectTabelItem("Arabic", "A1265", "French", 3, "Optional", 20, "S4"));
items.add(new SubjectTabelItem("Html", "H102", "English", 4, "Mandatory", 12, "S1"));
items.add(new SubjectTabelItem("Php", "PI3301", "English", 3, "Mandatory", 25, "S3"));
items.add(new SubjectTabelItem("Assembly", "I3564", "English", 3, "Optional", 20, "S5"));
items.add(new SubjectTabelItem("Go", "I1234", "English", 3, "Mandatory", 20, "S5"));
tableModel = new SubjectTableModel(items);
table = new JTable(tableModel);
add(new JScrollPane(table));
searchField.getDocument().addDocumentListener(new DocumentListener() {
protected void process(DocumentEvent e) {
try {
Document document = e.getDocument();
String text = document.getText(0, document.getLength());
find(text);
} catch (BadLocationException ex) {
ex.printStackTrace();
}
}
#Override
public void insertUpdate(DocumentEvent e) {
process(e);
}
#Override
public void removeUpdate(DocumentEvent e) {
process(e);
}
#Override
public void changedUpdate(DocumentEvent e) {
process(e);
}
});
}
protected void find(String text) {
tableModel.highlight(text);
}
}
public class Stubject {
private String name;
private String code;
private String section;
private int credit;
private String type;
private int hours;
private String semestre;
public Stubject(String name, String code, String section, int credit, String type, int hours, String semestre) {
this.name = name;
this.code = code;
this.section = section;
this.credit = credit;
this.type = type;
this.hours = hours;
this.semestre = semestre;
}
public String getName() {
return name;
}
public String getCode() {
return code;
}
public String getSection() {
return section;
}
public int getCredit() {
return credit;
}
public String getType() {
return type;
}
public int getHours() {
return hours;
}
public String getSemestre() {
return semestre;
}
}
public class SubjectTabelItem extends Stubject {
private boolean selected = false;
public SubjectTabelItem(String name, String code, String section, int credit, String type, int hours, String semestre) {
super(name, code, section, credit, type, hours, semestre);
}
public boolean isSelected() {
return selected;
}
public void setSelected(boolean isSelected) {
this.selected = isSelected;
}
}
public class SubjectTableModel extends AbstractTableModel {
private List<SubjectTabelItem> items;
private String[] columnNames = new String[]{
"Name",
"Code",
"Section",
"Credit",
"Type",
"Hours",
"Semestre",
""
};
public SubjectTableModel(List<SubjectTabelItem> items) {
this.items = items;
}
public void setItems(List<SubjectTabelItem> items) {
this.items = items;
}
#Override
public int getRowCount() {
return items.size();
}
#Override
public int getColumnCount() {
return columnNames.length;
}
#Override
public String getColumnName(int column) {
return columnNames[column];
}
#Override
public Class<?> getColumnClass(int columnIndex) {
switch (columnIndex) {
case 0:
case 1:
case 2:
case 4:
case 6:
return String.class;
case 3:
case 5:
return Integer.class;
case 7:
return Boolean.class;
}
return String.class;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
SubjectTabelItem item = items.get(rowIndex);
switch (columnIndex) {
case 0:
return item.getName();
case 1:
return item.getCode();
case 2:
return item.getSection();
case 3:
return item.getCredit();
case 4:
return item.getType();
case 5:
return item.getHours();
case 6:
return item.getSemestre();
case 7:
return item.isSelected();
}
return null;
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
SubjectTabelItem item = items.get(rowIndex);
switch (columnIndex) {
case 7:
if (aValue instanceof Boolean) {
boolean selected = (boolean) aValue;
if (selected != item.isSelected()) {
item.setSelected(selected);
fireTableCellUpdated(rowIndex, columnIndex);
}
}
break;
}
}
public void highlight(String text) {
String lowerCased = text.toLowerCase();
for (int row = 0; row < getRowCount(); row++) {
SubjectTabelItem item = items.get(row);
if (item.getName().toLowerCase().contains(lowerCased)) {
setValueAt(true, row, 7);
} else {
setValueAt(false, row, 7);
}
}
}
}
}
See How to Write a Document Listener for more details
I want to sort a JTable like this:
That is: (EDIT)
If I click the "Name" column to sort, "Tom", "Polazzo" and "Anna" must be sorted alphabetically, and rows with the same names must stay together("grouped" by the name), and each name should be shown only once, the rest cells must be blank.
If I click the "Duration" or "Book #" column, I want all rows sorted ascending/descending by values of duration/book number, but same as in point 1), rows with the same "Name" must stay together, that is, stay grouped, and only the first row in every group is shown, and the rest "Name" stay blank.
The data in the table model's vector are collected from parsing a XML file. The rows with same "Name" are under the same node in the hierarchy tree.
I think there're two ways to do this:
a) When collecting the data and construct the rows, under the same "Name" node, give the cell at column 0 the "Name" value, and leave the rest of rows "" in the same column. But, I don't know how to construct the comparator of column "Name", to ensure the first row always being the top in sorting. (It cannot be the biggest and the smallest when we override compare() method, can it?)
b) Every time we click the table header to sort, make the renderer repaint the table the way we want: comparing the value in the first line of each group, and if it's the same as the last line's column 0's value, don't paint this cell, until we reach another different value. In that way, we don't mess with comparators/sorters, and it turns into a renderer problem. That's what I kind of achieved in the SSCCE below, but I am half way there and I need some tips.
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.List;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
import java.util.Comparator;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.RowSorter;
import javax.swing.RowSorter.SortKey;
import javax.swing.event.RowSorterEvent;
import javax.swing.event.RowSorterListener;
import javax.swing.SortOrder;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
import com.WindThunderStudio.JHeaderToolTip.JHeaderToolTip;
import net.miginfocom.swing.MigLayout;
public class RowGroupInTable extends JFrame {
public RowGroupInTable() {
begin();
}
private void begin() {
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new BorderLayout());
DefaultTableModel model = new DefaultTableModel();
Object[][] data = new Object[][] {{"Tom", "17", "Book1"},
{"Tom", 23, "Book2"},
{"Tom", 25, "Book3"},
{"Polazzo", 41, "Book1"},
{"Polazzo", 45, "Book2"},
{"Polazzo", 12, "Book3"},
{"Anna", 1, "Book3"},
{"Anna", 33, "Book5"}};
String[] titles = new String[] {"Name", "Last job duration", "Book #"};
JTable table = new JTable(data, titles);
table.setFillsViewportHeight(true);
table.setAutoCreateRowSorter(false);
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
ArrayList<SortKey> sortKeys = new ArrayList<RowSorter.SortKey>();
sortKeys.add(new SortKey(2, SortOrder.ASCENDING));
sortKeys.add(new SortKey(1, SortOrder.ASCENDING));
// sorter.setSortKeys(sortKeys);
sorter.setSortable(0, true);
sorter.setSortable(1, false);
sorter.setSortable(2, true);
table.setRowSorter(sorter);
table.setDefaultRenderer(Object.class, new MyRenderer(table.getDefaultRenderer(Object.class)));
JTableHeader header = table.getTableHeader();
header.addMouseListener(new MouseListener() {
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseClicked(MouseEvent e) {
int col = ((JTableHeader)(e.getComponent())).getColumnModel().getColumnIndexAtX(e.getX());
}
});
JScrollPane sp = new JScrollPane(table);
sp.setBounds(0, 0, 200, 200);
add(sp, BorderLayout.CENTER);
pack();
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
RowGroupInTable frame = new RowGroupInTable();
}
});
}
private class MyRenderer implements TableCellRenderer {
TableCellRenderer def;
public MyRenderer() {
// TODO Auto-generated constructor stub
}
public MyRenderer(TableCellRenderer defaultRend) {
this();
this.def = defaultRend;
}
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
int rowCount = table.getModel().getRowCount();
Component orig = (def).getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (column == 0) {
if (row == 0) {
return orig;
} else if (row > 0 && row < rowCount) {
if (table.getModel().getValueAt(row-1, column).equals(value)) {
return new JLabel("");
} else {
return orig;
}
}
}
return orig;
}
}
}
each name should be shown only once, the rest cells must be blank.
If I understand your requirement you might be able to use table.getValueAt(...) instead of table.getModel().getValueAt(...):
import java.awt.*;
import java.util.*;
import java.util.List;
import javax.swing.*;
import javax.swing.table.*;
public class RowGroupInTableTest {
private JComponent makeUI() {
String[] titles = new String[] {"Name", "Last job duration", "Book #"};
DefaultTableModel model = new DefaultTableModel(null, titles) {
#Override public Class<?> getColumnClass(int column) {
return MyData.class;
}
};
addMyData(model, new MyData("Tom", 17, "Book1"));
addMyData(model, new MyData("Tom", 23, "Book2"));
addMyData(model, new MyData("Tom", 25, "Book3"));
addMyData(model, new MyData("Polazzo", 41, "Book1"));
addMyData(model, new MyData("Polazzo", 45, "Book2"));
addMyData(model, new MyData("Polazzo", 12, "Book3"));
addMyData(model, new MyData("Anna", 1, "Book3"));
addMyData(model, new MyData("Anna", 33, "Book5"));
JTable table = new JTable(model);
table.setFillsViewportHeight(true);
table.setDefaultRenderer(MyData.class, new MyRenderer());
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
Comparator<MyData> c = Comparator.comparing(MyData::getName);
sorter.setComparator(0, c);
sorter.setComparator(1, c.thenComparing(Comparator.comparingInt(MyData::getDuration)));
sorter.setComparator(2, c.thenComparing(Comparator.comparing(MyData::getBook)));
table.setRowSorter(sorter);
return new JScrollPane(table);
}
private static void addMyData(DefaultTableModel model, MyData data) {
//Omission work...
model.addRow(Collections.nCopies(3, data).toArray());
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.getContentPane().add(new RowGroupInTableTest().makeUI());
f.setSize(320, 240);
f.setLocationRelativeTo(null);
f.setVisible(true);
});
}
}
class MyData {
private final String name;
private final int duration;
private final String book;
protected MyData(String name, int duration, String book) {
this.name = name;
this.duration = duration;
this.book = book;
}
public String getName() {
return name;
}
public int getDuration() {
return duration;
}
public String getBook() {
return book;
}
}
class MyRenderer implements TableCellRenderer {
TableCellRenderer def = new DefaultTableCellRenderer();
#Override public Component getTableCellRendererComponent(
JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
JLabel orig = (JLabel) def.getTableCellRendererComponent(
table, value, isSelected, hasFocus, row, column);
orig.setHorizontalAlignment(SwingConstants.LEFT);
MyData data = (MyData) value;
switch (table.convertColumnIndexToModel(column)) {
case 0:
String str = data.getName();
if (row > 0) {
//if (table.getModel().getValueAt(row-1, column).equals(value)) {
//Since it compares with the value of the previous line on the display,
//table.getModel() is not needed
MyData prev = (MyData) table.getValueAt(row - 1, column);
if (Objects.equals(prev.getName(), str)) {
str = " ";
}
}
orig.setText(str);
break;
case 1:
orig.setHorizontalAlignment(SwingConstants.RIGHT);
orig.setText("" + data.getDuration());
break;
case 2:
orig.setText(data.getBook());
break;
default:
break;
}
return orig;
}
}
edit
Now if I only use Java 7, is there some "old" way to do this? Just setting the comparators in the Java 7 way?
You would need to impliment Comparator:
TableRowSorter<TableModel> sorter = new TableRowSorter<TableModel>(table.getModel());
//Comparator<MyData> c = Comparator.comparing(MyData::getName);
//sorter.setComparator(0, c);
//sorter.setComparator(1, c.thenComparing(Comparator.comparingInt(MyData::getDuration)));
//sorter.setComparator(2, c.thenComparing(Comparator.comparing(MyData::getBook)));
sorter.setComparator(0, new MyDataGroupComparator(0));
sorter.setComparator(1, new MyDataGroupComparator(1));
sorter.setComparator(2, new MyDataGroupComparator(2));
table.setRowSorter(sorter);
class MyDataGroupComparator implements Comparator<MyData> {
private final int column;
protected MyDataGroupComparator(int column) {
this.column = column;
}
#Override public int compare(MyData a, MyData b) {
if (a == null && b == null) {
return 0;
} else if (a != null && b == null) {
return -1;
} else if (a == null && b != null) {
return 1;
} else {
int v = a.getName().compareTo(b.getName());
if (v == 0) {
switch (column) {
case 2:
return a.getBook().compareTo(b.getBook());
case 1:
return a.getDuration() - b.getDuration();
case 0:
default:
return v;
}
}
return v;
}
}
}
when I change table.getModel().getValueAt() to table.getValueAt() I cannot get my original example to work. Why?
Works fine for me(only the cell under Anna is blank):
I am trying to get the following text file to come up into this 'Jtable'.. I’m having a hard time getting all the data/columns to appear.. I know that it’s something easy I’m just new to java so any help would be appreciated.
My text file:
1;2;3;4;5;6
1b;2b;3b;4b;5b;6b
1C;2C;3C;4C;5C;6C
My code so far:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.table.AbstractTableModel;
public class JtableTest {
public static void main(String[] args) throws Exception{
Runnable r = new Runnable() {
public void run() {
new JtableTest ().createUI();
}
};
EventQueue.invokeLater(r);
}
private void createUI() {
try {
JFrame frame = new JFrame();
frame.setLayout(new BorderLayout());
JTable table = new JTable();
String readLine = null;
// DatabaseTableModel tableModel = new DatabaseTableModel();
File file = new File ("JtableTest.txt");
FileReader reader = new FileReader(file);
BufferedReader bufReader = new BufferedReader(reader);//Need to close this
List<Dentry> studentList = new ArrayList<Dentry>();
while ((readLine = bufReader.readLine()) != null) {
String[] splitData = readLine.split(";");
Dentry dentry = new Dentry();
dentry.setName(splitData[0]);
dentry.setNumbers(Arrays.copyOfRange(splitData, 1, splitData.length));
studentList.add(dentry);
}
DatabaseTableModel tableModel = new DatabaseTableModel();
tableModel.setList(studentList);////////////
table.setModel(tableModel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(table));
frame.setTitle("File to JTable");
frame.pack();
frame.setVisible(true);
} catch(IOException ex) {}
}
class Dentry {
private String name;
private String[] number;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber(int index) {
String value = null;
if (index >= 0 && index < number.length) {
value = number[index];
}
return value;
}
public void setNumbers(String... number) {
this.number = number;
class DatabaseTableModel extends AbstractTableModel {
private List<Dentry> list = new ArrayList<Dentry>();
private String[] columnNames = {"1", "2", "3", "4", "5", "6"};
public void setList(List<Dentry> list) {
this.list = list;
fireTableDataChanged();
}
#Override
public String getColumnName(int column) {
return columnNames[column];
}
public int getRowCount() {
return list.size();
}
public int getColumnCount() {
return columnNames.length;
}
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case 0:
return list.get(rowIndex).getName();
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
return list.get(rowIndex).getNumber(columnIndex);
default:
return null;
}
}
}
}
}
}
Your data contains 6 columns, but your pojo only allows for 2 (the name and a number).
Start by changing your pojo to support multiple columns
class Dentry {
private String name;
private String[] number;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNumber(int index) {
String value = null;
if (index >= 0 && index < number.length) {
value = number[index];
}
return value;
}
public void setNumbers(String... number) {
this.number = number;
}
}
All I've done here is allowed your to apply a variable length of numbers and provided a convenience method to access them.
Next, you need to change the way you're loading the data so you can supply all the other elements from the line in the file to your pojo
List<Dentry> studentList = new ArrayList<Dentry>();
while ((readLine = bufReader.readLine()) != null) {
String[] splitData = readLine.split(";");
Dentry dentry = new Dentry();
dentry.setName(splitData[0]);
dentry.setNumbers(Arrays.copyOfRange(splitData, 1, splitData.length));
studentList.add(dentry);
}
This just creates a copy of the splitData from the second element (index 1) to the last element inclusive.
I also changed the getVaueAt method to make it simpler to get the number for the specified column...
public Object getValueAt(int rowIndex, int columnIndex) {
switch (columnIndex) {
case 0:
return list.get(rowIndex).getName();
case 1:
case 2:
case 3:
case 4:
case 5:
return list.get(rowIndex).getNumber(columnIndex - 1);
default:
return null;
}
}
You could just use default as the pojo checks the range of the index, but this is a nice demonstration of the fall through nature of a switch statement.
My problem is:
I have a button that will add components to a JList when it is clicked. Each row of the list is composed by two jtextFields. The first is the ID of the row. Every time I press the "Add button", a new row appears, the ID is incremented, and the person who is using the application will write whatever he want on the second jTextField (forward the ID field).
Then it will have a scroll pane for when there are more than 4 rows.
And I want a Remove Button too. To be possible to remove some rows.
Can you help me with this? I don't know how to create a list like this...
Thanks!
Simply use JTable with two columns. This will allow you to establish two columns, one for the ID and one for the value. This will allow you make the second column editable.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
public class TestTable04 {
public static void main(String[] args) {
new TestTable04();
}
private int id = 0;
public TestTable04() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
final RecordTableModel model = new RecordTableModel();
JTable table = new JTable(model);
JButton add = new JButton("Add");
add.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
model.add(new Record(++id));
}
});
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(add, BorderLayout.SOUTH);
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class Record {
private int id;
private String value;
public Record(int id) {
this.id = id;
}
public int getID() {
return id;
}
public void setValue(String value) {
this.value = value;
}
public String getValue() {
return value;
}
}
public class RecordTableModel extends AbstractTableModel {
private List<Record> lstRecords;
public RecordTableModel() {
lstRecords = new ArrayList<>(24);
}
public void add(Record record) {
lstRecords.add(record);
fireTableRowsInserted(lstRecords.size() - 1, lstRecords.size() - 1);
}
public void remove(Record record) {
if (lstRecords.contains(record)) {
int index = lstRecords.indexOf(record);
remove(index);
}
}
public void remove(int index) {
lstRecords.remove(index);
fireTableRowsDeleted(index, index);
}
#Override
public int getRowCount() {
return lstRecords.size();
}
#Override
public int getColumnCount() {
return 2;
}
#Override
public Class<?> getColumnClass(int columnIndex) {
Class clazz = String.class;
switch (columnIndex) {
case 0:
clazz = Integer.class;
break;
}
return clazz;
}
#Override
public String getColumnName(int column) {
String name = null;
switch (column) {
case 0:
name = "ID";
break;
case 1:
name = "Value";
break;
}
return name;
}
#Override
public Object getValueAt(int rowIndex, int columnIndex) {
Record record = lstRecords.get(rowIndex);
Object value = null;
switch (columnIndex) {
case 0:
value = record.getID();
break;
case 1:
value = record.getValue();
break;
}
return value;
}
#Override
public boolean isCellEditable(int rowIndex, int columnIndex) {
return columnIndex == 1;
}
#Override
public void setValueAt(Object aValue, int rowIndex, int columnIndex) {
Record record = lstRecords.get(rowIndex);
switch (columnIndex) {
case 1:
record.setValue(aValue == null ? null : aValue.toString());
fireTableCellUpdated(rowIndex, columnIndex);
break;
}
}
}
}
Check out How to use tables for more details
You can try creating a custom ListCellRenderer and use JList's setRenderer method to assign it. Override the getListCellRenderer() method of ListCellRenderer to return a JPanel containing two components, a JLabel for the id, and a JTextfield for the user input. You may need to keep a reference to the JTextField elsewhere so that you can retrieve the user input.
To implement the add and remove methods, I would recommend backing the JList with a ListModel. Add and remove to the ListModel will also update the JList UI.