Why Does the Jtable Header Not Appear in the Image

Why does the JTable header not appear in the image?

It was not a requirement of this thread to render the table without first displaying it, but that was the ultimate goal

ScreenImage handles this.

You must manually add the header to the scrollpane.

import javax.swing.*;
import java.awt.Graphics;
import java.awt.BorderLayout;
import java.awt.image.BufferedImage;

import javax.imageio.ImageIO;
import java.io.File;

class TableImage {

public static void main(String[] args) throws Exception {
Object[][] data = {
{"Hari", new Integer(23), new Double(78.23), new Boolean(true)},
{"James", new Integer(23), new Double(47.64), new Boolean(false)},
{"Sally", new Integer(22), new Double(84.81), new Boolean(true)}
};

String[] columns = {"Name", "Age", "GPA", "Pass"};

JTable table = new JTable(data, columns);
JScrollPane scroll = new JScrollPane(table);

scroll.setColumnHeaderView(table.getTableHeader());
table.setPreferredScrollableViewportSize(table.getPreferredSize());

JPanel p = new JPanel(new BorderLayout());
p.add(scroll, BorderLayout.CENTER);

BufferedImage bi = ScreenImage.createImage(p);

JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(bi)));
ImageIO.write(bi,"png",new File("table.png"));
}
}

Note: Kleopatra's suggeston to use the addNotify() on the panel will not work with ScreenImage. The addNotify() method makes the component displayable and the ScreenImage code will only lay out the components for non-displayable components. I might look into making this more general.

JTable header not showing still

Based on your code and then having to add the missing functionality, you code works...

Sample Image

import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.AbstractTableModel;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.TableColumn;

public class Gui extends JFrame {

public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}

Gui frame = new Gui();
frame.start(new DefaultTableModel(new Object[]{"A", "B", "C"}, 10));
}
});
}

AbstractTableModel model;
JTable table;

public void start(AbstractTableModel model) {
this.model = model;
table = new JTable(model) {
@Override
public boolean isCellEditable(int arg0, int arg1) {
return false;
}

};

table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
TableColumn column = null;
for (int i = 0; i < model.getColumnCount(); i++) {
column = table.getColumnModel().getColumn(i);
column.setPreferredWidth(120);
column.setMaxWidth(300);
column.setMinWidth(50);
}

JScrollPane pane = new JScrollPane(table);
pane.setPreferredSize(new Dimension(900, 900));

add(pane);
setLayout(new FlowLayout());
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();

}
}

This then leads to two broad assumptions...

  1. You're TableModel doesn't have any columns
  2. You're TableModel doesn't have any column names...

For example, a TableModel with no column names...

No column names

Personally...

  • I wouldn't use FlowLayout for this, BorderLayout will give better results
  • pack the frame before you make it visible
  • Set the layout before you add components as sometimes, things can get messed up...

For example...

setLayout(new FlowLayout()); // But I'd use a `BorderLayout`
add(pane);
setDefaultCloseOperation(EXIT_ON_CLOSE);
pack();
setVisible(true);

JTable won't show column headers

Put your JTable inside a JScrollPane. Try this:

add(new JScrollPane(scrTbl));

Java JTable column header not showing using DefaultModel

You're adding the JTable directly to the GUI. Instead, yes you need to embed the JTable into the viewport of a JScrollPane and then add the JScrollPane to the GUI.

For example:

    table = new JTable();
JScrollPane scrollPane = new JScrollPane(table);
// table.setBounds(10, 304, 461, 189);
scrollPane.setBounds(10, 304, 461, 189); // This is bad, but will leave for now
// frame.getContentPane().add(table);
frame.getContentPane().add(scrollPane);

Also, you're harming your GUI by using null layouts and absolute positioning, as this can interfere with a component's ability to show itself fully and correctly, to achieve its own preferred size. Much better is to learn and use the layout managers.

For instance, when I run your program on my platform, I see:

Sample Image

Note how the buttons do not show their full texts due to their not being allowed to achieve their preferred sizes.

For example, using BoxLayout with some nested JPanels, one using GridLayout(1, 0, 5, 0) for one row, variable number of columns, and a 5 point horizontal gap between components, and another nested JPanel using GridBagLayout, for placement of JTextFields and JLabels, and some "wrapper" JPanels using default FlowLayout to center components within them...

import java.awt.Dimension;
import java.awt.Font;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.Insets;

import javax.swing.*;
import javax.swing.table.DefaultTableModel;

public class AdminPage2 extends JPanel {
private static final long serialVersionUID = 1L;
public static final String TITLE = "Administrator Portal";
private static final Font TITLE_FONT = new Font("Tahoma", Font.BOLD, 20);
private static final String[] COL_NAMES = {"Name", "User Name", "Password"};
private int txtFieldCols = 20;
private JTextField nameField = new JTextField(txtFieldCols);
private JTextField userNameField = new JTextField(txtFieldCols);
private JPasswordField passwordField = new JPasswordField(txtFieldCols);
private DefaultTableModel tableModel = new DefaultTableModel(COL_NAMES, 0);
private JTable table = new JTable(tableModel);
private JScrollPane tableScrollPane = new JScrollPane(table);

public AdminPage2() {
JLabel titleLabel = new JLabel(TITLE, SwingConstants.CENTER);
titleLabel.setFont(TITLE_FONT);
JPanel titlePanel = new JPanel();
titlePanel.add(titleLabel);

JPanel buttonPanel = new JPanel(new GridLayout(1, 0, 5, 0));
// of course you'd add ActionListeners or Actions to your buttons
buttonPanel.add(new JButton("Add Library"));
buttonPanel.add(new JButton("Delete"));
buttonPanel.add(new JButton("View Library"));
buttonPanel.add(new JButton("Logout"));

JPanel textFieldPanel = new JPanel(new GridBagLayout());
textFieldPanel.add(new JLabel("Name:"), createGbc(0, 0));
textFieldPanel.add(nameField, createGbc(1, 0));
textFieldPanel.add(new JLabel("User Name:"), createGbc(0, 1));
textFieldPanel.add(userNameField, createGbc(1, 1));
textFieldPanel.add(new JLabel("Password:"), createGbc(0, 2));
textFieldPanel.add(passwordField, createGbc(1, 2));

JPanel wrapTfPanel = new JPanel();
wrapTfPanel.add(textFieldPanel);

Dimension scrollPanePrefSz = tableScrollPane.getPreferredSize();
int w = scrollPanePrefSz.width;
int h = scrollPanePrefSz.height / 2;
scrollPanePrefSz = new Dimension(w, h);
tableScrollPane.setPreferredSize(scrollPanePrefSz);

// put together main JPanel components
int ebGap = 4;
setBorder(BorderFactory.createEmptyBorder(ebGap, ebGap, ebGap, ebGap));
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(Box.createVerticalStrut(5));
add(titlePanel);
add(Box.createVerticalStrut(5));
add(buttonPanel);
add(Box.createVerticalStrut(5));
add(wrapTfPanel);
add(Box.createVerticalStrut(5));
add(tableScrollPane);
}

// create constraints to use when adding component to GridBagLayout
private GridBagConstraints createGbc(int x, int y) {
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = x;
gbc.gridy = y;
gbc.gridwidth = 1;
gbc.gridheight = 1;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.anchor = x == 0 ? GridBagConstraints.WEST : GridBagConstraints.EAST;
gbc.fill = GridBagConstraints.HORIZONTAL;
int in = 10;
int leftIn = x == 0 ? 4 * in : in;
gbc.insets = new Insets(in, leftIn, in, in);
return gbc;
}

public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}

private static void createAndShowGui() {
AdminPage2 mainPanel = new AdminPage2();
JFrame frame = new JFrame("Administrator Page");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}

}

Which displays as:

Sample Image

Regarding your questions:

but new question why give the scrollpane a bound instead of the table itself

The JScrollPane holds the JTable within it, and so if you use null layouts (which you shouldn't), and you're adding this JTable-containing JScrollPane to the GUI, you must set its bounds. Much better though is to use layout managers as I've outlined above. It makes it much easier to modify the GUI later and to debug it now.

and why adding the scrollpane into the panel instead of the table.

Because that's how JScrollPanes work. They don't add scrollbars to a component but rather nest the component itself, here the JTable, within the JScrollPane's viewport. Please read the JScrollPane Tutorial (see link) to see the details on this.


A further note on the power of layout managers. Say you want to add a new JLabel / JTextField combination, one that accepts a password hint, and say the JTextField's name is passwordHint. If you were using null layouts and absolute positioning, you'd have to set the bounds of your new JLabel and JTextField, but you'd also have to change the bounds of all components below and to the right of it, and would have to re-set the size of the GUI manually. If your GUI is very complex, this can lead to bugs and a lot of frustration.

If you used the layout managers above however, all you'd need to do would be to add two lines of code to the textFieldPanel JPanel creational code as shown below with the obvious comments:

// original textFieldPanel creational code
JPanel textFieldPanel = new JPanel(new GridBagLayout());
textFieldPanel.add(new JLabel("Name:"), createGbc(0, 0));
textFieldPanel.add(nameField, createGbc(1, 0));
textFieldPanel.add(new JLabel("User Name:"), createGbc(0, 1));
textFieldPanel.add(userNameField, createGbc(1, 1));
textFieldPanel.add(new JLabel("Password:"), createGbc(0, 2));
textFieldPanel.add(passwordField, createGbc(1, 2));

// !! ****** these lines added ******
textFieldPanel.add(new JLabel("Password Hint:"), createGbc(0, 3));
textFieldPanel.add(passwordHint, createGbc(1, 3));

This results in a perfect placement of the new components without adversely affecting the old:

Sample Image

Set JTable Background Header Image

table.getTableHeader().setDefaultRenderer(new ImageHeaderRenderer());

This code is setting your renderer to the table header. And in renderer you can draw your image to a JLabel.

Image cell renderer:

class ImageHeaderRenderer extends DefaultTableCellRenderer {
JLabel label = new JLabel();

ImageIcon icon = new ImageIcon(getClass().getResource("image.png"));

public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected,
boolean hasFocus, int row, int column) {
label.setText("");
label.setIcon(icon);
return label;
}
}

Java JTable Column headers not showing - JScrollPane?

The problem is not the table or the jscrollpane but how you put them in the panel:

panel.add(scrollPane);
panel.add(titleLabel);
panel.add(scheduleTable);

First of all the scheduleTable is (visually) inside the scrollPane, so you only need to put the scrollPane, not the table again. So we want:

panel.add(titleLabel); //first put the title
panel.add(scrollPane);

this works nicely:

Sample Image

And when I say it works nicely I mean it works nicely... by chance. The real layout of the elements is discovered when you make the window bigger:

Sample Image

The problem is in this code again:

panel.add(titleLabel); //first put the title
panel.add(scrollPane);

This surely adds the components (label and scrollpane) to the panel, however the obvious question is "Where in the panel adds them?".

Then answer is that the panel has a LayoutManager that decides the position of its components. There are several of them, you have to pick one, learn how it works and use it, look at this visual guide.

Because you didn't specify a layout, the panel is using the default layout for panels FlowLayout, that puts the components one after the other, side by side.

Pick a layout look at the tutorial and then use it. For example I could use a BorderLayout like this:

JPanel panel = new JPanel(new BorderLayout());
...
panel.add(titleLabel, BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);

Gives:

Sample Image

Full code:

String[] columnNames = {"Date","Field", "Home Team","Visitor Team", "Score"};
JFrame guiFrame = new JFrame(); //no layout specified, frames use BorderLayout by default
guiFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
guiFrame.setTitle("Soccer Schedule");
guiFrame.setSize(500,500);
guiFrame.setLocationRelativeTo(null);

JPanel panel = new JPanel(new BorderLayout());
panel.setSize(450, 450);
JLabel titleLabel = new JLabel("Real Tired");

//String[][] data = createTableContents(scheduleCsv);

//my test data
String[][] data = new String[1][5];
for (int i = 0; i < 5; ++i)
{
data[0][i] = "cell " + i;
}

JTable scheduleTable = new JTable(data,columnNames);
JScrollPane scrollPane = new JScrollPane(scheduleTable);
panel.add(titleLabel, BorderLayout.NORTH);
panel.add(scrollPane, BorderLayout.CENTER);

guiFrame.add(panel); //this is actually guiFrame.add(panel, BorderLayout.CENTER);
guiFrame.setVisible(true);

Jtable, issues with displaying sorting icon TableHeader with custom renderer and system L&F

[...] the sorting icon does not appear when I'm using the default system L&F (in my case windows 8) but the icon appears when I use the Java L&F.

The difference between the default L&F (Metal) and the system L&F is that the first one draws the sorting icon in the center (vertical alignment) of the header cell and the latter draws it in top of the header cell.

What happens next is that you draw a border for the header cells. A border is painted from the edge of the component inwards, meaning it paints over the component's area. Since the sorting icon is on the edge for the system L&F, it is painted over by the border.

You can solve this by removing the border, or adding the label to a JPanel which will handle the border instead:

@Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocused, int row, int column) {

Component comp = delegate.getTableCellRendererComponent(table, value, isSelected, hasFocused, row, column);
JPanel panel = new JPanel(new BorderLayout());
if (comp instanceof JLabel) {
JLabel label = (JLabel) comp;
label.setBackground(Color.WHITE);
label.setHorizontalAlignment(SwingConstants.CENTER);
panel.add(label);
panel.setBorder(lb);
}
return panel;
}

I removed setPreferredSize as noted by @mKorbel and the dim field should be removed with it.

Also, in the lines

JTableHeader tableHeaderCompnoent = table_1.getTableHeader();
tableHeaderCompnoent.setDefaultRenderer(new HeaderRenderer(tableHeaderCompnoent.getDefaultRenderer()));
table_1.getTableHeader().setDefaultRenderer(new HeaderRenderer(tableHeaderCompnoent.getDefaultRenderer()));

the 2nd and 3rd are the same, you can remove the 3rd.



Related Topics



Leave a reply



Submit