Swing - JTable cell highlight with different Colors - java

I have a textbox with search button, if i click on search text, String gets highlighted in JTable cells, if the search text is having multiple Strings with comma separated, all those comma separated Strings has to be displayed in a different colors dynamically.
Can you please suggest what changes i need to do to the below working code(single String highlight)
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.DefaultHighlighter;
import java.awt.*;
public class TableExample {
JFrame f;
private JTextField textField;
TableExample() {
f = new JFrame();
String data[][] = {{"101", "lala", "670000"}, {"101", "lala", "670000"}, {"102", "Jai", "780000"},
{"101", "Sachin", "700000"}};
String column[] = {"ID", "NAME", "SALARY"};
JTable jt = new JTable();
jt.setBounds(30, 40, 200, 300);
JScrollPane sp = new JScrollPane(jt);
DefaultTableModel dtm = new DefaultTableModel(data, column);
dtm.setColumnIdentifiers(column);
jt.setModel(dtm);
JPanel inputpanel = new JPanel();
inputpanel.setLayout(new BoxLayout(inputpanel, BoxLayout.LINE_AXIS));
textField = new JTextField();
JButton button = new JButton("Highlight");
button.addActionListener(e -> {
jt.repaint();
});
inputpanel.add(textField);
inputpanel.add(button);
jt.setDefaultRenderer(Object.class, new CellHighlightRenderer());
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, inputpanel, sp);
f.add(splitPane);
f.setSize(300, 400);
}
class CellHighlightRenderer extends JTextField implements TableCellRenderer {
public DefaultHighlighter high = new DefaultHighlighter();
public DefaultHighlighter.DefaultHighlightPainter highlight_painter = new DefaultHighlighter.DefaultHighlightPainter(
Color.YELLOW);
public CellHighlightRenderer() {
setBorder(BorderFactory.createEmptyBorder());
setHighlighter(high);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
setFont(table.getFont());
setValue(value);
int pos = 0;
String pattern = textField.getText(); // Grab the text.
if (pattern != null && pattern.trim().length() > 0)
while ((pos = value.toString().indexOf(pattern, pos)) >= 0) {
try {
high.addHighlight(pos, pos + pattern.length(), highlight_painter);
pos += pattern.length();
} catch (Exception e) {
e.printStackTrace();
}
}
return this;
}
protected void setValue(Object value) {
setText((value == null) ? "" : value.toString());
}
}
public static void main(String[] args) {
SwingUtilities.invokeLater(()->{
TableExample te = new TableExample();
te.f.setVisible(true);
});
}
}

I have done what i think you asked. Read commends inside the code. Whenever u see a comment, try to compare it with your code. I didnt know what colors you want so i have done with random color to each word.
Preview:
package test;
import java.awt.Color;
import java.awt.Component;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableCellRenderer;
import javax.swing.text.DefaultHighlighter;
public class TableExample {
JFrame f;
private JTextField textField; // make the textfield a field so u can grab its text whenever u need it (in our
// case in renderer)
private HashMap<String, Color> words = new HashMap<>();
TableExample() {
f = new JFrame();
// I changed the data a bit for my tests, take care of that.
String data[][] = { { "101", "lala", "670000" }, { "101", "lala", "670000" }, { "102", "Jai", "780000" },
{ "101", "Sachin", "700000" } };
String column[] = { "ID", "NAME", "SALARY" };
JTable jt = new JTable();
jt.setBounds(30, 40, 200, 300);
JScrollPane sp = new JScrollPane(jt); // You were not adding the sp into your panel, take care that next time.
DefaultTableModel dtm = new DefaultTableModel(data, column); // Create the model with our data.
dtm.setColumnIdentifiers(column);
jt.setModel(dtm);
JPanel inputpanel = new JPanel();
inputpanel.setLayout(new BoxLayout(inputpanel, BoxLayout.LINE_AXIS));
textField = new JTextField();
JButton button = new JButton("Highlight");
button.addActionListener(e -> { // Button should only repaint the table (a.k.a read the renderer again).
words.clear(); //Clear the map with the words
String text = textField.getText(); // Grab the text.
if (text.trim().length() > 0) {
String[] splitPattern = text.split(","); //Split the words
for (String s : splitPattern) {
words.put(s, getARandomColor()); //Put each word with a RANDOM color.
}
}
jt.repaint();// Changing the table's renderer is bad on every button click, and no needed.
});
inputpanel.add(textField);
inputpanel.add(button);
jt.setDefaultRenderer(Object.class, new CellHighlightRenderer()); // Add the renderer only 1 time.
// I add sp here, not the table (if you want your table to actually have a
// scrollpane)
JSplitPane splitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, inputpanel, sp);
f.add(splitPane);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Hopefully you didnt forget this.
f.setSize(300, 400);
f.pack(); // Use pack() to the frame between set it visible.
// f.setVisible(true); //setVisible to constructor is bad idea. I suggest you to
// avoid that, and show your frame after its initiation.
}
class CellHighlightRenderer extends JTextField implements TableCellRenderer {
public DefaultHighlighter high = new DefaultHighlighter();
public DefaultHighlighter.DefaultHighlightPainter highlight_painter = new DefaultHighlighter.DefaultHighlightPainter(
Color.YELLOW);
public CellHighlightRenderer() {
setBorder(BorderFactory.createEmptyBorder());
setHighlighter(high);
}
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
setBackground(isSelected ? table.getSelectionBackground() : table.getBackground());
setForeground(isSelected ? table.getSelectionForeground() : table.getForeground());
setFont(table.getFont());
setValue(value);
int pos = 0;
if (words.size() > 0) {
for (String word : words.keySet()) {
while ((pos = value.toString().indexOf(word, pos)) >= 0) {
try {
highlight_painter = new DefaultHighlighter.DefaultHighlightPainter(words.get(word));
// high.addHighlight(first, last, highlight_painter);
high.addHighlight(pos, pos + word.length(), highlight_painter);
pos += word.length();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
return this;
}
protected void setValue(Object value) {
setText((value == null) ? "" : value.toString());
}
}
private Color getARandomColor() {
return new Color((int) (Math.random() * 255), (int) (Math.random() * 255), (int) (Math.random() * 255));
}
public static void main(String[] args) {
/* *******THIS IS VERY IMPORTANT******* */
/* All swing applications should run in their own thread, named EDT */
SwingUtilities.invokeLater(() -> {
TableExample te = new TableExample();
te.f.setVisible(true); // Here, make it visisble
});
}
}

Related

Multiple column in JComboBox in java swing

how to create jcomboBox with two /multiple columns in the drop down let but when we select only one selected value show in Jcombobox
please give me any solution for this .
You might be able to use JList#setLayoutOrientation(JList.VERTICAL_WRAP):
import java.awt.*;
import javax.accessibility.Accessible;
import javax.swing.*;
import javax.swing.plaf.basic.ComboPopup;
public class TwoColumnsDropdownTest {
private Component makeUI() {
DefaultComboBoxModel<String> model = new DefaultComboBoxModel<>();
model.addElement("111");
model.addElement("2222");
model.addElement("3");
model.addElement("44444");
model.addElement("55555");
model.addElement("66");
model.addElement("777");
model.addElement("8");
model.addElement("9999");
int rowCount = (model.getSize() + 1) / 2;
JComboBox<String> combo = new JComboBox<String>(model) {
#Override public Dimension getPreferredSize() {
Insets i = getInsets();
Dimension d = super.getPreferredSize();
int w = Math.max(100, d.width);
int h = d.height;
int buttonWidth = 20; // ???
return new Dimension(buttonWidth + w + i.left + i.right, h + i.top + i.bottom);
}
#Override public void updateUI() {
super.updateUI();
setMaximumRowCount(rowCount);
setPrototypeDisplayValue("12345");
Accessible o = getAccessibleContext().getAccessibleChild(0);
if (o instanceof ComboPopup) {
JList<?> list = ((ComboPopup) o).getList();
list.setLayoutOrientation(JList.VERTICAL_WRAP);
list.setVisibleRowCount(rowCount);
list.setFixedCellWidth((getPreferredSize().width - 2) / 2);
}
}
};
JPanel p = new JPanel();
p.add(combo);
return p;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.getContentPane().add(new TwoColumnsDropdownTest().makeUI());
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}
You need to do a few things.
By default, what is displayed in the JComboBox, as the selected item, is the value returned by method toString of the items in the ComboBoxModel. Based on your comment I wrote an Item class and overrode the toString method.
In order to display something different in the drop-down list, you need a custom ListCellRenderer.
In order for the drop-down list to display the entire details of each item, the drop-down list needs to be wider than the JComboBox. I used code from the following SO question to achieve that:
How can I change the width of a JComboBox dropdown list?
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Point;
import java.math.BigDecimal;
import java.text.NumberFormat;
import javax.accessibility.Accessible;
import javax.accessibility.AccessibleContext;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.event.PopupMenuEvent;
import javax.swing.event.PopupMenuListener;
import javax.swing.plaf.basic.BasicComboPopup;
public class MultiColumnCombo implements PopupMenuListener {
private JComboBox<Item> combo;
public void popupMenuCanceled(PopupMenuEvent event) {
// Do nothing.
}
public void popupMenuWillBecomeInvisible(PopupMenuEvent event) {
// Do nothing.
}
public void popupMenuWillBecomeVisible(PopupMenuEvent event) {
AccessibleContext comboAccessibleContext = combo.getAccessibleContext();
int comboAccessibleChildrenCount = comboAccessibleContext.getAccessibleChildrenCount();
if (comboAccessibleChildrenCount > 0) {
Accessible comboAccessibleChild0 = comboAccessibleContext.getAccessibleChild(0);
if (comboAccessibleChild0 instanceof BasicComboPopup) {
EventQueue.invokeLater(() -> {
BasicComboPopup comboPopup = (BasicComboPopup) comboAccessibleChild0;
JScrollPane scrollPane = (JScrollPane) comboPopup.getComponent(0);
Dimension d = setCurrentDimension(scrollPane.getPreferredSize());
scrollPane.setPreferredSize(d);
scrollPane.setMaximumSize(d);
scrollPane.setMinimumSize(d);
scrollPane.setSize(d);
Point location = combo.getLocationOnScreen();
int height = combo.getPreferredSize().height;
comboPopup.setLocation(location.x, location.y + height - 1);
comboPopup.setLocation(location.x, location.y + height);
});
}
}
}
private void createAndDisplayGui() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createCombo());
frame.setSize(450, 300);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createCombo() {
JPanel panel = new JPanel();
Item[] items = new Item[]{new Item("A", "Item A", new BigDecimal(1.99d), new BigDecimal(2.99d)),
new Item("B", "Item B", new BigDecimal(7.5d), new BigDecimal(9.0d)),
new Item("C", "Item C", new BigDecimal(0.25d), new BigDecimal(3.15d))};
combo = new JComboBox<>(items);
AccessibleContext comboAccessibleContext = combo.getAccessibleContext();
int comboAccessibleChildrenCount = comboAccessibleContext.getAccessibleChildrenCount();
if (comboAccessibleChildrenCount > 0) {
Accessible comboAccessibleChild0 = comboAccessibleContext.getAccessibleChild(0);
if (comboAccessibleChild0 instanceof BasicComboPopup) {
BasicComboPopup comboPopup = (BasicComboPopup) comboAccessibleChild0;
comboPopup.getList().setCellRenderer(new MultiColumnRenderer());
}
}
combo.addPopupMenuListener(this);
panel.add(combo);
return panel;
}
private Dimension setCurrentDimension(Dimension dim) {
Dimension d = new Dimension(dim);
d.width = 120;
return d;
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> new MultiColumnCombo().createAndDisplayGui());
}
}
class Item {
private String code;
private String name;
private BigDecimal price;
private BigDecimal salePrice;
public Item(String code, String name, BigDecimal price, BigDecimal salePrice) {
this.code = code;
this.name = name;
this.price = price;
this.salePrice = salePrice;
}
public String displayString() {
return String.format("%s %s %s %s",
code,
name,
NumberFormat.getCurrencyInstance().format(price),
NumberFormat.getCurrencyInstance().format(salePrice));
}
public String toString() {
return name;
}
}
class MultiColumnRenderer implements ListCellRenderer<Object> {
/** Component returned by method {#link #getListCellRendererComponent}. */
private JLabel cmpt;
public MultiColumnRenderer() {
cmpt = new JLabel();
cmpt.setOpaque(true);
cmpt.setBorder(BorderFactory.createEmptyBorder(1, 1, 1, 1));
}
public Component getListCellRendererComponent(JList<? extends Object> list,
Object value,
int index,
boolean isSelected,
boolean cellHasFocus) {
String text;
if (value == null) {
text = "";
}
else {
if (value instanceof Item) {
text = ((Item) value).displayString();
}
else {
text = value.toString();
}
}
cmpt.setText(text);
if (isSelected) {
cmpt.setBackground(list.getSelectionBackground());
cmpt.setForeground(list.getSelectionForeground());
}
else {
cmpt.setBackground(list.getBackground());
cmpt.setForeground(list.getForeground());
}
cmpt.setFont(list.getFont());
return cmpt;
}
}

JLabels and TextFields don't appear when declared in separate method/class

I'm having some trouble making JLabels and textFields appear on the JPanel.
If I put all of this code in the main method it works fine, however when I move the JLabels and TextFields into their own methods or classes the JPanel remains empty. The image I use is shown.
package gui;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
#SuppressWarnings("serial")
class ImagePanel extends JPanel {
private Image img;
public ImagePanel(String img) {
this(new ImageIcon(img).getImage());
}
public ImagePanel(Image img) {
this.img = img;
Dimension size = new Dimension(img.getWidth(null), img.getHeight(null));
setPreferredSize(size);
setMinimumSize(size);
setMaximumSize(size);
setSize(size);
setLayout(null);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(img, 0, 0, null);
}
}
public class EquippedInput {
private JPanel panel;
public static void main(String[] args){
ImagePanel panel = new ImagePanel(new ImageIcon("Images/Crusader Background.jpg").getImage());
JScrollPane scrollBar = new JScrollPane(panel,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
panel.setLayout(null);
JFrame frame = new JFrame();
frame.getContentPane().add(scrollBar);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1160, 1000);
frame.setVisible(true);
panel.setPreferredSize(new Dimension(1125, 1210));
scrollBar.getVerticalScrollBar().setUnitIncrement(20);
}
public EquippedInput() {
initialize();
}
private void initialize() {
/**
* Stats and slots labels.
*/
JLabel lblWeaponAttacksPerSecond = new JLabel("Weapon Attacks Per Second");
JLabel lblWeaponAttackSpeed = new JLabel("Attack Speed");
JLabel lblMainHand = new JLabel("Main Hand");
JLabel lblOffHand = new JLabel("Off Hand");
JLabel stats[] = {lblWeaponAttacksPerSecond, lblWeaponAttackSpeed};
JLabel slots[] = {lblMainHand, lblOffHand};
/**
* Place stats and slots labels.
*/
for (int i = 0; i < stats.length; i++) {
stats[i].setBounds(10, (40 + 30 * i), 200, 20);
panel.add(stats[i]);
}
for (int i = 0; i < slots.length; i++) {
slots[i].setBounds((220 + 70 * i), 10, 60, 20);
panel.add(slots[i]);
}
/**
* Text fields.
*/
JTextField textField0 = new JTextField();
JTextField textField1 = new JTextField();
JTextField mainHandTextField[] = {textField0, textField1};
/**
* Arrays of whether a stat can roll on an item. 1 = can roll. 0 = can't roll.
*/
int mainHandAvailableStats[] = {0, 1};
/**
* Text field builders, each loop is a single slot.
*/
for (int i = 0; i < mainHandTextField.length; i++) {
panel.add(mainHandTextField[i]);
if(mainHandAvailableStats[i] == 1) {
int textFieldVerticalPosition = 40 + 30 * i;
mainHandTextField[i].setBounds(220, textFieldVerticalPosition, 60, 20);
panel.add(mainHandTextField[i]);
}
}
}
}
The actual code has a lot more content for the JPanel, but this produces the exact same result.
I am going to have to explain this code and this answer later, but it shows:
Getting data from a text file and creating a table model from it
Creating and displaying a JTable with null cells opaque
Showing a background image in a JPanel
The data file is this:
Main_Hand Off_Hand Head Shoulder Neck
Weapon_Attacks_per_sec 5 null null null null
Attack_Speed 20 null null null null
Damage_% 30 null null null null
Min_Bonus_Damage 3 null null null null
Max_Bonus_Damage 40 null null null null
Min_Weapon_Damage 30 null null null null
Max_Weapon_Damage 80 null null null null
Strength 70 50 20 30 30
It must be named TableFunData.txt and be in the same directory/package as the code is in.
The code is:
import java.awt.BorderLayout;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Scanner;
import java.util.Vector;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableModel;
#SuppressWarnings("serial")
public class TableFun extends JPanel {
private static final String IMG_PATH = "https://upload.wikimedia.org/"
+ "wikipedia/commons/d/d1/Ozanne-Brest.jpg";
private JTable table = new JTable();
private BufferedImage img;
public TableFun(BufferedImage img, TableModel model) {
this.img = img;
table = new JTable(model);
table.setDefaultRenderer(Object.class, new MyCellRenderer());
table.setOpaque(false);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
table.setShowGrid(false);
JScrollPane scrollPane = new JScrollPane(table);
scrollPane.setOpaque(false);
scrollPane.getViewport().setOpaque(false);
setLayout(new BorderLayout());
add(scrollPane, BorderLayout.CENTER);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
#Override
public Dimension getPreferredSize() {
Dimension superSize = super.getPreferredSize();
if (isPreferredSizeSet() || img == null) {
return superSize;
}
int supW = superSize.width;
int supH = superSize.height;
int imgW = img.getWidth();
int imgH = img.getHeight();
int w = Math.max(imgW, supW);
int h = Math.max(imgH, supH);
return new Dimension(w, h);
}
private class MyCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus,
int row, int column) {
DefaultTableCellRenderer renderer = (DefaultTableCellRenderer) super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (column != 0) {
renderer.setHorizontalAlignment(JLabel.CENTER);
} else {
renderer.setHorizontalAlignment(JLabel.LEADING);
}
renderer.setOpaque(value != null);
return renderer;
}
}
private static void createAndShowGui() {
BufferedImage img = null;
try {
URL imageUrl = new URL(IMG_PATH);
img = ImageIO.read(imageUrl);
} catch (IOException e1) {
e1.printStackTrace();
System.exit(-1);
}
String dataPath = "TableFunData.txt";
DataIO dataIO = new DataIO(dataPath);
TableModel model = null;
try {
model = dataIO.createTableModel();
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
JFrame frame = new JFrame("Table Fun");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new TableFun(img, model));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class DataIO {
private static final String NULL = "null";
private String dataPath;
public DataIO(String dataPath) {
this.dataPath = dataPath;
}
public TableModel createTableModel() throws IOException {
InputStream inputStream = getClass().getResourceAsStream(dataPath);
Scanner scanner = new Scanner(inputStream);
Vector<String> columnNames = new Vector<>();
Vector<Vector<Object>> data = new Vector<>();
while (scanner.hasNext()) {
String line = scanner.nextLine();
if (columnNames.size() == 0) {
columnNames = createColumnNames(line);
} else {
Vector<Object> dataRow = createDataRow(line);
data.add(dataRow);
}
}
if (scanner != null) {
scanner.close();
}
DefaultTableModel model = new DefaultTableModel(data, columnNames);
return model;
}
private Vector<String> createColumnNames(String line) {
String[] tokens = line.split("\\s+");
Vector<String> columnNames = new Vector<>();
columnNames.add(" "); // first name is blank
for (String token : tokens) {
if (!token.trim().isEmpty()) {
token = token.replace("_", " "); // add spaces
columnNames.add(token);
}
}
return columnNames;
}
private Vector<Object> createDataRow(String line) {
String[] tokens = line.split("\\s+");
Vector<Object> dataRow = new Vector<>();
for (String token : tokens) {
token = token.trim();
if (!token.isEmpty()) {
// first token is the title
if (dataRow.size() == 0) {
token = token.replace("_", " "); // add spaces
dataRow.add(token);
} else {
// if title already added
if (token.equalsIgnoreCase(NULL)) {
dataRow.add(null);
} else {
int value = Integer.parseInt(token);
dataRow.add(value);
}
}
}
}
return dataRow;
}
}
The GUI looks like:
Now for some explanation:
OK, what this code does:
First I use an external image, one that is freely available from Wikipedia / Wikimedia, so that my code can be run from any computer and demonstrate the use of images in a JPanel. The image path can be found here:
private static final String IMG_PATH = "https://upload.wikimedia.org/"
+ "wikipedia/commons/d/d1/Ozanne-Brest.jpg";
and it's used here to create an image:
URL imageUrl = new URL(IMG_PATH);
img = ImageIO.read(imageUrl);
Which is then drawn within the JPanel's paintComponent method:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
My code also extends JPanel as this gives my GUI programs much greater flexibility than they'd have if I extended JFrame:
public class TableFun extends JPanel {
I override the JPanel's getPreferredSize method so that it sizes to the image, unless it needs to be bigger. That's why I first get the super's preferred size, and try to make my GUI big enough to show the image, and bigger if it needs to be to show more components:
#Override
public Dimension getPreferredSize() {
Dimension superSize = super.getPreferredSize();
if (isPreferredSizeSet() || img == null) {
return superSize;
}
int supW = superSize.width;
int supH = superSize.height;
int imgW = img.getWidth();
int imgH = img.getHeight();
int w = Math.max(imgW, supW);
int h = Math.max(imgH, supH);
return new Dimension(w, h);
}
In order to simplify code, I try to separate code from data, and place my data inside of a text file. The DataIO class is a small utility class that exists to read in the text held by the file, and then to parse the data line by line to create and fill a DefaultTableModel object. Note that I get the file not as a "File" but rather as a class resource, since this way I can include it within the jar file if I desire to do so. I use a Scanner to read in each line, exactly as you'd do for a text file.
public TableModel createTableModel() throws IOException {
InputStream inputStream = getClass().getResourceAsStream(dataPath);
Scanner scanner = new Scanner(inputStream);
Once I get a line of text, I split it by white space, which creates an array of Strings with all the white space trimmed off:
String[] tokens = line.split("\\s+");
The JTable uses a table cell renderer called MyCellRenderer
table.setDefaultRenderer(Object.class, new MyCellRenderer());
that makes the cells opaque if they hold data, and non-opaque (clear) if they hold null values:
renderer.setOpaque(value != null);
I also make sure that the JTable itself, the JScrollPane and the scrollpane's viewport are all non-opaque, so that the image can show through:
table.setOpaque(false);
// ....
scrollPane.setOpaque(false);
scrollPane.getViewport().setOpaque(false);
You mention a NPE being thrown, which likely means that your Java program is not finding the text file. Again it must be placed with your class files. You should create a text file with Eclipse in the same directory as your java file, name it precisely TableFunData.txt (spelling and capitalization matter), and when you're done, Eclipse should look something like:
where the code and the data text are seen in the same package using Eclipse's package explorer (highlighted in blue and red), and the code's package statement should show that it is in fact inside this package (in green).
Here is a simple implementation of JTable, as #Hovercraft Full Of Eels suggested.
Background colors are rendered based on data. 1 will cause background color to be blue, while 0 will render as red.
(The demo is based on https://docs.oracle.com/javase/tutorial/displayCode.html?code=https://docs.oracle.com/javase/tutorial/uiswing/examples/components/SimpleTableDemoProject/src/components/SimpleTableDemo.java )
public class TableDemo extends JPanel {
public TableDemo() {
super(new GridLayout(1,0));
String[] columnNames = {" ",
"Main Hand",
"Off Hand",
"Head",
"Shoulder",
"Neck"};
Object[][] data = {
{"Weapon Attacks Per Second", new Integer(1), new Integer(0), new Integer(0),new Integer(0),new Integer(0)},
{"Attack Speed", new Integer(1), new Integer(0), new Integer(1),new Integer(0),new Integer(0)},
{"Damage%", new Integer(1), new Integer(0), new Integer(0),new Integer(0),new Integer(0)},
{"Min Bonus Damage", new Integer(1), new Integer(0), new Integer(0),new Integer(1),new Integer(0)},
{"Max Bonus Damage", new Integer(1), new Integer(1), new Integer(0),new Integer(0),new Integer(0)},
{"Min Weapon Damage", new Integer(1), new Integer(0), new Integer(0),new Integer(1),new Integer(0)},
{"Max Weapon Damage", new Integer(1), new Integer(0), new Integer(1),new Integer(0),new Integer(0)},
};
final JTable table = new JTable(data, columnNames);
table.setPreferredScrollableViewportSize(new Dimension(600, 70));
table.getColumnModel().getColumn(1).setCellRenderer(new ColorRenderer());
table.getColumnModel().getColumn(2).setCellRenderer(new ColorRenderer());
table.getColumnModel().getColumn(3).setCellRenderer(new ColorRenderer());
table.getColumnModel().getColumn(4).setCellRenderer(new ColorRenderer());
table.getColumnModel().getColumn(5).setCellRenderer(new ColorRenderer());
//Create the scroll pane and add the table to it.
JScrollPane scrollPane = new JScrollPane(table);
//Add the scroll pane to this panel.
add(scrollPane);
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("TableDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
TableDemo newContentPane = new TableDemo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
class ColorRenderer extends DefaultTableCellRenderer {
public ColorRenderer() {
setOpaque(true); //MUST do this for background to show up.
}
#Override
public Component getTableCellRendererComponent( JTable table, Object value, boolean isSelected,
boolean hasFocus,int row, int column) {
if((int)value == 1) {
setBackground(Color.BLUE);
}else {
setBackground(Color.RED);
}
return this;
}
}

Using custom JPanel components in JList

I need to display JList with custom JPanel components called Item. These components have unique identification name. They may be dynamically added to the JList, or just updated (if already exist). I try the following implementation, but it produces just an empty JList. Please advise.
class Item extends JPanel {
JLabel name = new JLabel(" ");
JLabel col1 = new JLabel(" ");
JLabel col2 = new JLabel(" ");
Item(){
setMinimumSize(new Dimension(100, 20));
setLayout(new BorderLayout());
add(name, BorderLayout.WEST);
add(col1, BorderLayout.CENTER);
add(col2, BorderLayout.EAST);
}
}
public class Test_List extends JFrame {
private final JList list = new JList(new Item[0]);
HashMap<String, Item> map = new HashMap<String, Item>();
Test_List(){
setTitle("Test JList");
setLayout(new BorderLayout());
add(new JScrollPane(list), BorderLayout.CENTER);
pack();
}
public void update_item(String name, String s1, String s2){
Item item = map.get(name);
if (item == null){ // add new
item = new Item();
item.name.setText(name);
map.put(name, item);
list.add(item);
}
item.col1.setText(s1);
item.col2.setText(s2);
}
public static void main(String s[]) {
final Test_List frame = new Test_List();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
frame.setVisible(true);
}
});
frame.update_item("A", "a", "aa"); // add new item
frame.update_item("B", "b", "bb"); // add new item
frame.update_item("A", "aa", "a"); // update existing item
}
}
A model is responsible for modeling data, it should NEVER contain UI specific information, as the model shouldn't care how it is to be displayed, it is responsible for transforming the data so it confirms to the view/model contract, in this case the ListModel contract.
Start by taking a look at Writing a Custom Cell Renderer
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.DefaultListModel;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.ListCellRenderer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.Border;
import javax.swing.border.EmptyBorder;
public class TestListCellRenderer {
public static void main(String[] args) {
new TestListCellRenderer();
}
public TestListCellRenderer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
DefaultListModel model = new DefaultListModel();
model.addElement(new Item("A", "a", "aa"));
model.addElement(new Item("B", "b", "bb"));
model.addElement(new Item("C", "c", "cc"));
model.addElement(new Item("D", "d", "dd"));
JList list = new JList(model);
list.setCellRenderer(new ItemCellRenderer());
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new JScrollPane(list));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class Item {
private String name;
private String col1;
private String col2;
public Item(String name, String col1, String col2) {
this.name = name;
this.col1 = col1;
this.col2 = col2;
}
public String getCol1() {
return col1;
}
public String getCol2() {
return col2;
}
public String getName() {
return name;
}
}
public static class ItemCellRenderer extends JPanel implements ListCellRenderer<Item>{
private static final Border SAFE_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
private static final Border DEFAULT_NO_FOCUS_BORDER = new EmptyBorder(1, 1, 1, 1);
protected static Border noFocusBorder = DEFAULT_NO_FOCUS_BORDER;
JLabel name = new JLabel(" ");
JLabel col1 = new JLabel(" ");
JLabel col2 = new JLabel(" ");
public ItemCellRenderer() {
setLayout(new BorderLayout());
add(name, BorderLayout.WEST);
add(col1, BorderLayout.CENTER);
add(col2, BorderLayout.EAST);
}
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 20);
}
#Override
public Dimension getPreferredSize() {
return getMinimumSize();
}
protected Border getNoFocusBorder() {
Border border = UIManager.getBorder("List.cellNoFocusBorder");
if (System.getSecurityManager() != null) {
if (border != null) return border;
return SAFE_NO_FOCUS_BORDER;
} else {
if (border != null &&
(noFocusBorder == null ||
noFocusBorder == DEFAULT_NO_FOCUS_BORDER)) {
return border;
}
return noFocusBorder;
}
}
#Override
public Component getListCellRendererComponent(JList<? extends Item> list, Item value, int index, boolean isSelected, boolean cellHasFocus) {
setComponentOrientation(list.getComponentOrientation());
Color bg = null;
Color fg = null;
JList.DropLocation dropLocation = list.getDropLocation();
if (dropLocation != null
&& !dropLocation.isInsert()
&& dropLocation.getIndex() == index) {
bg = UIManager.getColor("List.dropCellBackground");
fg = UIManager.getColor("List.dropCellForeground");
isSelected = true;
}
if (isSelected) {
setBackground(bg == null ? list.getSelectionBackground() : bg);
setForeground(fg == null ? list.getSelectionForeground() : fg);
} else {
setBackground(list.getBackground());
setForeground(list.getForeground());
}
name.setText(value.getName());
col1.setText(value.getCol1());
col2.setText(value.getCol2());
name.setForeground(getForeground());
col1.setForeground(getForeground());
col2.setForeground(getForeground());
setEnabled(list.isEnabled());
name.setFont(list.getFont());
col1.setFont(list.getFont());
col2.setFont(list.getFont());
Border border = null;
if (cellHasFocus) {
if (isSelected) {
border = UIManager.getBorder("List.focusSelectedCellHighlightBorder");
}
if (border == null) {
border = UIManager.getBorder("List.focusCellHighlightBorder");
}
} else {
border = getNoFocusBorder();
}
setBorder(border);
return this;
}
}
}
Having said all that, I think you would be better off with a using a JTable, see How to Use Tables

Issue after filtering JTable

I have an issue with a program I am working on. To briefly explain, I have a JTable with multiple columns and rows. Particular columns have editable fields when upon changing the value other column values change according to the inputted data. Everything works well however when I've added a filter option to the JTable, changes made to an editable column won't change the values of other columns as intended after applying the filter. I've attached a couple of images to show the problem.
The first image shows the unfiltered table working correctly. Changing a Discount column value will reduce the corresponding price stored in the GPL column by the percent the inputted discount and displayed in the corresponding row in the SP column. Changing a Quantity column value will multiply the corresponding SP column price with the inputted quantity and displayed in the corresponding row in the Total column.
The second image shows the filtered table not working as intended. Changing a value in either Discount or Quantity columns will not change the intended columns.
I've added the SSCCE code below which contains 2 classes. First is the table itself and the second is the listener for the table.
EDIT I've changed the code of the class according to camickr's answer and now fully works.
TableCellChange class
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.KeyboardFocusManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.math.BigDecimal;
import java.math.MathContext;
import java.text.DecimalFormat;
import java.util.Locale;
import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.DefaultCellEditor;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.JTextField;
import javax.swing.RowFilter;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.event.DocumentEvent;
import javax.swing.event.DocumentListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;
import javax.swing.table.TableModel;
import javax.swing.table.TableRowSorter;
public final class TableCellChange extends JPanel {
private static JFrame frameTableCellChange;
private JPanel panelTable, panelButtons;
private JButton buttonResetDiscounts, buttonResetQuantities, buttonExit;
private JTextField textFilterBox, quantityField, discountField;
private JLabel labelFilter;
private DefaultTableModel tableModel;
private JTable table;
private TableRowSorter<DefaultTableModel> sorter;
private TableColumn columnDiscount, columnTotal, columnQuantity;
private TableCellListener tableCellListener;
private String checkForNull;
private DecimalFormat decimalFormatUS;
private Locale localeUSFormat;
private BigDecimal valueDiscount, valueGPL, resultDiscount, resultSP, resultTotal,
backupDiscount = new BigDecimal("0");
private int selectedColumnIndex, selectedRowIndex, valueQuantity, backupQuantity = 1;
public TableCellChange() {
super();
panelTable = new JPanel();
panelButtons = new JPanel();
setLayout(new BorderLayout());
createTable();
createButtons();
add(panelTable, BorderLayout.NORTH);
add(panelButtons, BorderLayout.CENTER);
// Always focus on the JTextField when opening the window
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
textFilterBox.requestFocusInWindow();
}
});
} // -> TableCellChange()
// Create the buttons for the query result window
public void createButtons() {
GridBagLayout gridbag = new GridBagLayout();
GridBagConstraints gridcons = new GridBagConstraints();
gridcons.fill = GridBagConstraints.HORIZONTAL;
panelButtons.setLayout(gridbag);
labelFilter = new JLabel("Quick search:");
gridcons.insets = new Insets(5,0,0,0);
gridcons.gridx = 0;
gridcons.gridy = 0;
gridcons.gridwidth = 2;
gridbag.setConstraints(labelFilter, gridcons);
labelFilter.setHorizontalAlignment(JLabel.CENTER);
panelButtons.add(labelFilter);
// Create text field for filtering
textFilterBox = new JTextField();
gridcons.insets = new Insets(5,0,0,0);
gridcons.gridx = 0;
gridcons.gridy = 1;
gridcons.gridwidth = 2;
gridbag.setConstraints(textFilterBox, gridcons);
textFilterBox.getDocument().addDocumentListener(
new DocumentListener() {
#Override
public void changedUpdate(DocumentEvent e) {
tableFilter();
}
#Override
public void insertUpdate(DocumentEvent e) {
tableFilter();
}
#Override
public void removeUpdate(DocumentEvent e) {
tableFilter();
}
}); // -> DocumentListener()
panelButtons.add(textFilterBox);
// Create the button to reset the discount column to 0%
buttonResetDiscounts = new JButton("Reset all discounts");
buttonResetDiscounts.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
BigDecimal valueGPL, valueTotal;
int valueQuantity;
for (int i = 0; i < table.getModel().getRowCount(); i++) {
valueGPL = new BigDecimal( table.getModel().
getValueAt(i, 2).toString().replaceAll("[$,]", "") );
table.getModel().setValueAt("0%", i, 3);
table.getModel().setValueAt(DecimalFormat
.getCurrencyInstance(localeUSFormat).format(valueGPL), i, 4);
valueQuantity = Integer.parseInt( table.getModel().
getValueAt(i, 5).toString() );
valueTotal = valueGPL.multiply(new BigDecimal(valueQuantity),
new MathContext(BigDecimal.ROUND_HALF_EVEN));
table.getModel().setValueAt(DecimalFormat
.getCurrencyInstance(localeUSFormat).format(valueTotal), i, 6);
}
}
});
gridcons.insets = new Insets(10,0,0,0);
gridcons.gridx = 0;
gridcons.gridy = 3;
gridcons.gridwidth = 1;
gridbag.setConstraints(buttonResetDiscounts, gridcons);
panelButtons.add(buttonResetDiscounts);
// Create button to reset the quantity column to 1
buttonResetQuantities = new JButton("Reset all quantities");
buttonResetQuantities.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
BigDecimal valueSP;
for (int i = 0; i < table.getModel().getRowCount(); i++) {
valueSP = new BigDecimal( table.getModel().
getValueAt(i, 4).toString().replaceAll("[$,]", "") );
table.getModel().setValueAt("1", i, 5);
table.getModel().setValueAt(DecimalFormat.
getCurrencyInstance(localeUSFormat).format(valueSP), i, 6);
}
}
});
gridcons.insets = new Insets(10,0,0,0);
gridcons.gridx = 1;
gridcons.gridy = 3;
gridcons.gridwidth = 1;
gridbag.setConstraints(buttonResetQuantities, gridcons);
panelButtons.add(buttonResetQuantities);
// Create button for closing the window and releasing resources
buttonExit = new JButton("Exit");
buttonExit.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e)
{
System.exit(0);
}
});
gridcons.insets = new Insets(5,0,0,0);
gridcons.gridx = 0;
gridcons.gridy = 5;
gridcons.gridwidth = 2;
gridbag.setConstraints(buttonExit, gridcons);
panelButtons.add(buttonExit);
} // -> createButtons()
// Filters the JTable based on user input
private void tableFilter() {
RowFilter<DefaultTableModel, Object> tableRowFilter;// = null;
// If current expression doesn't parse, don't update
try {
tableRowFilter = RowFilter.regexFilter("(?i)" + textFilterBox.
getText(), 0, 1, 2);
} catch (java.util.regex.PatternSyntaxException e) {
return;
}
sorter.setRowFilter(tableRowFilter);
} // -> tableFilter
// Method that creates the JTable
public void createTable() {
// Create listener for selecting all text when a text field gains focus
KeyboardFocusManager.getCurrentKeyboardFocusManager()
.addPropertyChangeListener("permanentFocusOwner", new PropertyChangeListener() {
#Override
public void propertyChange(final PropertyChangeEvent e) {
if (e.getNewValue() instanceof JTextField) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JTextField textField = (JTextField)e.getNewValue();
textField.selectAll();
}
});
}
}
});
String[] columnNames = {"Model", "Description", "GPL", "Discount", "SP",
"Quantity", "Total"};
Object[][] data = {
{"MR16", "desc1", "$649.00", "0%", "$649.00", new Integer(1), "$649.00"},
{"MR24", "desc2", "$1,199.00", "0%", "$1,199.00", new Integer(1), "1,199.00"},
{"MR62", "desc3", "$699.00", "0%", "$699.00", new Integer(1), "$699.00"},
{"MR66", "desc4", "$1,299.00", "0%", "$1,299.00", new Integer(1), "$1,299.00"},
{"MX80", "desc5", "$1,995.00", "0%", "$1,995.00", new Integer(1), "$1,995.00"},
{"MX90", "desc6", "$3,995.00", "0%", "$3,995.00", new Integer(1), "$3,995.00"},
{"MX400", "desc7", "$15,995.00", "0%", "$15,995.00", new Integer(1), "$15,995.00"},
{"MX600", "desc8", "$31,995.00", "0%", "$31,995.00", new Integer(1), "$31,995.00"},
{"MS22-HW", "desc9", "$1,999.00", "0%", "$1,999.00", new Integer(1), "$1,999.00"},
{"MS42-HW", "desc10", "$3,499.00", "0%", "$3,499.00", new Integer(1), "$3,499.00"},
};
// Create the TableModel and populate it
tableModel = new DefaultTableModel(data, columnNames) {
Class [] classes = {String.class, String.class, String.class,
String.class, String.class, int.class, String.class, Boolean.class};
#Override
public Class getColumnClass(int column) {
return classes[column];
}
};
// Create a JTable and populate it with the content of the TableModel
table = new JTable(tableModel) {
#Override
public boolean isCellEditable(int row, int column) {
if (column == 0 || column == 1 || column == 2 || column == 4 ||
column == 6) {
return false;
}
return true;
}
};
// This sorter is used for text filtering
sorter = new TableRowSorter<>(tableModel);
for (int column = 3; column < 6; column++) {
sorter.setSortable(column, false);
}
table.setRowSorter(sorter);
columnTotal= table.getColumnModel().getColumn(6);
columnTotal.setPreferredWidth(100);
// Filter user input in the quantity text field to only allow digits
discountField =new JTextField();
discountField.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e)
{
if(!Character.isDigit(e.getKeyChar()) && e.getKeyChar() !=KeyEvent.VK_BACK_SPACE) {
discountField.setEditable(false);
discountField.setBackground(Color.WHITE);
} else {
discountField.setEditable(true);
}
}
});
// Set the text field to the cells of the quantity column
columnQuantity = table.getColumnModel().getColumn(5);
columnQuantity.setCellEditor(new DefaultCellEditor (discountField));
// Filter user input in the discount text field to only allow digits
quantityField =new JTextField();
quantityField.addKeyListener(new KeyAdapter() {
#Override
public void keyTyped(KeyEvent e)
{
if(!Character.isDigit(e.getKeyChar()) && e.getKeyChar() !=KeyEvent.VK_BACK_SPACE) {
quantityField.setEditable(false);
quantityField.setBackground(Color.WHITE);
//JOptionPane.showMessageDialog(null,"Only digit input is allowed!");
} else {
quantityField.setEditable(true);
}
}
});
// Set the text field to the cells of the quantity column
columnDiscount = table.getColumnModel().getColumn(3);
columnDiscount.setCellEditor(new DefaultCellEditor(discountField));
// Create an US number format
localeUSFormat = Locale.US;
decimalFormatUS = (DecimalFormat) DecimalFormat.getInstance(localeUSFormat);
decimalFormatUS.setMaximumFractionDigits(2);
// Create abstract action which listens for changes made in the JTable
Action actionTableListener = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
TableCellListener tcl = (TableCellListener)e.getSource();
// Get the current row and column index of the table
selectedRowIndex = tcl.getRow();
selectedColumnIndex = tcl.getColumn();
TableModel model = tcl.getTable().getModel();
// Have a string variable check for null cell value
checkForNull = model.getValueAt(selectedRowIndex,selectedColumnIndex).toString();
// Change the discounted and total price values
if (selectedColumnIndex == 3) {
// Check if the discount value is null and replace with
// last used value if true
if (checkForNull.equals("")) {
model.setValueAt(backupDiscount + "%",selectedRowIndex, selectedColumnIndex);
return;
}
// Get the discount value and replace the '%' with nothing
valueDiscount = new BigDecimal(( model
.getValueAt(selectedRowIndex,selectedColumnIndex)
.toString().replaceAll("[%]","") ));
//
model.setValueAt(valueDiscount + "%",selectedRowIndex, selectedColumnIndex);
// Check if the discount value is greater than 100
if ( (valueDiscount.compareTo(new BigDecimal(100)) == 1 ) ) {
model.setValueAt(backupDiscount + "%",selectedRowIndex, selectedColumnIndex);
JOptionPane.showMessageDialog(null,"Discount cannot be more than 100%.");
} else {
backupDiscount = valueDiscount;
valueDiscount = valueDiscount.divide(new BigDecimal(100)
, 2, BigDecimal.ROUND_HALF_EVEN);
// Calculate SP and Total values based on the discount input
valueGPL = new BigDecimal( ( model
.getValueAt(selectedRowIndex,selectedColumnIndex - 1)
.toString().replaceAll("[$,]","") ) );
// Get the quantity value
valueQuantity = Integer.parseInt( ( model
.getValueAt(selectedRowIndex,selectedColumnIndex + 2)
.toString() ) );
// Calculate the new discount value
resultDiscount = valueGPL.multiply(valueDiscount,
new MathContext(BigDecimal.ROUND_HALF_EVEN));
// Calculate the new SP value
resultSP = valueGPL.subtract(resultDiscount,
new MathContext(BigDecimal.ROUND_HALF_EVEN));
// Calculate the new result value
resultTotal = resultSP.multiply(new BigDecimal(valueQuantity),
new MathContext(BigDecimal.ROUND_HALF_EVEN));
// Display the new SP value
model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat)
.format(resultSP),selectedRowIndex, selectedColumnIndex + 1);
// Display the new Total value
model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat)
.format(resultTotal),selectedRowIndex, selectedColumnIndex + 3);
}
}
// Change the total price values based on the quantity column
if (selectedColumnIndex == 5) {
// Check if the quantity value is null and replace with
// last used value if true
if (checkForNull.equals("")) {
model.setValueAt(backupQuantity,selectedRowIndex, selectedColumnIndex);
return;
}
// Change total price value based on the quantity column
resultSP = new BigDecimal( ( model.
getValueAt(selectedRowIndex,
selectedColumnIndex - 1).toString().replaceAll("[$,]","") ) );
valueQuantity = Integer.parseInt( ( model.getValueAt(selectedRowIndex,
selectedColumnIndex).toString() ) );
// Check if the value quantity is over a certain limit
if (valueQuantity <= 0 || valueQuantity >= 999999) {
model.setValueAt(backupQuantity,selectedRowIndex, selectedColumnIndex);
JOptionPane.showMessageDialog(null,"Quantity value is too high or invalid!");
} else {
// If the value is under the limit: backup the new quantity
// value, calculate the new total value and display it
backupQuantity = valueQuantity;
resultTotal = resultSP.multiply(new BigDecimal(valueQuantity),
new MathContext(BigDecimal.ROUND_HALF_EVEN));
model.setValueAt(DecimalFormat.getCurrencyInstance(localeUSFormat)
.format(resultTotal), selectedRowIndex, selectedColumnIndex + 1);
}
}
}
}; // -> AbstractAction()
tableCellListener = new TableCellListener(table, actionTableListener);
table.setPreferredScrollableViewportSize(table.
getPreferredSize());
table.setRowHeight(22);
setVisibleRowCount(table,10);
table.setAutoResizeMode( JTable.AUTO_RESIZE_OFF );
table.setFillsViewportHeight(true);
table.getTableHeader().setReorderingAllowed(false);
table.getTableHeader().setResizingAllowed(false);
panelTable.add(new JScrollPane(table));
} // -> createTable()
// Method to display a fixed number of rows in the JTable viewport
public static void setVisibleRowCount(JTable table, int rows){
int height = 0;
for(int row=0; row<rows; row++) {
height += table.getRowHeight(row);
}
table.setPreferredScrollableViewportSize(new Dimension(
table.getPreferredScrollableViewportSize().width, height ));
}
// Create and display the contents of the frame
public static void showGUI() {
// Disable boldface controls
UIManager.put("swing.boldMetal", Boolean.FALSE);
// Create the frame
frameTableCellChange = new JFrame("Table frame");
frameTableCellChange.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frameTableCellChange.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent we) {
System.exit(0);
}
});
// Create and set up the content pane.
TableCellChange newContentPane = new TableCellChange();
newContentPane.setOpaque(true); //content panes must be opaque
frameTableCellChange.setContentPane(newContentPane);
// Arrange and display the window.
frameTableCellChange.pack(); //must be called first
frameTableCellChange.setLocationRelativeTo(null); //center window
frameTableCellChange.setResizable(false);
frameTableCellChange.setVisible(true);
} //-> showQueryResultGUI()
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.
UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException | InstantiationException |
IllegalAccessException |
javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(TableCellChange.class.getName()).
log(java.util.logging.Level.SEVERE, null, ex);
}
// Display the frame and it's contents
TableCellChange.showGUI();
}
});
} //-> main(String[] args)
} //-> TableCellChange class
EDIT This class was created by Rob Camick (a.k.a. camickr), all credits go to him for creating this awesome piece of code. Only comments were removed from the code in order to respect the character limit.
TableCellListener class
import java.awt.event.ActionEvent;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import javax.swing.Action;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
/*
* This class listens for changes made to the data in the table via the
* TableCellEditor. When editing is started, the value of the cell is saved
* When editing is stopped the new value is saved. When the old and new
* values are different, then the provided Action is invoked.
* The source of the Action is a TableCellListener instance.
*/
public class TableCellListener implements PropertyChangeListener, Runnable {
private JTable table;
private Action action;
private int row;
private int column;
private Object oldValue;
private Object newValue;
public TableCellListener(JTable table, Action action) {
this.table = table;
this.action = action;
this.table.addPropertyChangeListener(this);
}
private TableCellListener(JTable table, int row, int column, Object oldValue, Object newValue) {
this.table = table;
this.row = row;
this.column = column;
this.oldValue = oldValue;
this.newValue = newValue;
}
public int getColumn() {
return column;
}
public Object getNewValue() {
return newValue;
}
public Object getOldValue() {
return oldValue;
}
public int getRow() {
return row;
}
public JTable getTable() {
return table;
}
#Override
public void propertyChange(PropertyChangeEvent e) {
if ("tableCellEditor".equals(e.getPropertyName())) {
if (table.isEditing()) {
processEditingStarted();
} else {
processEditingStopped();
}
}
}
private void processEditingStarted() {
SwingUtilities.invokeLater(this);
}
#Override
public void run() {
row = table.convertRowIndexToView(table.getEditingRow());
row = table.getEditingRow();
column = table.convertColumnIndexToModel(table.getEditingColumn());
oldValue = table.getModel().getValueAt(row, column);
newValue = null;
}
private void processEditingStopped() {
newValue = table.getModel().getValueAt(row, column);
if (!newValue.equals(oldValue)) {
TableCellListener tcl = new TableCellListener(
getTable(), getRow(), getColumn(), getOldValue(), getNewValue());
ActionEvent event = new ActionEvent(
tcl,
ActionEvent.ACTION_PERFORMED,
"");
action.actionPerformed(event);
}
}
}
I understand that when filtering a table the indexes of the table view change and must be synchronized with the indexes of the underlying model. How can that be done in order for the filtered table to work?
You implemented the Action for the TableCellListener incorrectly. You can't use the selected row/colum because those values are in the Table view. The TableCellListener works on the model.
Check out the example Action provided with Table Cell Editor. To get the row/column that was changed you must reference the TableCellListener itself.
Edit:
Here is my simple text example. When you change the "Price", the "Price Change" and "Value" columns are automatically updated.
import java.awt.*;
import java.awt.event.*;
import java.io.*;
import java.net.*;
import javax.swing.*;
import javax.swing.table.*;
public class TableCellListenerTest
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
public static void createAndShowGUI()
{
String[] columnNames = {"Stock", "Shares", "Price", "Price Change", "Value"};
Object[][] data =
{
{"IBM", new Integer(100), new Double(85), new Double(0), new Double(8500)},
{"Apple", new Integer(300), new Double(30), new Double(0), new Double(9000)},
{"Sun", new Integer(1500), new Double(5), new Double(0), new Double(7500)},
{"Google", new Integer(100), new Double(100), new Double(0), new Double(10000)}
};
DefaultTableModel model = new DefaultTableModel(data, columnNames)
{
public Class getColumnClass(int column)
{
return getValueAt(0, column).getClass();
}
public boolean isCellEditable(int row, int column)
{
return column == 2;
}
};
JTable table = new JTable(model);
table.setPreferredScrollableViewportSize(table.getPreferredSize());
JScrollPane scrollPane = new JScrollPane(table);
// Add a sorter
TableRowSorter<DefaultTableModel> sorter = new TableRowSorter<DefaultTableModel>(model);
table.setRowSorter(sorter);
// Filter
try
{
RowFilter<DefaultTableModel, Object> rf = RowFilter.regexFilter("l", 0);
sorter.setRowFilter(rf);
}
catch (java.util.regex.PatternSyntaxException e) {}
Action action = new AbstractAction()
{
public void actionPerformed(ActionEvent e)
{
TableCellListener tcl = (TableCellListener)e.getSource();
int column = tcl.getColumn();
if (column == 2)
{
int row = tcl.getRow();
double oldPrice = ((Double)tcl.getOldValue()).doubleValue();
double newPrice = ((Double)tcl.getNewValue()).doubleValue();
TableModel model = tcl.getTable().getModel();
double priceChange = new Double(newPrice - oldPrice);
model.setValueAt(priceChange, row, 3);
double shares = ((Integer)model.getValueAt(row, 1)).doubleValue();
Double value = new Double(shares * newPrice);
model.setValueAt(value, row, 4);
}
}
};
TableCellListener tcl = new TableCellListener(table, action);
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame frame = new JFrame("Table Cell Listener");
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.add( scrollPane );
frame.setSize(400, 160);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
}
}
You may need to do the conversion from view to model. Take a look at Sorting and Filtering part of How to Use Tables tutorial:
When a table uses a sorter, the data the users sees may be in a
different order than that specified by the data model, and may not
include all rows specified by the data model. The data the user
actually sees is known as the view, and has its own set of
coordinates. JTable provides methods that convert from model
coordinates to view coordinates — convertColumnIndexToView and
convertRowIndexToView — and that convert from view coordinates to
model coordinates — convertColumnIndexToModel and
convertRowIndexToModel.
EDIT: convert from view (table) to model:
Replace these rows:
row = table.convertRowIndexToView(table.getEditingRow());
row = table.getEditingRow();
With:
row = table.convertRowIndexToModel(table.getEditingRow());

How to strikethrough text of a selected row(having checkbox in first column) of JTable?

I have a Jtable with a checkbox in first column. I want to strikethrough text of a row when the checkbox is selected. (eg same as we do in microsoft outlook when our task is complete.) I have tried using AttributeString, but not able to do it.
Can anyone please guide me to solve it?
String strStrike;
AttributedString as;
public void setTextStrikeThrough() {
for(int r=0;r< taskcells.length;r++) {
if (ttable.getValueAt(r,0).equals(Boolean.TRUE)) {
for(int c=2;c<7;c++) {
strStrike+=taskcells[r][c-1];
}//end inner for as=new
AttributedString(strStrike);
as.addAttribute(TextAttribute.STRIKETHROUGH,
TextAttribute.STRIKETHROUGH_ON);
as.getIterator();
}//end if
}//end for
}
I am not getting exactly where to call this method. I want to strikethrough text of a row when checkbox of that row has been checked.
I don't know that an ActionListener will work well for a JCheckBox in a JTable since the check box isn't a real button but rather a rendering of a checkbox. Perhaps playing with the table model will help. For instance you can use HTML to display a strike through of Strings displayed in table cells. For instance below I create a custom TableModel that extends DefaultTableModel and holds rows with a Boolean object followed by objects of a TextWrapper class that I've created that changes its toString result depending on a boolean.
e.g.,
import java.util.Vector;
import javax.swing.*;
import javax.swing.table.DefaultTableModel;
public class StrikeThroughRow {
public static final Object[][] DATA = {{Boolean.TRUE, "Monday", "fe"},
{Boolean.FALSE, "Tuesday", "fi"}, {Boolean.TRUE, "Wednesday", "fo"},
{Boolean.FALSE, "Thursday", "fum"}, {Boolean.TRUE, "Friday", "foo"}};
public StrikeThroughRow() {
}
private static void createAndShowUI() {
JTable table = new JTable(new StrikeThroughModel(DATA));
JScrollPane scrollpane = new JScrollPane(table);
JFrame frame = new JFrame("StrikeThroughRow");
frame.getContentPane().add(scrollpane);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class StrikeThroughModel extends DefaultTableModel {
public StrikeThroughModel(Object[][] data) {
super(new String[]{"Check", "Work Day", "Giant Speak"}, 0);
for (int i = 0; i < data.length; i++) {
Vector<Object> rowVect = new Vector<Object>();
rowVect.add(data[i][0]);
if (data[i].length > 1) {
for (int j = 1; j < data[i].length; j++) {
rowVect.add(new TextWrapper(data[i][j].toString(), (Boolean)data[i][0]));
}
}
addRow(rowVect);
}
}
#Override
public Class<?> getColumnClass(int columnIndex) {
if (columnIndex == 0) {
return Boolean.class;
}
return super.getColumnClass(columnIndex);
}
#Override
public void setValueAt(Object value, int row, int column) {
if (column == 0) {
for (int i = 1; i < getColumnCount(); i++) {
TextWrapper textWrapper = (TextWrapper) getValueAt(row, i);
textWrapper.setStrikeThrough((Boolean) value);
fireTableCellUpdated(row, i);
}
}
super.setValueAt(value, row, column);
}
}
class TextWrapper {
private String text;
private boolean strikeThrough = false;
public TextWrapper(String text) {
this.text = text;
}
public TextWrapper(String text, boolean strikeThrough) {
this(text);
this.strikeThrough = strikeThrough;
}
#Override
public String toString() {
if (strikeThrough) {
return "<html><strike>" + text + "</html></strike>";
}
return text;
}
public void setStrikeThrough(boolean strikeThrough) {
this.strikeThrough = strikeThrough;
}
}
I'm betting that there are better solutions including creating a custom renderer for your cells, but the code above offers a quick and dirty fix.
Here is how you can create a "strike through font":
Map attributes = component.getFont().getAttributes();
attributes.put(TextAttribute.STRIKETHROUGH, TextAttribute.STRIKETHROUGH_ON);
component.setFont( new Font(attributes) );
One way to apply the font is to use the Table Row Rendering approach. Take a look at the background color example. Instead of setting the background of the renderer you can set the Font.
Otherwise you would need to create a custom renderer for the columns in your table to use the appropriate Font.
Add a listener to the checkbox which will add/remove the from the label. Here is an example of box and label maybe helpful:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.AbstractButton;
import javax.swing.BorderFactory;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class check {
public static void main(String args[]) {
JFrame frame = new JFrame("for bsm");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JCheckBox box = new JCheckBox("check me");
final JLabel label = new JLabel("<html>text</html>");
label.setFont(new Font("helvetica", Font.PLAIN, 12));
label.setForeground(new Color(50, 50, 25));
ActionListener actionListener = new ActionListener() {
public void actionPerformed(ActionEvent actionEvent) {
AbstractButton abstractButton = (AbstractButton) actionEvent.getSource();
if(abstractButton.getModel().isSelected())
label.setText(label.getText().replace("<html>", "<html><strike>").replace("</html>", "</strike></html>"));
else
label.setText(label.getText().replace("<html><strike>", "<html>").replace("</strike></html>", "</html>"));
}
};
box.addActionListener(actionListener);
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout(10, 10));
panel.add(label, BorderLayout.NORTH);
panel.add(box, BorderLayout.SOUTH);
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
frame.add(panel);
frame.setSize(300, 200);
frame.setVisible(true);
}
}

Categories