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.
Related
my program create a jframe > set the content pane to a jpanel > use repaint() one or more time > set the content pane to another jpanel > use some repaint() again > etc...
(note: for reasons i have to create the jframe then add the jpanel later)
but only calling setContentPane(newjpanel) doesnt allow me to use repaint() afterward and the only thing that i know will unlock it is resizing the window manually
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MyJFrame extends JFrame {
private static final long serialVersionUID = 1L;
public MyJFrame() {
// default panel for visual feedback
JPanel pan = new JPanel();
pan.setBackground(Color.green);
this.setContentPane(pan);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // TODO
this.setSize(400, 400);
// other unrelated configuration stuff
// ...
this.setVisible(true);
}
public void switchPanel(JPanel pan) {
this.setContentPane(pan);
// pan.setVisible(true); doesnt work
// pan.revalidate(); neither
}
public static void main(String[] args) {
MyJFrame frame = new MyJFrame();
//...
JPanel mypan = new MyJPanel();
// there i have an IO operation to get an image, without this delay this code work just fine
// so i put this thread.sleep to simulate this delay
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
frame.switchPanel(mypan);
while (true) {
// this repaint wont work unless i change the window size
mypan.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
}
}
}
public static class MyJPanel extends JPanel {
private static final long serialVersionUID = 1L;
public Point pos = new Point(0, 0);
public synchronized void paintComponent(Graphics g) {
super.paintComponent(g);
// i made the square move for visual feedback
pos.x += 1;
g.fillRect(pos.x, pos.y, 10, 10);
}
}
}
this code should show a square going in direction of +x
instead it wont until the window is resized
another option i know is setting visibility of the jframe to false changing the pane then setting it to true but it is really visualy unpleasant
so my question is, how could i make it possible to add/remove jpanels at run time? (adding code in switchPanel, adding container, really anything)
(2nd note: i would like to if possible REALLY add/remove jpanels at run time, im aware of using for example a card layout and turning off and on panels visibility in it but in cant do that there)
When components are created they have a size of 0, so there is nothing to paint.
When you dynamically add a component to a visible frame, the you need to revalidate() the parent container you add the component to. This will invoke the layout manager on the parent container and the child components will be given a size/location.
Normally this would mean you revalidate() the JPanel you add the component to. However, in this case, since you are replacing the content pane, the easiest way to do that is to revalidate() the frame:
public void switchPanel(JPanel pan) {
this.setContentPane(pan);
revalidate();
}
here's a dirty fix that i found (work both for the mre and my main code)
public void switchPanel(JPanel pan) {
this.setContentPane(pan);
Dimension dim = this.getSize();
int y = dim.height;
int x = dim.width;
this.setSize(x+1,y);
this.setSize(x, y);
}
tho it seem to be the equivalent of shutting down your pc to close a window, so not very appropriate.
I can't get the standard BorderFactory.createEmptyBorder working properly in Java Swing.
I tried it with a simple Java app that creates a Spinner View and declares an empty border around it. The border is shown everywhere except for the right side of the spinner view. but that only happened in Windows, on OSX it works as intended.
To clarify the use of a border: In my real application I have a visible border at the outside of the spinner and then I want to center the text inside. I found the createCompoundBorder property very useful for that.
import javax.swing.*;
import java.awt.*;
import javax.swing.border.Border;
class HelloWorldSwing {
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {}
Runnable guiCreator = new Runnable() {
public void run() {
JFrame fenster = new JFrame("Hallo Welt mit Swing");
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SpinnerNumberModel model = new SpinnerNumberModel(1,1,9,1);
JSpinner spinner = new JSpinner(model);
JFormattedTextField textField = ((JSpinner.DefaultEditor)spinner.getEditor()).getTextField();
textField.setOpaque(true);
// textField.setBackground(Color.);
spinner.setBorder(BorderFactory.createEmptyBorder(50, 100, 50, 100));
spinner.setUI(new javax.swing.plaf.basic.BasicSpinnerUI() {
protected Component createNextButton() {
Component c = new JButton();
c.setPreferredSize(new Dimension(0, 0));
c.setFocusable(false);
return c;
}
protected Component createPreviousButton() {
Component c = new JButton();
c.setPreferredSize(new Dimension(0, 0));
c.setFocusable(false);
return c;
}
});
spinner.setBackground(Color.LIGHT_GRAY);
fenster.add(spinner);
fenster.setSize(300, 200);
fenster.setVisible(true);
}
};
SwingUtilities.invokeLater(guiCreator);
}
}
=
I don't want to find a solution that implements one more UI element. This would be an easy and simple solution (Thanks to Andrew Thompson). The reason is that it is just a small project where I got into this error. In small projects I mostly want a clean and good looking code, which means that such bugs are fixed by trying to fix the broken code and not by doing a workaround.
It looks like a JSpinner is using a custom layout manager and is not handling the Border correctly.
I modified your code to give the buttons the width of the right edge of the border minus 1. I guess the layout manager leaves a 1 pixel gap between the edge of the text field and the button.
Seems to work on Windows, but it might mess up OSX?
import javax.swing.*;
import java.awt.*;
import javax.swing.border.Border;
class HelloWorldSwing {
public static void main(String[] args) {
try {
UIManager.setLookAndFeel(
UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {}
Runnable guiCreator = new Runnable() {
public void run() {
JFrame fenster = new JFrame("Hallo Welt mit Swing");
fenster.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
SpinnerNumberModel model = new SpinnerNumberModel(1,1,9,1);
JSpinner spinner = new JSpinner(model);
JFormattedTextField textField = ((JSpinner.DefaultEditor)spinner.getEditor()).getTextField();
textField.setOpaque(true);
// textField.setBackground(Color.);
spinner.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 0));
spinner.setUI(new javax.swing.plaf.basic.BasicSpinnerUI() {
protected Component createNextButton() {
Component c = new JButton();
c.setPreferredSize(new Dimension(9, 0));
c.setVisible(false);
return c;
}
protected Component createPreviousButton() {
Component c = new JButton();
c.setPreferredSize(new Dimension(9, 0));
c.setVisible(false);
return c;
}
});
spinner.setBackground(Color.LIGHT_GRAY);
fenster.setLayout( new FlowLayout() );
fenster.add(spinner);
fenster.setSize(300, 200);
fenster.setVisible(true);
}
};
SwingUtilities.invokeLater(guiCreator);
}
}
The other solution is to not use a JSpinner. You could easily use a JTextField and write a custom Action for the Up/Down arrows. A little more work but it will work on all platforms.
Also since you seems to be worried about creating extra components this will be far more efficient in that regard. The JSpinner is a complex component itself and the JFormattedTextField is far more complex than a simple JTextField.
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.
I have written the following example code:
import org.jdesktop.swingx.*;
import javax.swing.*;
import java.awt.*;
public class TaskPaneExample{
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new TaskPaneExample();
}});
}
public TaskPaneExample() {
JFrame frame = new JFrame("TaskPane Example 1");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(doInit(), BorderLayout.CENTER);
frame.setLocationRelativeTo(null);
frame.pack();
frame.setVisible(true);
}
private Component doInit() {
JXTaskPaneContainer taskpanecontainer = new JXTaskPaneContainer();
taskpanecontainer.setLayout(new VerticalLayout(2));
final JXTaskPane taskpane1 = new JXTaskPane(){
public void setCollapsed(boolean w){
super.setCollapsed(w);
}};
taskpane1.setTitle("First TaskPane");
JPanel panel1 = new JPanel();
panel1.setBackground(Color.red);
panel1.setSize(100,100);
taskpane1.add(panel1);
taskpanecontainer.add(taskpane1);
JXTaskPane taskpane2 = new JXTaskPane(){
public void setCollapsed(boolean w){
super.setCollapsed(w);
}};
taskpane2.setTitle("My Tasks");
JPanel panel2 = new JPanel();
panel2.setBackground(Color.blue);
panel2.setSize(100,100);
taskpane2.add(panel2);
taskpanecontainer.add(taskpane2);
taskpanecontainer.setBorder(javax.swing.BorderFactory.createEmptyBorder(0, 0, 0, 0));
return taskpanecontainer;
}
}
}
What I need are two things:
how do I change the bgcolor of the title? I think it is done with the taskpane2.setUI(..) option but I had no luck working with it.
how to set the border between the JXTaskPane and the Jpanel to zero?
Originally, the JXTaskPane was designed as a kind of "fixed-properties" component - it should look exactly like the corresponding native component (then of WinXP): custom appearance wasn't meant to be supported. As a consequence, the implementation of the title/border is deeply hidden in the XXTaskPaneUI - actually, everything boils down to be a Border.
1) as dogbane already mentioned, some of the properties can be changed on a per-application basis. Just beware: those are implementation details which might change or not be supported by concrete ui implementations (Nimbus is always a good candidate to not respecting them, even our not-really-synth implementation might not, forgot)
2) the "gap" is the border of the contentPane, you can set your own. Again a beware: might not survive an updateUI (could be that the ui delegates override them unconditionally, if so, please file an issue in the SwingX issuetracker)
((JComponent) taskpane2.getContentPane()).setBorder(BorderFactory.createEmptyBorder());
BTW: those panel.setSize have exactly no effect - layoutManagers rule ;-)
To change the bgcolour of the title, you can try setting the start and end background gradient colours in the UIManager:
UIManager.put("TaskPane.titleBackgroundGradientStart", Colors.White.color());
UIManager.put("TaskPane.titleBackgroundGradientEnd", Color.GREEN);
I also found an open swingx jira task for this: SWINGX-731 Support to define the color to JXTaskPane header.
I am doing slideshow of images program in java using timer.
In timer event listner i have added code to chnage image but image is not changing.
Below is the code i have written
class ImagePanel extends JPanel {
private Image backgroundImage;
public ImagePanel(Image backgroundImage) {
super();
this.backgroundImage = backgroundImage;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(this.backgroundImage, 0, 0, null);
}
}
public class A extends JFrame{
static int counter;
List<String> imagePaths;
int nimgpaths=0;
static A frame = new A();
public static void main(String[] args) {
frame.setSize(1024, 768);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getPath();
/* Getting required image */
Image backgroundImage = null;
String pathToTheImage = "C:\\Documents and Settings\\Administrator\\My Documents\\My Pictures\\civ1.JPG";
try {
backgroundImage = ImageIO.read(new File(pathToTheImage));
} catch (IOException e) {
e.printStackTrace();
}
/* Initializing panel with the our image */
ImagePanel panel = new ImagePanel(backgroundImage);
frame.getContentPane().add(panel);
frame.setVisible(true);
frame.timerEvent();
//frame.show();
}
public void timerEvent(){
Timer timer = new Timer(5000, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("Time event occured");
if(counter > nimgpaths)
counter=0;
String imgPath=imagePaths.get(counter);
Image backgroundImage = null;
try {
backgroundImage = ImageIO.read(new File(imgPath));
}catch (Exception e1) {
e1.printStackTrace();
}
/* Initializing panel with the our image */
frame.removeAll();
ImagePanel panel = new ImagePanel(backgroundImage);
panel.repaint();
//panel.setBackground(backgroundImage);
frame.getContentPane().add(panel);
}
});
timer.start();
}
// To get path of images
public void getPath(){
DbOps db=new DbOps();
imagePaths=db.getPath();
nimgpaths=imagePaths.size();
for(Iterator i=imagePaths.iterator();i.hasNext();){
System.out.println((String)i.next());
}
}
}
Why are you using a custom panel and painting?
Your code is simply painting the image at its preferred size. This functionality is available when you use a JLabel. Then when you use the label all you need to do is use:
label.setIcon(....);
when you want to change the image. Read the section from the Swing tutorial on How to Use Icons for more information.
The only reason to create a custom component is if you plan to scale the image or do something fancy like that. If this is the case then you can use something like the Background Panel which supports scaled images as well as a setImage() method so you can change the image dynamically.
A much better design for ImagePanel would let you just replace the image, rather than removing the component. If you do have to replace a visible component, though, you have to call validate() on its container, or the new one isn't going to show up (most of the time, anyway.) I think that's your problem here.
frame.removeAll() is not doing what you would expect - it is removing the components from the frame itself rather than removing the components from the content pane of the frame. Change the code at the end of the timer's action listener to something like this to fix it:
ImagePanel panel = new ImagePanel(backgroundImage);
frame.getContentPane().removeAll();
frame.getContentPane().add(panel);
frame.getContentPane().invalidate();
frame.getContentPane().validate();
Your concept itself is wrong.
You can refresh the panel like so:
public void refreshPanel(JPanel panel){
panel.removeAll();
panel.invalidate();
panel.validate();
}
Problem:
I see in your code that you are trying to create more than one object of the same panel, which you need to refresh.
It would be better to create one panel object and refresh that object.
ImagePanel panel = new ImagePanel(backgroundImage);
Hope you can understand what I wanted to explain to you.
If you are still confused then let me know.