Java Swing right border not working on Microsoft Windows - java

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.

Related

I am writing a program using awt pakage in java but my components are not getting added

I am writing a program using the AWT package in which I also implemented ActionListner which shows the number of clicks I made on the button.
package awtlistenerdemo;
import java.awt.*;
import java.awt.event.*;
public class AwtListenerDemo implements ActionListener {
TextField t1 = new TextField(30);
Button b;
int count = 0;
Frame f;
AwtListenerDemo() {
f = new Frame("Action Listener Example");
b = new Button("Ok");
f.setLayout(null);
b.setBounds(100, 100, 60, 20);
t1.setBounds(100, 200, 80, 30);
f.add(b);
f.add(t1);
b.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == b) {
count++;
t1.setText("Button Clicked" + count + "Times");
}
}
public static void main(String[] args) {
Frame f = new Frame("Action Listener Example");
f.setVisible(true);
f.setSize(300, 300);
}
}
The main method never constructs an AwtListenerDemo so all you see is the standard, blank frame created in that method. Once that problem is fixed, some of the statements in the main method need to be moved into the constructor and applied to the frame it creates.
This is the result:
import java.awt.*;
import java.awt.event.*;
public class AwtListenerDemo implements ActionListener {
TextField t1 = new TextField(30);
Button b;
int count = 0;
Frame f;
AwtListenerDemo() {
f = new Frame("Action Listener Example");
b = new Button("Ok");
f.setLayout(null);
b.setBounds(100, 100, 60, 20);
t1.setBounds(100, 200, 80, 30);
f.add(b);
f.add(t1);
b.addActionListener(this);
// ADD this!
f.setSize(300,300);
// then set it VISIBLE
f.setVisible(true);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == b) {
count++;
t1.setText("Button Clicked" + count + "Times");
}
}
public static void main(String[] args) {
// Change the main code to..
new AwtListenerDemo();
}
}
General Tips
See this answer for many good reasons to abandon AWT components in favor of Swing. Added to that is that few people in this day & age have ever used AWT (so can't help with problems) or if they did, have forgotten the finer details (so give faulty advice). As mentioned in the linked answer, Swing is built on AWT, but has differences that need to be unlearned when making the transition. Seriously, start with Swing or go directly to Java-FX (an entirely different GUI toolkit which is based on neither Swing nor AWT). Warning: This is the one & only time I will be helping to fix broken AWT code for you. (3)
Java GUIs have to work on different OS', screen size, screen resolution etc. using different PLAFs in different locales. As such, they are not conducive to pixel perfect layout. Instead use layout managers, or combinations of them along with layout padding and borders for white space. The (beginnings of these) problems can be seen even in that short example, in that: a) Either the button, the text field, or both are not center aligned. If the frame is resized even slightly, it would be both. b) The size guess for the text field is also wrong - it is not long enough to display the text!
Relevant to (1), there are still some problems with this code. I did the minimum needed to answer the question and will do no more.
TextField t1 = new TextField(30);
Button b;
int count = 0;
Frame f;
AwtListenerDemo() {
b = new Button("Ok");
f = new Frame("Action Listener Example");
f.setSize(300, 300);
f.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent e) {
f.dispose();
}
});
f.setLayout(null);
b.setBounds(100, 100, 60, 20);
t1.setBounds(100, 200, 80, 30);
f.add(b);
f.add(t1);
f.setVisible(true);
b.addActionListener(this);
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == b) {
count++;
t1.setText("Button Clicked" + count + "Times");
}
}
public static void main(String[] args) {
new AwtListenerDemo();
}

What is the best way to make a handsome GUI for a Java software

I created a website that acts as a software.
Now i'm trying to do the exact same thing with Java.
is there a way to draw some objects like "div" in html, that i can change x and y position (absolute), background-image, background-color, and put other object into it, [...] with Java ?
I tried this code :
import javax.swing.*;
import javax.swing.border.*;
import javax.accessibility.*;
import java.awt.*;
import java.awt.event.*;
/*
* LayeredPaneDemo.java requires
* images/dukeWaveRed.gif.
*/
public class Demo extends JPanel implements ActionListener, MouseMotionListener{
private String[] layerStrings = { "Yellow (0)", "Magenta (1)",
"Cyan (2)", "Red (3)",
"Green (4)" };
private Color[] layerColors = { Color.yellow, Color.magenta,
Color.cyan, Color.red,
Color.green };
private JLayeredPane layeredPane;
private JLabel dukeLabel;
private JCheckBox onTop;
private JComboBox layerList;
//Action commands
private static String ON_TOP_COMMAND = "ontop";
private static String LAYER_COMMAND = "layer";
//Adjustments to put Duke's toe at the cursor's tip.
private static final int XFUDGE = 40;
private static final int YFUDGE = 57;
public Demo() {
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
//Create and load the duke icon.
final ImageIcon icon = createImageIcon("images/dukeWaveRed.gif");
//Create and set up the layered pane.
layeredPane = new JLayeredPane();
layeredPane.setPreferredSize(new Dimension(300, 310));
layeredPane.setBorder(BorderFactory.createTitledBorder(
"Move the Mouse to Move Duke"));
layeredPane.addMouseMotionListener(this);
//This is the origin of the first label added.
Point origin = new Point(10, 20);
//This is the offset for computing the origin for the next label.
int offset = 35;
//Add several overlapping, colored labels to the layered pane
//using absolute positioning/sizing.
for (int i = 0; i < layerStrings.length; i++) {
JLabel label = createColoredLabel(layerStrings[i], layerColors[i], origin);
layeredPane.add(label, new Integer(i));
origin.x += offset;
origin.y += offset;
}
//Create and add the Duke label to the layered pane.
dukeLabel = new JLabel(icon);
if (icon != null) {
dukeLabel.setBounds(15, 225, icon.getIconWidth(), icon.getIconHeight());
} else {
System.err.println("Duke icon not found; using black square instead.");
dukeLabel.setBounds(15, 225, 30, 30);
dukeLabel.setOpaque(true);
dukeLabel.setBackground(Color.BLACK);
}
layeredPane.add(dukeLabel, new Integer(2), 0);
//Add control pane and layered pane to this JPanel.
add(Box.createRigidArea(new Dimension(0, 10)));
add(createControlPanel());
add(Box.createRigidArea(new Dimension(0, 10)));
add(layeredPane);
}
/** Returns an ImageIcon, or null if the path was invalid. */
protected static ImageIcon createImageIcon(String path) {
java.net.URL imgURL = Demo.class.getResource(path);
if (imgURL != null) {
return new ImageIcon(imgURL);
} else {
System.err.println("Couldn't find file: " + path);
return null;
}
}
//Create and set up a colored label.
private JLabel createColoredLabel(String text, Color color, Point origin) {
JLabel label = new JLabel(text);
label.setVerticalAlignment(JLabel.TOP);
label.setHorizontalAlignment(JLabel.CENTER);
label.setOpaque(true);
label.setBackground(color);
label.setForeground(Color.black);
label.setBorder(BorderFactory.createLineBorder(Color.black));
label.setBounds(origin.x, origin.y, 140, 140);
return label;
}
//Create the control pane for the top of the frame.
private JPanel createControlPanel() {
onTop = new JCheckBox("Top Position in Layer");
onTop.setSelected(true);
onTop.setActionCommand(ON_TOP_COMMAND);
onTop.addActionListener(this);
layerList = new JComboBox(layerStrings);
layerList.setSelectedIndex(2); //cyan layer
layerList.setActionCommand(LAYER_COMMAND);
layerList.addActionListener(this);
JPanel controls = new JPanel();
controls.add(layerList);
controls.add(onTop);
controls.setBorder(BorderFactory.createTitledBorder("Choose Duke's Layer and Position"));
return controls;
}
//Make Duke follow the cursor.
public void mouseMoved(MouseEvent e) {
dukeLabel.setLocation(e.getX()-XFUDGE, e.getY()-YFUDGE);
}
public void mouseDragged(MouseEvent e) {} //do nothing
//Handle user interaction with the check box and combo box.
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (ON_TOP_COMMAND.equals(cmd)) {
if (onTop.isSelected())
layeredPane.moveToFront(dukeLabel);
else
layeredPane.moveToBack(dukeLabel);
}
else if (LAYER_COMMAND.equals(cmd)) {
int position = onTop.isSelected() ? 0 : 1;
layeredPane.setLayer(dukeLabel,layerList.getSelectedIndex(),position);
}
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
JFrame frame = new JFrame("LayeredPaneDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Create and set up the content pane.
JComponent newContentPane = new Demo();
newContentPane.setOpaque(true); //content panes must be opaque
frame.setContentPane(newContentPane);
//Display the window.
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
//Schedule a job for the event-dispatching thread:
//creating and showing this application's GUI.
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}
This is the result :
It looks to run, but I can not put JLabel into JLabel (will have i to create my own object ? with a JLabel and with sons ?
And finally when i put an addMouseListener to a JLabel , if an other JLabel is above it, i can click it through the other :-/
Use JavaFX with Java 8.
It is a bit slower in development until learned. Due to lambdas and component complexity.
The styling can be done with CSS.
There are animated effects.
Since the question targets Swing, I'll add a Swing-oriented answer anyway for the people who find this question and really must use Swing for any reason.
The trick is to use a fitting look & feel to do it for you in Swing.
Swing tutorial documentation on the subject.
That alone doesn't answer the question as this is about make it pretty and the default look & feel selections are not exactly all that great to look at / didn't stand the test of time. But there are also third party look & feels available for download, you're not necessarily limited to what is bundled with the runtime by default.
As an example, one can use Insubstantial (formerly known as Substance) to pretty up Swing applications. It is too naive to think that you can just plop this look & feel into an existing program and make it look good in an instant, but when you design your application with one of these look & feels from the beginning, the application can look very slick indeed.
But one should really use Swing only when it is a must. Swing is an aging technology and whatever extensions were available for download for it in its glory days are slowly disappearing from the internet / no longer maintained. On top of that it is basically the difference between choosing a pre-designed look & feel or having flexibility to do styling yourself, which JavaFX allows you to do with quite some flexibility.

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.

How to add button in text field?

I'm creating a text field in java using swing components. I want to make a search text field like one appears in Mozilla or other browsers.
I have added a button in text field. I have set border layout of JTextField. everything is working fine but whenever large text is written in text field (as it reaches the given size of text field) it goes behind the button. As everyone of you must have seen, this does not occur in search bars. Text must not go behind the button rather there must be some gap between button and text.
Does anyone know how to do that?
Maybe start with something like this:
The blinking cursor is positioned at the far right of the text field.
import java.awt.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class ButtonsInTextField {
JPanel gui = new JPanel(new GridBagLayout());
JTextField textField;
ButtonsInTextField(int cols) {
JPanel textFieldWithButtonsPanel = new JPanel(new FlowLayout(
SwingConstants.LEADING, 5, 1));
textField = new JTextField(cols);
textFieldWithButtonsPanel.add(textField);
addButtonToPanel(textFieldWithButtonsPanel, 8);
addButtonToPanel(textFieldWithButtonsPanel, 16);
addButtonToPanel(textFieldWithButtonsPanel, 24);
// WARNING: Not sensitive to PLAF change!
textFieldWithButtonsPanel.setBackground(textField.getBackground());
textFieldWithButtonsPanel.setBorder(textField.getBorder());
textField.setBorder(null);
// END WARNING:
gui.add(textFieldWithButtonsPanel);
}
private final void addButtonToPanel(JPanel panel, int height) {
BufferedImage bi = new BufferedImage(
// find the size of an icon from the system,
// this is just a guess
24, height, BufferedImage.TYPE_INT_RGB);
JButton b = new JButton(new ImageIcon(bi));
b.setContentAreaFilled(false);
//b.setBorderPainted(false);
b.setMargin(new Insets(0,0,0,0));
panel.add(b);
}
public final JComponent getGui() {
return gui;
}
public final JTextField getField() {
return textField;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
ButtonsInTextField bitf = new ButtonsInTextField(20);
JOptionPane.showMessageDialog(null, bitf.getGui());
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency
SwingUtilities.invokeLater(r);
}
}
As people have noted above, it might have helped to see the code, especially the Layout manager.
However, you might try the following (if you haven't yet):
Call setColumns
http://docs.oracle.com/javase/7/docs/api/javax/swing/JTextField.html#setColumns(int)
Call setPreferredSize /setMaximumSize/setMinimumSize depending on your layout manager.
But I'd try to avoid this solution because it's pixel-level maintenance.
Regards
As an alternative solution you can use a Component Border, which allows you to use the button as a Border so it appears within the text field.

Java (Swing): JScrollPane.setBounds() does not apply?

I'm trying to create a simple JList with a scrollbar, and therefore i need to have the JList within a JScrollPane. So far, so good. However, for some reason i can't resize/position the JScrollPane!? It sounds logic that everything inside it should stretch to 100%, so if i set the JScrollPane to be 300px wide, the elements inside will be as well. Is that correct?
While you're at it, please critisize and give me hints if i should change something or optimize it.
Anyhow, here's the code:
package train;
import java.awt.*;
import javax.swing.*;
public class GUI {
private DefaultListModel loggerContent = new DefaultListModel();
private JList logger = new JList(loggerContent);
GUI() {
JFrame mainFrame = new JFrame("title");
this.addToLog("testing testing");
this.addToLog("another test");
// Create all elements
logger = new JList(loggerContent);
JScrollPane logWrapper = new JScrollPane(logger);
logWrapper.setBounds(10, 10, 20, 50);
// Add all elements
mainFrame.add(logWrapper);
// Show everything
mainFrame.setSize(new Dimension(600, 500));
mainFrame.setVisible(true);
}
public void addToLog(String inputString) {
int size = logger.getModel().getSize();
loggerContent.add(size, inputString);
}
}
Thanks in advance,
qwerty
EDIT: Here's a screenshot of it running: http://i.stack.imgur.com/sLGgQ.png
The setVisibleRowCount() method of JList is particularly convenient for this, as suggested in the relevant tutorial. ListDemo is a good example.
Addendum:
please critisize and give me hints…
Well, since you ask: Don't invoke public methods in the constructor; make them private or invoke them after the constructor finishes. There's no need to find the last index for add(), when addElement() is available. Also, be sure to construct your GUI on the event dispatch thread .
import java.awt.*;
import javax.swing.*;
/** #see http://stackoverflow.com/questions/5422160 */
public class ListPanel extends JPanel {
private DefaultListModel model = new DefaultListModel();
private JList list = new JList(model);
ListPanel() {
list.setVisibleRowCount(5);
}
public void append(String inputString) {
model.addElement(inputString);
}
private void init() {
for (int i = 0; i < 10; i++) {
this.append("String " + String.valueOf(i));
}
JFrame mainFrame = new JFrame("GUI");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane jsp = new JScrollPane(list);
mainFrame.add(jsp);
mainFrame.pack();
mainFrame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ListPanel().init();
}
});
}
}
The bounds & size of a component are generally ignored over that of it's preferred size and the constraints of the layout being used by the container.
To solve this problem, learn how to use layouts & apply them appropriately.
Try to put your JScrollPane inside a JPanel and add the panel to the frame.
JPanel panel = new JPanel();
panel.add (logWrapper);
mainFrame.add(panel);
Then set the bounds of the panel instead of the JScrollpane
panel.setBounds(10, 10, 20, 50);
The probles is that Swing uses layout managers to control child bounds property. Adding a JScrollpane directly to the main frame, doesn't allow you to choose right bounds properly.

Categories

Resources