I am trying to color the Border of a JTextField red and then change it back to "normal" later on. When I am using Linux (furthermore Ubuntu), the initial Border differs from the Border that you get by using UIManager.getBorder("TextField.border"); one of them is a SynthBorder and one is a FieldBorder. The "correct" one would be the SynthBorder.
SSCCE:
import javax.swing.BorderFactory;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Main
{
private static boolean switched;
public static void main( final String[] args )
throws ClassNotFoundException, InstantiationException, IllegalAccessException, UnsupportedLookAndFeelException
{
UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() );
JFrame frame = new JFrame( "Test border change" );
frame.getContentPane().setLayout( new BoxLayout( frame.getContentPane(), BoxLayout.LINE_AXIS ) );
JTextField tf = new JTextField();
JButton button = new JButton( "Switch" );
button.addActionListener( action ->
{
if ( switched )
{
tf.setBorder( UIManager.getBorder( "TextField.border" ) );
switched = !switched;
}
else
{
tf.setBorder( BorderFactory.createLineBorder( Color.RED ) );
switched = !switched;
}
} );
frame.getContentPane().add( tf );
frame.getContentPane().add( button );
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.pack();
frame.setVisible( true );
}
}
I have already tried:
using JComponent.updateUI() (no effect)
nulling the border (ruins the layout)
preserving it (not a proper way)
Does anyone have a better idea?
You can get the default border in the UIManager with this code:
jTextField2.setBorder(UIManager.getLookAndFeel().getDefaults().getBorder("TextField.border"));
When you replace the Border try using:
Border uiBorder = BorderUIResource( BorderFactory.createLineBorder( Color.RED ) );
tf.setBorder( uiBorder );
When you use any wrapper class with "UIResource" this tell the LAF the component is part of the LAF and not a custom implementation
Then to restore the Border:
SwingUtilities.updateComponentTreeUI( tf );
Hopefully this will fake the UI into resetting the LAF properties, specifically the Border.
Read the section from the Swing tutorial on How to Set the LAF for more information.
Of course this is not as efficient as simply saving the Border and resetting it as all properties of the text field will be updated bye the updatComponentTreeUI(...) (if this works).
Still don't see why you can't save the Border. You could use the putClientProperty(...) method of the JComponent class to save the Border and then restore it using the getClientProperty(...) method.
You could even automate this by using adding a PropertyChangeListener to listen for a change in the border. When an event is generated if the getClientProperty(...) returns null, then you save the old value from the PropertyChangeEvent.
yourJTextField.setBorder(new JTextField().getBorder());
You can get the border right after creating the component to save it, and set it again later.
Border defaultBorder = tf.getBorder();
...
tf.setBorder(defaultBorder);
Related
I was wondering whether the way I expect the JFormattedTextField to work in combination with a default button is correct. When editing the value of a JFormattedTextField, you usually want to commit the value and then use it, this usually happens on focusLost or sometimes when the ActionListener is manually triggered. Now, if such a text field is inside of a JDialog which has a DefaultButton defined, triggering the default button with Ctrl+Enter will not cause the currently focused JFormattedTextField to trigger it's focusLost event.
Now, I see two solutions for this:
Manually make sure all inupt fields are in a comitted (or reverted) state
Use a SwingUtilities.InvokeLater in the ActionListener of the default button
However, both of these seem dirty, especially the first one, as it's not very easy to extend said dialog without forgetting to handle the components. The second one is rather undesirable imo because more invokeLaters usually mean more code that's harder to debug.
Is there a better way of solving this?
EXAMPLE:
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class DefaultButton
{
public static void main( String[] args )
{
JFrame frame = new JFrame();
JFormattedTextField field = new JFormattedTextField();
field.setValue( "HELLO" );
JButton defaultButton = new JButton( "Default" );
defaultButton.addActionListener( __ ->
{
JOptionPane.showMessageDialog( frame, "You've chosen: " + field.getValue() );
} );
frame.getRootPane().setDefaultButton( defaultButton );
JPanel panel = new JPanel( new BorderLayout() );
panel.add( field, BorderLayout.NORTH );
panel.add( defaultButton, BorderLayout.SOUTH );
frame.add( panel );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
frame.setVisible( true );
}
}
Yes, when the JFormattedTextField has input focus and you activate the defaultButton, the JFormattedTextField does not lose focus and therefore the value is not committed.
Refer to How to Use Formatted Text Fields.
Here is a quote:
A formatted text field's text and its value are two different properties, and the value often lags behind the text.
The text property is defined by the JTextField class. This property always reflects what the field displays. The value property, defined by the JFormattedTextField class, might not reflect the latest text displayed in the field. While the user is typing, the text property changes, but the value property does not change until the changes are committed.
I see two possible solutions (although I imagine there are more).
In the actionPerformed() method, either call getText() rather than getValue(), or call commitEdit() before calling getValue(), i.e.
JOptionPane.showMessageDialog( frame, "You've chosen: " + field.getText() );
or
try {
field.commitEdit();
}
catch (java.text.ParseException x) {
x.printStackTrace();
}
JOptionPane.showMessageDialog(frame, "You've chosen: " + field.getValue());
EDIT
I believe this is what you are looking for.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.KeyboardFocusManager;
import javax.swing.JButton;
import javax.swing.JFormattedTextField;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class DefaultButton
{
public static void main( String[] args )
{
JFrame frame = new JFrame();
JFormattedTextField field = new JFormattedTextField();
field.setValue( "HELLO" );
JButton defaultButton = new JButton( "Default" );
defaultButton.addActionListener( __ ->
{
KeyboardFocusManager kfm = KeyboardFocusManager.getCurrentKeyboardFocusManager();
if (kfm.getFocusOwner() == field) {
kfm.focusNextComponent();
}
EventQueue.invokeLater(() -> JOptionPane.showMessageDialog( frame, "You've chosen: " + field.getValue() ));
} );
frame.getRootPane().setDefaultButton( defaultButton );
JPanel panel = new JPanel( new BorderLayout() );
panel.add( field, BorderLayout.NORTH );
panel.add( defaultButton, BorderLayout.SOUTH );
frame.add( panel );
frame.pack();
frame.setLocationRelativeTo( null );
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE );
frame.setVisible( true );
}
}
Concentrate on method actionPerformed(). If the JFormattedTextField currently has the focus then force the focus to move to the next component. Regardless of which component has the keyboard focus, wrap the displaying of the JOptionPane in a invokeLater() call.
I am very new to using swing for java development, and only had experience with coding in java and never creating a GUI. Done some reading and made a decision to go with Swing.
Now i need to setup my application window, i would like the window to be a particular color, with 2 bands of another color along to top and bottom, with additional areas within the window to be another color again.
Can anyone give me some tips on how i would go about this?
Thanks
Almost always you will want to have a BorderLayout as a first step in representing your application.
You can create JPanels, set their background colors, and then with BorderLayout add them North and South.
Make sure you set minimum height on the JPanel to something or you won't see anything. If you want two JPanels on top of each other (on the top and bottom), then you could embed a Borderlayout in the north, and one in the south, each one have a panel north, and one south.
As long as the height has been defined correctly for the JPanel then it should work.
As already stated, you can create a tree-like hierarchy of panels (components) that each set their background color to whatever you want.
The downside with this approach is that it will be more difficult to place components over this tree.
However, you can always override JPanel paintComponent to paint whatever you want.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Test extends JFrame
{
public Test()
{
super( "Test" );
JPanel mainPanel = new BackgroundPanel();
mainPanel.setPreferredSize( new Dimension( 400, 300 ) );
getContentPane().add( mainPanel );
pack();
setLocationRelativeTo( null );
setDefaultCloseOperation( EXIT_ON_CLOSE );
setVisible( true );
}
public static void main( String[] args )
{
SwingUtilities.invokeLater( new Runnable()
{
#Override
public void run()
{
new Test();
}
});
}
}
class BackgroundPanel extends JPanel
{
#Override
protected void paintComponent( Graphics g )
{
g.setColor( Color.RED );
g.fillRect( 0, 0, getWidth(), 20 );
g.setColor( Color.BLUE );
g.fillRect( 0, getHeight() - 20, getWidth(), 20 );
}
}
In this case you can add components to the main panel as usual.
Background Information
I would like to build a tool/palette window (also called a "floating" window) in Swing with Java Version 1.6.0_26. I thought a JWindow is the best pick, and also the Swing documentation points out to use a JWindow for such purposes (floating window, which has an owner frame, has no decoration and no windows taskbar entry).
My floating tool window's content consists of several other components like JButtons and also JTextFields.
The problem
When I click into the floating tool window, the owner window (the JFrame, my "main application window") "flickers" occasionally. The "flicker" looks like the owner window is losing the focus for a few milliseconds and than gets the focus back, resulting in a very fast disabling/enabling of the window (note that no window event is fired, like focus-lost or window-deactivated).
I have tested this under Windows 7 64 Bit and Windows XP.
Video and example code
To clarify the problem (it is a bit difficult to explain), I've taken a video, there you can see the "flicker" of the owner window while I click onto the floating tool window repeatedly:
http://goo.gl/wVEqJ (.MOV video format, I think you need Quicktime to play it)
I also put together a simple example code to reproduce the problem (this code is used in the video):
import java.awt.*;
import javax.swing.*;
public class JWindowFlickerExample
{
public JWindowFlickerExample()
{
// I know swing code should be executed on the EDT,
// just wanted to keep it simple
// Create and show the "main application window"
JFrame frame = new JFrame( getClass().getSimpleName() );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.setSize( 640, 480 );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
// Create and show the "floating tool window"
MyFloatingToolWindow testWindow = new MyFloatingToolWindow( frame );
testWindow.setLocation( 400, 400 );
testWindow.setVisible( true );
}
public static void main( String[] args )
{
new JWindowFlickerExample();
}
#SuppressWarnings( "serial" )
private class MyFloatingToolWindow extends JWindow
{
public MyFloatingToolWindow( Window hostWindow )
{
super( hostWindow );
// setFocusableWindowState( false );
setSize( 300, 400 );
setLayout( null );
getContentPane().setBackground( Color.LIGHT_GRAY );
JTextField textField = new JTextField();
textField.setLocation( 50, 50 );
textField.setSize( 70, 30 );
add( textField );
}
}
}
Progress so far
I also tried to set "Window.setFocusableWindowState" to false for the floating tool window. If it is false, there is no "flicker", the problem is gone. The JavaDoc for that method points out:
Setting a Window's focusability state to false is the standard mechanism for an application to identify to the AWT a Window which will be used as a floating palette or toolbar, and thus should be a non-focusable Window."
But then I can't use a JTextField in the floating tool window of course, because I can't focus it (maybe a text field in a floating tool window is unusual, but in my case a must).
I guess the "flicker" effect has something to do with the focus management in some way... for the fraction of a second the floating tool window gets the focus, taking it away from the owner window and then back. But I'm not sure; as a side note: if the text field in the floating tool window has the focus, the owner window stays enabled (which is a correct behavior).
I hope there is an easy solution, one so that I can stay with the JWindow as my floating tool window and with text fields as it's content - because, apart from the described "flicker" problem, everything works great.
I really appreciate any help, thanks a lot!
Does this variant of the code show the same problem? (Note: I did not see any noticeable flicker before I started changing it.)
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class JWindowFlickerExample
{
public JWindowFlickerExample()
{
// I know swing code should be executed on the EDT,
// just wanted to keep it simple
// SOMETIMES 'KEEPING IT SIMPLE' CAN CAUSE THE PROBLEM!
SwingUtilities.invokeLater( new Runnable() {
public void run() {
// Create and show the "main application window"
JFrame frame = new JFrame( getClass().getSimpleName() );
frame.setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
frame.pack();
frame.setSize( 640, 480 );
frame.setLocationRelativeTo( null );
frame.setVisible( true );
// Create and show the "floating tool window"
MyFloatingToolWindow testWindow = new MyFloatingToolWindow( frame );
testWindow.setLocation( 400, 400 );
testWindow.setVisible( true );
}
});
}
public static void main( String[] args )
{
new JWindowFlickerExample();
}
#SuppressWarnings( "serial" )
private class MyFloatingToolWindow extends JWindow
{
public MyFloatingToolWindow( Window hostWindow )
{
super( hostWindow );
JTextField textField = new JTextField(20);
JPanel p = new JPanel(new GridLayout());
p.setBackground( Color.GREEN );
p.setBorder(new EmptyBorder(40,40,40,40));
p.add(textField);
add( p );
pack();
}
}
}
I think the bug at http://bugs.sun.com/view_bug.do?bug_id=4109702 may be related.
Anyway, here's a fix that seems to remove the flicker for me (Java 1.6 on Windows XP):
window = new JWindow(parentFrame);
window.setFocusableWindowState(false);
window.addComponentListener(new ComponentAdapter() {
#Override
public void componentShown(ComponentEvent e) {
window.setFocusableWindowState(true);
// Putting the focus on the content pane means that the first
// visible component isn't focused, but if the user tabs, they
// will get to it.
window.getContentPane().requestFocus();
}
#Override
public void componentHidden(ComponentEvent e) {
window.setFocusableWindowState(false);
}
});
The trick seems to be to have the window not be focusable at the time it is shown.
The knob on vertical JSlider's on my Windows 7 machine (with native look-and-feel) is really, really tiny in both directions. Not just skinny but short as well.
Can anyone confirm this? Should I report it? If so, where? Thanks!
Here is the code for the sample program (in the screen shot):
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
public class SliderTest
{
public static void main( String[] args )
{
// Set the look and feel to that of the system
try
{ UIManager.setLookAndFeel( UIManager.getSystemLookAndFeelClassName() ); }
catch ( Exception e )
{ System.err.println( e ); }
// Launch the GUI from the event dispatch thread
javax.swing.SwingUtilities.invokeLater( new Runnable()
{
public void run ()
{
JFrame window = new JFrame();
window.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
JPanel contentPane = new JPanel();
contentPane.add( new JSlider(SwingConstants.HORIZONTAL) );
contentPane.add( new JSlider(SwingConstants.VERTICAL) );
window.setContentPane( contentPane );
window.pack();
window.setLocationRelativeTo( null ); // Center window
window.setVisible( true );
}
});
}
}
First off, this happens in Windows Vista too. It seems to be the case, that the slider tries to take as little space as possible. If you want a bigger JSlider use JSlider.setPaintTicks. So you have to add the following:
JSlider vertical = new JSlider( SwingConstants.VERTICAL );
vertical.setPaintTicks( true );
contentPane.add( vertical );
That should do the trick.
I want to maximize a JPanel inside a JFrame when the user clicks a button.What is the best way to achieve this.The view and the data model should be in sync in both the panels,that is the panel which in the JFrame and the maximized one.Please suggest me some solution.
my requirement is: i have a JFrame with 4 JPanels named as
JPanelA,JPanelB,JPanelC,JPanelD
Here the JPanelD contains a JList and
a button below it say "MAXIMIZE
PANEL" button . JList has a JTree
with in it . Sometimes the JList may
have huge set of data and it is not
visible to the user clearly.
So he need to maximize this JPanelD alone to see the contents of the JList clearly.For that he clicks "MAXIMIZE PANEL" button.After the click action ,the JPanelD in the JFrame remains there,also a new JPanel with the same JList data(ie.,the replica of the JPanelD say JPanelDMaximized)should be popped up.This is what i want to do ..
Of course you could do this yourself, but you should really look at JInternalFrame and consider using that for your panel. It will save a bunch of headache.
Edit: Sun's tutorial should get you what you need.
Follow-up to your clarification of the problem:
Take my code, and remove:
maximizedFrame.setUndecorated( true );
and size the frame bigger before you make it visible. That should satisfy the maximize-like behaviour you need.
Your other problem is that you cannot add JPanelD to the two JFrames. The pop-up frame must have its own unique JPanel object (let's call it JPanelE). So you need to:
Initialize and lay out JPanelE like you do JPanelD. That means giving JPanelE its own JList (and JTree, and so on).
Share the ListModel from JPanelD's JList with JPanelE's JList, and so on. The feasibility and details of executing this successfully depends on the specifics of your implementation, and is beyond the scope of your original problem.
Create a JWindow (or an undecorated JFrame) with a JPanel. Leave the JWindow invisible, initially. (The wiring of this new JPanel to the same data model used by the original JPanel is left as an exercise.)
When your maximize-panel button's ActionListener executes, it must:
2.1. Update the (invisible) JWindow's location and size to match the (visible) JFrame's.
2.2. Make your JFrame invisible.
2.3. Make your JWindow visible.
When your unmaximize-panel button's ActionListener executes, it must:
3.1. Update the (invisible) JFrame's location and size to match the (visible) JWindow's.
3.2. Make your JWindow invisible.
3.3. Make your JFrame visible
Example:
package stackoverflow;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.HeadlessException;
import java.awt.event.ActionEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MaximizingPanelApp extends JFrame {
private JPanel framePanel;
private JPanel windowPanel;
private JFrame maximizedFrame;
public static void main(String[] args) {
JFrame appFrame = new MaximizingPanelApp();
appFrame.setVisible( true );
}
public MaximizingPanelApp() throws HeadlessException {
super( "Application" );
setDefaultCloseOperation( JFrame.DISPOSE_ON_CLOSE );
initialize();
}
private void initialize() {
// JFrame
{
Container container = getContentPane();
container.setLayout( new BorderLayout() );
framePanel = new JPanel();
framePanel.setBackground( Color.ORANGE );
container.add( framePanel, BorderLayout.CENTER );
JButton button = new JButton( new MaximizeAction() );
container.add( button, BorderLayout.SOUTH );
setSize( 400, 300 );
}
// JWindow
{
maximizedFrame = new JFrame();
Container container = maximizedFrame.getContentPane();
container.setLayout( new BorderLayout() );
windowPanel = new JPanel();
windowPanel.setBackground( Color.ORANGE );
container.add( windowPanel, BorderLayout.CENTER );
JButton button = new JButton( new UnMaximizeAction() );
container.add( button, BorderLayout.SOUTH );
maximizedFrame.setSize( getSize() );
maximizedFrame.setUndecorated( true );
}
}
private class MaximizeAction extends AbstractAction {
private MaximizeAction() {
super( "Maximize" );
}
public void actionPerformed(ActionEvent e) {
maximizedFrame.setSize( getSize() );
maximizedFrame.setLocation( getLocation() );
setVisible( false );
maximizedFrame.setVisible( true );
}
}
private class UnMaximizeAction extends AbstractAction {
private UnMaximizeAction() {
super( "Un-Maximize" );
}
public void actionPerformed(ActionEvent e) {
setLocation( maximizedFrame.getLocation() );
setSize( maximizedFrame.getSize() );
maximizedFrame.setVisible( false );
maximizedFrame.dispose();
setVisible( true );
}
}
}
This depends on the layout manager you use. If you add a JPanel to a JFrame using the default layout manager, and the JFrame only contains the JPanel and nothing else, you'll achieve what you describe.
Here's an example. The JPanel is green; notice how it resizes as you resize the JFrame.
import javax.swing.*;
import java.awt.*;
public class ScratchSpace {
public static void main(String[] args) {
JFrame frame = new JFrame("Stretchy panel demo");
final JPanel panel = new JPanel();
panel.setOpaque(true);
panel.setBackground(Color.GREEN);
panel.setPreferredSize(new Dimension(600, 400));
final JComponent contentPane = (JComponent) frame.getContentPane();
contentPane.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
frame.getContentPane().add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}