Nimbus - override color for TableHeader - java

I would like to override the background color of headers in JTables when using the Nimbus L&F. I'm effectively "theming" the Nimbus L&F, i.e. making small adjustments to it.
Whatever I try it doesn't seem to have effect.
Here's an SSCCS :
public class MyTest {
public static void main(String[] args) {
new MyTest();
}
public MyTest() {
try {
UIManager.setLookAndFeel("javax.swing.plaf.nimbus.NimbusLookAndFeel");
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
Logger.getLogger(MyTest.class.getName()).log(Level.SEVERE, null, ex);
}
UIManager.put("TableHeader.background", Color.RED);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
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[][]{
{"hhvt ", "er sdf", "sfdg"},
{"hyshg ", "dh sdf", "jer"}
},
new Object[]{"Col A", "Col B", "Col C"}
);
JTable table = new JTable(model);
setLayout(new BorderLayout());
add(new JScrollPane(table));
}
}
}
Here's the result:
I'm well aware that Nimbus is a Synth L&F so it uses Painters for just about anything. I bet I could override some Painter in UIManager but I don't want to redo a Painter from scratch. The Painters in Nimbus are quite advanced, they use gradients and what have you. I would like to take advantage of that. It's just the color I would like to change.

Here's a possible - but quite ugly - solution.
Nimbus relies heavily on Painters. The reason why Nimbus looks good is because it uses gradients, shadows and what not. That's the job of the Painter. We really, really don't want to do our own Painters. The Nimbus Painters are quite complex and produce beautiful results. So we want to leverage them. Not do them ourselves!
Nimbus has a lot of auto-generated source code. All source code is generated off the skin.laf XML file (which is in the JDK source) but the XML file is not used at runtime. Most of the auto-generated source files are in fact type-specific Painters. For example there's a painter class for TableHeaderRendererPainter (a painter responsible for painting table headers) and so on. The problem is that all auto-generated source code is package-private.
Painters are set when an instance of NimbusLookAndFeel is initialized. They don't change after this.
From skin.laf file we can see what colors are used for what. In our case we can see it's really the nimbusBlueGrey color that governs the background color of the table headers. We can't just change the value of nimbusBlueGrey as that would affect everything in Nimbus that uses this color. So we need to come up with something else. And this is where it gets ugly.
In the specific case we're interested in table headers as they look by default (i.e. when the mouse is not over them, the table is not disabled, the column header is not pressed, etc). So this is what we'll concentrate on below. But the technique would be the same for any other type of special decoration that somebody would want to do.
The technique is to first start up a temporary instance of the NimbusLookAndFeel. We do this only so we can 'steal' one of the Painters it has generated. We than safe keep this Painter and then start the NimbusLookAndFeel for real. Now we can replace our specific Painter so that we swap in the one we saved previously.
public class MyTest {
public static void main(String[] args) throws UnsupportedLookAndFeelException {
new MyTest();
}
public MyTest() throws UnsupportedLookAndFeelException {
// Start dummy instance of L&F
NimbusLookAndFeel nimbusTmp = new NimbusLookAndFeel();
Object nimbusBlueGreyOrg = UIManager.get("nimbusBlueGrey"); // original value
UIManager.put("nimbusBlueGrey", Color.RED); // the color we want
try {
UIManager.setLookAndFeel(nimbusTmp);
} catch (UnsupportedLookAndFeelException ex) {
Logger.getLogger(MyTest.class.getName()).log(Level.SEVERE, null, ex);
}
Object painter = UIManager.get("TableHeader:\"TableHeader.renderer\"[Enabled].backgroundPainter");
// We've got what we came for. Now unload the dummy.
UIManager.getLookAndFeel().uninitialize(); // important to avoid UIDefaults change listeners firing
UIManager.put("nimbusBlueGrey", nimbusBlueGreyOrg); // revert
// Load the L&F for real.
UIManager.setLookAndFeel(new NimbusLookAndFeel());
// Swap in the value we saved previously
UIManager.put("TableHeader:\"TableHeader.renderer\"[Enabled].backgroundPainter", painter);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
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[][]{
{"hhvt ", "er sdf", "sfdg"},
{"hyshg ", "dh sdf", "jer"}},
new Object[]{"Col A", "Col B", "Col C"}
);
JTable table = new JTable(model);
setLayout(new BorderLayout());
add(new JScrollPane(table));
}
}
}
Not proud of this, but it works. Anyone with better ideas ?

Related

java scrolling while dragging

So I have several problems:
1) Is this code OK ? Or can it be written better ? (I will have an array with pictures in the final version)
2) When I click Next button the rectangle that I drew on first picture stays on the second one, how to clear it ? So after pressing Next button there is no rectangle on the new picture ?
3) I want to be able to auto-scroll while I drag the mouse button (while drawing the rectangle), but it's not really working...
Please help
public class SelectionExample {
static public TestPane panelek;
static public BufferedImage tempimage;
public static void main(String[] args) {
new SelectionExample();
}
public SelectionExample() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
panelek = new TestPane();
panelek.setPreferredSize(new Dimension(100, 100));
panelek.setAutoscrolls(true);
JScrollPane scrollPane = new JScrollPane(panelek);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
frame.add(scrollPane, BorderLayout.CENTER);
JButton next = new JButton("NEXT");
frame.add(next, BorderLayout.PAGE_START);
try {
tempimage = ImageIO.read(new File("D:/test/temp1.jpg"));
} catch (IOException ex) {
Logger.getLogger(SelectionExample.class.getName()).log(Level.SEVERE, null, ex);
}
next.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
try {
tempimage = ImageIO.read(new File("D:/test/temp2.jpg"));
} catch (IOException ex) {
Logger.getLogger(SelectionExample.class.getName()).log(Level.SEVERE, null, ex);
}
panelek.repaint();
}
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Is this code OK ? Or can it be written better ?
Don't use static variables. If you want to change the image on your panel then you need to create a setImage(...) method in your class. The method will then save the image and invoke repaint(). That is classes should be responsible for managing their properties and provide getter/setter methods.
The formatting of your code is terrible and therefore difficult to read. Use either tabs or spaces for code indentation and be consistent.
I want to be able to auto-scroll while I drag the mouse button
Read the API for the setAutoScrolls(...) method of the JComponent class. It provides code for the mouseDragged(...) method of your MouseMotionLister.

Is it possible to retain button's border after changing the Look and Feel? (Java Swing)

I am currently working on a project in which users are allowed to choose a Look and Feel.
However, when users choose another Look and Feel and change it back to the original CrossPlatformLookAndFeel, the borders of buttons disappear.
code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
public class SSCCE {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final JFrame frame = new JFrame();
final JButton button = new JButton("Button");
button.setBorder(LineBorder.createBlackLineBorder());
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent ev) {
try{
UIManager.setLookAndFeel("com.sun.java.swing.plaf.windows.WindowsLookAndFeel");
SwingUtilities.updateComponentTreeUI(frame);
UIManager.setLookAndFeel(UIManager.getCrossPlatformLookAndFeelClassName());
SwingUtilities.updateComponentTreeUI(frame);
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
}
});
//
frame.setLayout(new FlowLayout(FlowLayout.CENTER));
frame.add(button);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
As you see, the border disappears after you click the button.
So the question is: can the border be retained after changing the Look and Feel? I know the border will not appear in WindowsLookAndFeel, but is it possible to "reappear" after the Look and Feel is changed back to the default one?
Last time I checked, there were a variety of bugs in PLAFs that caused these types of odd behavior. Especially when changing from the MetaL LAF (but a good swathe are related to Nimbus as well).
The only reliable way to get the app. to change PLAFs is:
Serialize the user's choice of new PLAF (i.e. as a String of the fully qualified class name).
Launch a new process that calls the main(String[]), which would check for the serialized string of the new PLAF to use, and use it.
(Possibly pass the state of the current GUI to the new GUI.)
Close the current GUI.
As you can see, quite a hassle to get an 'absolutely rock solid reliable' PLAF change.

JComboBox on Windows 7 has rendering artifacts

When I use a JComboBox on Windows 7, the four corners each have a pixel that doesn't match the background colour of the parent component.
In Windows 8 this problem doesn't happen (although that could be because in Windows 8, the JComboBox is rendered as a perfect rectangle). Nor does it happen on OS X.
What can I do to make the corner pixels let the background colour of the parent component through?
Here's an image showing the problem:
Here's a self-contained code example I'm using:
import com.sun.java.swing.plaf.windows.WindowsLookAndFeel;
import javax.swing.*;
import java.awt.*;
public class Main {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
UIManager.setLookAndFeel(new WindowsLookAndFeel());
} catch (Exception e) {
e.printStackTrace();
}
JPanel contentPane = new JPanel();
contentPane.setBackground(Color.WHITE);
JComboBox<String> comboBox = new JComboBox<String>(new String[]{"One", "Two"});
contentPane.add(comboBox);
JFrame frame = new JFrame("JComboBox Test");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
Try removing the border...
comboBox.setBorder(null);
The next choice would be to design a specialised look and feel delegate that achieved what you wanted on Windows...
For example...
public static class MyComboBoxUI extends WindowsComboBoxUI {
#Override
protected void installDefaults() {
super.installDefaults();
LookAndFeel.uninstallBorder(comboBox);
}
public static ComponentUI createUI(JComponent c) {
return new MyComboBoxUI();
}
}
And then install it using...
UIManager.put("ComboBoxUI", MyComboBoxUI.class.getName());
This will mean you won't need to remove the borders from every combo box you create
Or, you could simply override the default border property in the UIManager...
UIManager.put("ComboBox.border", new EmptyBorder(0, 0, 0, 0));
Either way, it will effect all combo boxes created after you apply it...
First thing I would try is setOpaque(false) on the JComboBox.
Also you should not set the WindowLookAndFeel directly. Instead set the System default L&F:
// force platform native look & feel
try {
final String className = UIManager.getSystemLookAndFeelClassName();
UIManager.setLookAndFeel(className);
} catch (final Exception e) {
// ignore
}
That will always set the OS's default look and feel, regardless of what OS you're running on.

Why don't I see the horizontal scroll bar when I have set the policy to HORIZONTAL_SCROLLBAR_AS_NEEDED

I have added jTable inside the jScrollPane as :
public Scenario_One() {
initComponents();
jScrollPane1.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
jScrollPane1.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED);
}
private void initComponents() {
.
.
jScrollPane1.setViewportView(jTable1);
.
.
}
The text to displayed in one single row is too large so I had to use horizontal scroll bars. But even after setting the policy to as needed I do not see the horizontal scroll bars. What could be the reason for this ?
As #MadProgrammer already commented, the property that controls the table sizing behaviour inside a JScrollPane is the autoResizeMode. Basically, there a two options:
autoResizeOff: the columns are always shown at their prefWidth, showing/hiding the horizontal scrollBar as needed. If their total width is less than the viewport width, the trailing space is left free (showing the viewport)
all the others: the columns are sized such that the total width of the table fits into the scrollPane, the horizontal scrollBar is never shown
The (intuitive) expectation that the table "fits" into the viewport until the columns get too wide to be fully shown and then automatically allow scrolling is not supported in core JTable. JXTable (contained in the SwingX project) provides an additional sizing option for doing it.
Try something like this...
public class TestTableColumns {
public static void main(String[] args) {
new TestTableColumns();
}
public TestTableColumns() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
DefaultTableModel model =new DefaultTableModel(
new Object[][] {
{"1", "2"},
{"11", "21"},
{"12", "22"},
{"13", "23"},
{"14", "24"},
{"15", "25"},
{"16", "26"},
{"17", "27"},
{"18", "28"}},
new Object[] {
"Small", "Big"});
JTable table = new JTable(model);
table.setShowHorizontalLines(true);
table.setShowVerticalLines(true);
table.setShowGrid(true);
table.setGridColor(Color.DARK_GRAY);
table.setAutoResizeMode(JTable.AUTO_RESIZE_OFF);
table.getColumn("Small").setPreferredWidth(100);
table.getColumn("Small").setWidth(12);
table.getColumn("Big").setPreferredWidth(400);
table.getColumn("Big").setWidth(100);
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new JScrollPane(table));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
The width of the view port is dictated by the width of the component it contains. For a JTable, that is determined by the size of the columns and the auto size policy

JButton margins. Not respected when nimbus plaf

The property margin of a JButton isn't respected when the nimbus look and feel is installed. .
I need some "little" buttons, but nimbus forces the space around button text to be large, so I only get "very large" buttons.
I discovered in nimbus defaults page that there is a property called:
Button.contentMargins
that is preset with large values.
I've tryed to override it with the following code:
UIManager.getDefaults().put("Button.contentMargins", new InsetsUIResource(0,0,0,0));
in the main, just after setting the nimbus look and feel.
But nothing happens, the empty space around buttons text still remains large.
Any idea?
Altering the value of the JComponent.sizeVariant may also be effective, as discussed in Resizing a Component.
on base of thread How to change the background color for JPanels with Nimbus Look and Feel? is possible to change and assign one value for something from Nimbus Defaults,
but are you sure that you needed this output to the GUI, nothing nice
v.s. basic JButton with Nimbus L&F
from code
import java.awt.*;
import javax.swing.*;
import javax.swing.plaf.InsetsUIResource;
public class NimbusJPanelBackGround {
public NimbusJPanelBackGround() {
JButton btn = new JButton(" Whatever ");
JButton btn1 = new JButton(" Whatever ");
JPanel p = new JPanel();
p.add(btn);
p.add(btn1);
JFrame f = new JFrame();
f.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
f.add(p, BorderLayout.CENTER);
f.setSize(200, 100);
f.setLocation(150, 150);
f.setVisible(true);
}
public static void main(String[] args) {
try {
for (UIManager.LookAndFeelInfo laf : UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(laf.getName())) {
UIManager.setLookAndFeel(laf.getClassName());
UIManager.getLookAndFeelDefaults().put("Panel.background", Color.white);
UIManager.getLookAndFeelDefaults().put("Button.contentMargins", new InsetsUIResource(0,0,0,0));
}
}
} catch (Exception e) {
e.printStackTrace();
}
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
NimbusJPanelBackGround nimbusJPanelBackGround = new NimbusJPanelBackGround();
}
});
}
}
previously +1 for interesting question

Categories

Resources