I am trying to create colored headers and footers when printing a JTable. Specifically, I am looking at getPrintable() in javax.swing.JTable, but MessageFormat does not give me the option to specify the color of the header or footer.
How can I do it?
clarification
I am interested in setting the header/footers while printing. For example, notepad appends the filename as a header to what you print.
update
Seems like there is no standard way of doing this, can someone give me some workarounds? The only answer posted so far has nothing to do with printing(as in send to a printer, not displaying to screen) header/footers.
Copied from my comment: I am interested in the printing header/footer. For example, when you are printing a document from notepad, it appends the filename as a header (or perhaps its the footer, I do not remember exactly)
One solution I can think of is to use your own printable:
public class CustomTablePrintable implements Printable {
Printable tablePrintable;
public void setTablePrintable(Printable printable) {
tablePrintable = printable;
}
public int print(Graphics graphics, PageFormat pageFormat,
int pageIndex) throws PrinterException {
if (pageIndex > 0) {
return NO_SUCH_PAGE;
}
tablePrintable.print(graphics, pageFormat, pageIndex);
Graphics2D g2d = (Graphics2D)graphics;
g2d.translate(pageFormat.getImageableX(), pageFormat.getImageableY());
// Draw header/footer here
graphics.drawString(header, posx, posy);
return PAGE_EXISTS;
}
}
When you call getPrintable from your JTable, inject it to a new instance to the custom printable and then use this with the PrinterJob.
You can now draw the header and footer as you wish, but you also lose some stuff:
You can't use MessageFormat to format the messages. I believe that you could easily add this functionality to your printable.
Header and footer aren't automatically positioned. You could have rough estimates for these though.
EDIT: I've looked at the Java Sources and there is the private class TablePrintable that does all the job. You can peak at the source code to see how the header and footer are printed. Then you can move this functionality to your Printable class.
This code is mainly from www.java2s.com with changes to show how to change the color. It is not the prettiest solution but hopefully will help you.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.border.Border;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;
import javax.swing.table.TableColumnModel;
public class MainClass {
public static void main(String args[]) {
String rows[][] = { { "A", "a" }, { "B", "b" }, { "E", "e" } };
String headers[] = { "Upper", "Lower" };
JFrame frame = new JFrame("Label Header");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JTable table = new JTable(rows, headers);
JScrollPane scrollPane = new JScrollPane(table);
Border headerBorder = UIManager.getBorder("TableHeader.cellBorder");
JLabel headerLabel1 = new JLabel(headers[0], JLabel.CENTER);
headerLabel1.setBorder(headerBorder);
// Here is where the color is changed.
headerLabel1.setBackground(new Color(255, 0, 0));
headerLabel1.setForeground(new Color(0, 0, 255));
// End of color change.
JLabel headerLabel2 = new JLabel(headers[1], JLabel.CENTER);
headerLabel2.setBorder(headerBorder);
TableCellRenderer renderer = new JComponentTableCellRenderer();
TableColumnModel columnModel = table.getColumnModel();
TableColumn column0 = columnModel.getColumn(0);
TableColumn column1 = columnModel.getColumn(1);
column0.setHeaderRenderer(renderer);
column0.setHeaderValue(headerLabel1);
column1.setHeaderRenderer(renderer);
column1.setHeaderValue(headerLabel2);
frame.add(scrollPane, BorderLayout.CENTER);
frame.setSize(300, 150);
frame.setVisible(true);
}
}
class JComponentTableCellRenderer implements TableCellRenderer {
public Component getTableCellRendererComponent(JTable table, Object value,
boolean isSelected, boolean hasFocus, int row, int column) {
return (JComponent)value;
}
}
The most important bit is:
// Here is where the color is changed.
headerLabel1.setBackground(new Color(255, 0, 0));
headerLabel1.setForeground(new Color(0, 0, 255));
// End of color change.
HTH let me know how you got on with it :)
This is a little out of left-field, but you could use a library like iText to create a PDF document, and then send that to the printer. You'll get a much greater level of control over the appearance and presentation of the document, including the ability to manage how the rows of your table break over page boundaries, and you'll be able to set the page header and footer very readily.
You can print a PDF in Java following these instructions: http://www.juixe.com/techknow/index.php/2008/01/17/print-a-pdf-document-in-java/
It's easy:
JTable table = new JTable();
((DefaultTableCellRenderer)table.getTableHeader().getColumnModel().getColumn(4).getHeaderRenderer()).setForeground(Color.ORANGE);
Related
I customized a Jtable headers with a TableCellRenderer so filter icon would appear, and as the user clicks on the filter icon, a pop-up filter like excel's appears (picture 1). However, if the user clicks on the text of the header, a row sorter is appliead. When the row sorter is applied (picture 2), it override the customization and filter icon disappears. Is there a way to avoid this behavior while keeping the row sorter on the table header?
You can do this by decorating the original renderer of the table header with a custom one which will add the desired icon of filtering.
For example, follows some code to preserve the sort icons while adding your own label next to the original column header:
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.util.Objects;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellRenderer;
public class Main {
private static class MyTableCellRenderer extends DefaultTableCellRenderer {
private final TableCellRenderer originalRenderer;
public MyTableCellRenderer(final TableCellRenderer originalRenderer) {
this.originalRenderer = Objects.requireNonNull(originalRenderer);
}
#Override
public Component getTableCellRendererComponent(final JTable table,
final Object value,
final boolean isSelected,
final boolean hasFocus,
final int row,
final int column) {
final Component original = originalRenderer.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if (row >= 0) //The header will have a row equal to -1.
return original;
final JPanel container = new JPanel(new FlowLayout(FlowLayout.CENTER, 10, 0));
final JLabel filtering = new JLabel("Filter", JLabel.CENTER); //Your icon should go in this label...
//Put some fancy background to make the positioning of the label clear:
filtering.setOpaque(true);
filtering.setBackground(Color.CYAN);
//The default renderer comes with a border... Let's apply the border to the whole new container:
if (original instanceof JComponent) {
container.setBorder(((JComponent) original).getBorder());
((JComponent) original).setBorder(null);
}
container.add(filtering);
container.add(original);
return container;
}
}
private static void createAndShowGUI() {
final JTable table = new JTable(new Object[][] {
new Object[]{"Data001", "Data002", "Data003"},
new Object[]{"Data011", "Data012", "Data013"},
new Object[]{"Data021", "Data022", "Data023"},
new Object[]{"Data031", "Data032", "Data033"},
new Object[]{"Data041", "Data042", "Data043"}
}, new Object[] {"Column1", "Column2", "Column3"});
table.setAutoCreateRowSorter(true);
final JTableHeader header = table.getTableHeader();
header.setDefaultRenderer(new MyTableCellRenderer(header.getDefaultRenderer()));
final JFrame frame = new JFrame("Table header");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(Main::createAndShowGUI);
}
}
Instead of the word Filter (which appears on each column header) you can use your icons on the created label instead.
References:
Source (see the section: 3. Keeping sort icons).
How can I put a control in the JTableHeader of a JTable?
This is an example of a JList. I know how to set a border to the entire JList or the panel containing the JList. My question is that how can we create line borders between the cells?`
For example in the given picture the color of the line border between the first cell and the second is grey, while there is a white colored line border between all the other cells.
Please don't say that this is a JComboBox or a JTree (because of the second element and its two children); even if it is not a JList, I want my JList to have similar LineBorders between the cells.
My web search led me to this interface. Its method getListCellRendererComponent takes arguments like E value, int index, boolean isSelected, boolean cellHasFocus, while I want the LineBorder to appear between all the cells no matter what index they have and whether they are selected or not etc.
EDIT:-
The border is thicker in some places and fine in others.
You can achive that with help of ListCellRenderer. Here is simple example:
import java.awt.Color;
import java.awt.Component;
import javax.swing.BorderFactory;
import javax.swing.DefaultListCellRenderer;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JList;
import javax.swing.ListCellRenderer;
public class TestFrame extends JFrame{
public TestFrame(){
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
init();
pack();
setVisible(true);
}
private void init() {
JList<String> list = new JList<>(new String[]{"1","2","3"});
list.setCellRenderer(getRenderer());
add(list);
}
private ListCellRenderer<? super String> getRenderer() {
return new DefaultListCellRenderer(){
#Override
public Component getListCellRendererComponent(JList<?> list,
Object value, int index, boolean isSelected,
boolean cellHasFocus) {
JLabel listCellRendererComponent = (JLabel) super.getListCellRendererComponent(list, value, index, isSelected,cellHasFocus);
listCellRendererComponent.setBorder(BorderFactory.createMatteBorder(0, 0, 1, 0,Color.BLACK));
return listCellRendererComponent;
}
};
}
public static void main(String... strings) {
new TestFrame();
}
}
Looks like:
Read more in tutorial.
EDIT: Just change LineBorder to MatteBorder(have changed code and image).
I have been trying to find a solution for this for the past few days and its driving me crazy. I have a table in which I set the selection colour to yellow. I also set the background of the cell editor component to yellow so that the colour remains the same when the cell is being edited. I do that by overriding the prepareEditor method as such:
#Override
public Component prepareEditor(TableCellEditor editor, int row, int col) {
Component c = super.prepareEditor(editor, row, col);
c.setBackground(Color.YELLOW);
c.setFont(myFont);
return c;
}
This is working fine for all columns except for a column in which I assign a combo box as the cell editor. Once I start editing a cell in that column the background becomes white. The background colour in the popup menu is Yellow but the background colour in the selected value box remains white. I tried adding a focus listener to the combo box but all that I was able to do was change the background of the popup items and not the background of the selected item. I tried adding the focus listener to the combo box itself as such:
myComboBox.addFocusListener(new FocusListener() {//code here});
and to the editor component as such:
myComboBox.getEditor().getEditorComponent().addFocusListener(new FocusListener() {//code here});
and none of these worked. Can someone please point out what Im doing wrong? Thanks.
You probably need to override the cell renderer. Use this in your UI manager and change the paintComponent method to your liking.
public class MyComboBoxUI extends MetalComboBoxUI {
public MyComboBoxUI() {
}
public static ComponentUI createUI(JComponent c) {
return new MyComboBoxUI();
}
#Override
public void installUI(JComponent c) {
ListCellRenderer rend = new ListCellRenderer() {
public Component getListCellRendererComponent(JList list, Object value, int index, boolean isSelected, boolean cellHasFocus) {
final JLabel renderer = new JLabel(value.toString()) {
protected void paintComponent(java.awt.Graphics g) {
UIDefaults uid = UIManager.getDefaults();
Graphics2D g2d = (Graphics2D)g;
Dimension d = this.getSize();
g2d.setPaint(new GradientPaint(0, 0, Color.red, 0, d.height, Color.orange, true));
g2d.fillRect(0, 0, d.width, d.height);
super.paintComponent(g);
}
};
renderer.setOpaque(false);
return renderer;
}
};
super.installUI(c);
((JComboBox)c).setRenderer(rend);
}
}
I managed to find a solution to the problem and Im posting it here in case anyone faces the same problem. the following is the code:
import java.awt.Color;
import java.awt.Component;
import java.awt.FlowLayout;
import java.awt.event.FocusEvent;
import java.awt.event.FocusListener;
import javax.swing.BorderFactory;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.UIManager;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableColumn;
import org.jdesktop.swingx.autocomplete.AutoCompleteDecorator;
import org.jdesktop.swingx.autocomplete.ComboBoxCellEditor;
public class ComboTest {
JTable table;
JComboBox comboBox;
ComboTest(){
String[] headings = {"Type", "Reference Number", "Amount"};
Object[][] data = {
{"Cash", "123", "2000"},
{"Online", "333", "1200"},
{"Bank Transfer", "667", "800"}
};
comboBox = new JComboBox(new String[] {"Cash", "Cheque", "Bank Transfer", "Credit Card", "Online"});
AutoCompleteDecorator.decorate(comboBox);
JFrame jfrm = new JFrame("Example");
jfrm.getContentPane().setLayout(new FlowLayout());
jfrm.setSize(500, 160);
table = new JTable(data, headings);
table.setSelectionBackground(Color.GREEN);
TableColumn ledgerColumn = table.getColumnModel().getColumn(0);
ledgerColumn.setCellEditor(new ComboBoxCellEditor(comboBox));
//This is the code that changes the colour of the combo Box when it is selected.
comboBox.getEditor().getEditorComponent().addFocusListener(new FocusListener() {
public void focusGained(FocusEvent arg0) {
comboBox.getEditor().getEditorComponent().setBackground(Color.GREEN);
}
public void focusLost(FocusEvent arg0) {
comboBox.getEditor().getEditorComponent().setBackground(Color.WHITE);
}
});
JScrollPane jscrlp = new JScrollPane(table);
jfrm.getContentPane().add(jscrlp);
jfrm.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable () {
public void run() {
new ComboTest();
}
});
}
}
I am trying to make a check where if the value of cell 3 is not blank, the whole row will be colored green, however this does nothing but, if I make no check of that, only checking to be sure nothing is selected (so that selecting gives the color to show that you did select) everything is drawn green.
if (!table.isRowSelected(row))
{
component.setBackground(getBackground());
if(table.getModel().getValueAt(row, 3).equals(""))
{
component.setBackground(Color.GREEN);
}
}
I tried to output the value and everything works properly, is there a problem here? A different way of doing this? thank you
I tried to output the value and everything works properly, is there a
problem here? A different way of doing this?
We would need some more code/info to answer this question properly:
How do you implement the TableCellRenderer? Is it by extending DefaultTableCellRenderer?
How do you set the TableCellRenderer to the JTable?
Is it 3 the correct model column index?
In any case I'd suggest you take a look to Using Custom Renderer section in How to Use Tables trail. Also you can see the example below and take this as start point:
import java.awt.Color;
import java.awt.Component;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.SwingUtilities;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class Demo {
private void initGUI() {
DefaultTableModel model = new DefaultTableModel(new Object[]{"Manufacturer", "Model", "Country", "Price"}, 0);
model.addRow(new Object[]{"Fender", "Stratocaster", "Japan", ""});
model.addRow(new Object[]{"Gibson", "Les Paul", "USA", "$ 1599"});
model.addRow(new Object[]{"Jackson", "Soloist S3", "Japan","$ 1299"});
model.addRow(new Object[]{"Paul Reed Smith","Standard 24", "USA", ""});
JTable table = new JTable(model);
table.setDefaultRenderer(Object.class, new DefaultTableCellRenderer(){
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
if(!isSelected){
Color background = table.getModel().getValueAt(row, 3).equals("") ? Color.GREEN : table.getBackground();
setBackground(background);
} else {
setBackground(table.getSelectionBackground());
}
return this;
}
});
JFrame frame = new JFrame("Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Demo().initGUI();
}
});
}
}
Picture
A different way of doing this?
Check out Table Row Rendering for a different approach that doesn't require you to use a custom renderer.
This approach is easier because you don't need a custom renderer when you have different types of data in each table column.
Is your if statement correct? You have to check for non-blank cell
So, your if statement should look like this
if(!table.getModel().getValueAt(row, 3).equals(""))
Also to check value at cell#3, you have to use index 2 like this.
if(!table.getModel().getValueAt(row, 2).equals(""))
I'm have a custom table header renderer that will have the standard label and a button inside a JComponent.
The issue I'm having is with the label returned by the default renderer. The call to the default renderer provides the standard label. If I return that as is, it looks as expected. If I try to modify the background or border nothing changes. Modifying the foreground does have the intended effect, however. I do not want to view the sort icons, so I'm attempting to construct a JLabel that looks the same, minus the icons. That is not working correctly either. My JLabel is opaque.
JLabel l = (JLabel)table.getTableHeader().getDefaultRenderer().getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
this.lbl.setBackground(l.getBackground());
return this.lbl;
I looked at the source for DefaultTableHeaderRenderer and I can't find anything special that the default class is doing.
I've also tried the following with no effect.
this.lbl.setOpaque(true);
this.lbl.setFont(UIManager.getFont("TableHeader.font"));
this.lbl.setBorder(UIManager.getBorder("TableHeader.cellBorder"));
this.lbl.setBackground(UIManager.getColor("TableHeader.background"));
this.lbl.setForeground(UIManager.getColor("TableHeader.foreground"));
return this.lbl;
EDIT: Clarification. Both code snippets above are inside getTableCellRenderComponent() of my custom renderer. I've tried both ways and neither has worked.
Try using the UIManagers values directly.
TableHeader.background = DerivedColor(color=214,217,223 parent=control offsets=0.0,0.0,0.0,0 pColor=214,217,223
TableHeader.font = javax.swing.plaf.FontUIResource[family=SansSerif,name=sansserif,style=plain,size=12]
TableHeader.foreground = DerivedColor(color=0,0,0 parent=text offsets=0.0,0.0,0.0,0 pColor=0,0,0
TableHeader.opaque = true
Something like UIManager.getColor("TableHeader.background") for example
The border I think you'll find is actually painted by the UI delegate directly.
Updated with example
From the included image, it's obvious that using UIManager does provide some of the basic information need to get the values used by the header, but it does highlight that the renderer is doing some special painting to get the shading.
The second column is the default renderer and the third is cheeky. It is basically stealing the cell renderer directly from the table header...
package testcellrenderer;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTable;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.table.DefaultTableCellRenderer;
import javax.swing.table.DefaultTableModel;
public class TestCellRenderer {
public static void main(String[] args) {
new TestCellRenderer();
}
public TestCellRenderer() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel("com.sun.java.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
DefaultTableModel model = new DefaultTableModel(
new Object[][]{{"Testing", "Testing", "Testing"}},
new Object[]{"Test A", "Test B", "Test C"}
);
JTable table = new JTable(model);
table.getColumn("Test A").setCellRenderer(new TestTableCellRenderer());
table.getColumn("Test C").setCellRenderer(table.getTableHeader().getDefaultRenderer());
setLayout(new BorderLayout());
add(new JScrollPane(table));
}
}
protected class TestTableCellRenderer extends DefaultTableCellRenderer {
#Override
public Component getTableCellRendererComponent(JTable table, Object value, boolean isSelected, boolean hasFocus, int row, int column) {
super.getTableCellRendererComponent(table, value, isSelected, hasFocus, row, column);
Color background = UIManager.getColor("TableHeader.background");
Color foreground = UIManager.getColor("TableHeader.foreground");
Font font = UIManager.getFont("TableHeader.font");
boolean opaque = UIManager.getBoolean("TableHeader.opaque");
setBackground(background);
setForeground(foreground);
setFont(font);
setOpaque(opaque);
return this;
}
}
}