When I edit text in a JTextField in my Swing application on OSX, the text gets garbled. It's most pronounced when I insert or delete characters but there are artifacts just when moving the cursor around. The data is fine, but the UI rendering is not.
What causes this and how can I fix it?
I'm using com.apple.laf.AquaLookAndFeel, as in this sample program. Type in some text and move the cursor around with the arrow keys to observe the weirdness.
import java.awt.*;
import javax.swing.*;
class TextFieldDisplay {
public static void main(String[] args) {
MainWindow app = new MainWindow();
}
}
class MainWindow extends JFrame {
public MainWindow() {
try {
UIManager.setLookAndFeel("com.apple.laf.AquaLookAndFeel");
} catch(Exception e) {
System.out.println("AquaLookAndFeel is not supported on your platform.");
System.exit(1);
}
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
setMinimumSize(new Dimension(300, 100));
JPanel innerPanel = new JPanel();
JScrollPane scrollPane = new JScrollPane();
scrollPane.setPreferredSize(new Dimension(250, 20));
scrollPane.setViewportView(innerPanel);
JPanel mainPanel = new JPanel();
getContentPane().add(mainPanel);
mainPanel.add(innerPanel);
JTextField textField = new JTextField();
textField.setPreferredSize(new Dimension(250, 20));
innerPanel.add(textField);
pack();
}
}
I noticed as I was writing the SSCCE that the display issues seemed to crop up after I added the JScrollPane.
Not sure if it's relevant but I'm using Apple Java version 1.6.0_51 with a retina display.
Two things jump out at me.
Firstly, you're not initalisig your UI in the EDT, secondly, you're messing with the preferred and minimum sizes of your components.
You are not taking into consideration the font metrics when calculating the size of your components, which seems to be causing issues when it is rendering the content
Start by taking a look at Initial Threads.
import java.awt.Dimension;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
import javax.swing.UIManager;
import javax.swing.WindowConstants;
class TextFieldDisplay {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
//UIManager.setLookAndFeel("com.apple.laf.AquaLookAndFeel");
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception e) {
System.out.println("AquaLookAndFeel is not supported on your platform.");
System.exit(1);
}
MainWindow app = new MainWindow();
}
});
}
}
class MainWindow extends JFrame {
public MainWindow() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
setVisible(true);
// setMinimumSize(new Dimension(300, 100));
JPanel innerPanel = new JPanel();
// JScrollPane scrollPane = new JScrollPane();
// scrollPane.setPreferredSize(new Dimension(250, 20));
// scrollPane.setViewportView(innerPanel);
JPanel mainPanel = new JPanel();
getContentPane().add(mainPanel);
mainPanel.add(innerPanel);
JTextField textField = new JTextField(20);
// textField.setPreferredSize(new Dimension(250, 20));
innerPanel.add(textField);
pack();
}
}
Related
I am writing in a notepad. And I want to implement text scaling in my notepad. But I don't know how to do it. I'm trying to find it but everyone is suggesting to change the font size. But I need another solution.
I am create new project and add buttons and JTextArea.
package zoomtest;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.BorderLayout;
import javax.swing.JButton;
import javax.swing.JTextArea;
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class zoom {
private JFrame frame;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
zoom window = new zoom();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public zoom() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.getContentPane().add(panel, BorderLayout.NORTH);
JButton ZoomIn = new JButton("Zoom in");
ZoomIn.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//Code here...
}
});
panel.add(ZoomIn);
JButton Zoomout = new JButton("Zoom out");
Zoomout.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent arg0) {
//Code here...
}
});
panel.add(Zoomout);
JTextArea jta = new JTextArea();
frame.getContentPane().add(jta, BorderLayout.CENTER);
}
}
Introduction
Oracle has a helpful tutorial, Creating a GUI With Swing. Skip the Learning Swing with the NetBeans IDE section. Pay close attention to the Laying Out Components Within a Container section.
I reworked your GUI. Here's how it looks when the application starts. I typed some text so you can see the font change.
Here's how it looks after we zoom out.
Here's how it looks after we zoom in.
Stack Overflow scales the images, so it's not as obvious that the text is zooming.
Explanation
Swing was designed to be used with layout managers. I created two JPanels, one for the JButtons and one for the JTextArea. I put the JTextArea in a JScrollPane so you could type more than 10 lines.
I keep track of the font size in an int field. This is a simple application model. Your Swing application should always have an application model made up of one or more plain Java getter/setter classes.
Code
Here's the complete runnable code.
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.FlowLayout;
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class ZoomTextExample {
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
new ZoomTextExample();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
private int pointSize;
private Font textFont;
private JFrame frame;
private JTextArea jta;
private JTextField pointSizeField;
public ZoomTextExample() {
this.pointSize = 16;
this.textFont = new Font(Font.DIALOG, Font.PLAIN, pointSize);
initialize();
}
private void initialize() {
frame = new JFrame("Text Editor");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createButtonPanel(), BorderLayout.NORTH);
frame.add(createTextAreaPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonPanel() {
JPanel panel = new JPanel(new FlowLayout());
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
JButton zoomIn = new JButton("Zoom in");
zoomIn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
incrementPointSize(+2);
updatePanels();
}
});
panel.add(zoomIn);
panel.add(Box.createHorizontalStrut(20));
JLabel label = new JLabel("Current font size:");
panel.add(label);
pointSizeField = new JTextField(3);
pointSizeField.setEditable(false);
pointSizeField.setText(Integer.toString(pointSize));
panel.add(pointSizeField);
panel.add(Box.createHorizontalStrut(20));
JButton zoomOut = new JButton("Zoom out");
zoomOut.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
incrementPointSize(-2);
updatePanels();
}
});
panel.add(zoomOut);
return panel;
}
private JPanel createTextAreaPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setBorder(BorderFactory.createEmptyBorder(0, 5, 5, 5));
jta = new JTextArea(10, 40);
jta.setFont(textFont);
JScrollPane scrollPane = new JScrollPane(jta);
panel.add(scrollPane, BorderLayout.CENTER);
return panel;
}
private void updatePanels() {
pointSizeField.setText(Integer.toString(pointSize));
textFont = textFont.deriveFont((float) pointSize);
jta.setFont(textFont);
frame.pack();
}
private void incrementPointSize(int increment) {
pointSize += increment;
}
}
I want split my Frame into two JPanel and the right JPanel serve as a textarea used to input and display.
However, I can't input anything in it and it can't display any thing.
the code as below:
JPanel jp1, jp2;
public DemoFrame() {
jp1 = new JPanel();
jp2 = new JPanel();
JLabel label = new JLabel("text");
JTextArea ta = new JTextArea(100,100);
ta.setText("some text");
ta.setSize(300, 300);
jp2.add(label);
jp2.add(ta);
JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, jp1, jp2);
this.getContentPane().add(jsp);;
setBounds(300, 200, 500, 500);
setVisible(true);
jsp.setDividerLocation(0.5);//
}
the output as below(it doesnt display anything):
Congratulations, you've fallen victim to a number of conspiring issues.
The main culprit is FlowLayout, which is the default layout manager for JPanel. Essentially, when you add your, rather large, JTextArea to the panel, the FlowLayout is trying to honour the preferred size as best as it can within the constraints of the available space. For reasons I'm not 100% sure of, that means laying out the component beyond the visible bounds of the container.
If you type enough text, you will begin to see it.
While there are a number of ways you might fix this, they are basically the same solution - use a different layout manager.
For this example, I've just used a BorderLayout instead
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JSplitPane;
import javax.swing.JTextArea;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test {
public static void main(String[] args) {
new Test();
}
private JPanel jp1, jp2;
public Test() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
jp1 = new JPanel();
jp2 = new JPanel(new BorderLayout());
JLabel label = new JLabel("text");
JTextArea ta = new JTextArea(50, 50);
ta.setText("some text");
jp2.add(label, BorderLayout.NORTH);
jp2.add(new JScrollPane(ta));
JSplitPane jsp = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, jp1, jp2);
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(jsp);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
I'm trying to create a scrollable text area, (much like the one i'm writing in right now as in stack overflow's one). It seems as if the scrollpane and the text area are mutually exclusive and i'd like to create a connection between them
package Notepad;
import java.awt.EventQueue;
import java.awt.Font;
import javax.swing.JFrame;
import java.awt.BorderLayout;
import javax.swing.JTextArea;
import javax.swing.JScrollBar;
public class test {
private JFrame frame;
private Font f = new Font(null);
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
test window = new test();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public test() {
initialize();
}
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().setLayout(new BorderLayout(0, 0));
JTextArea textArea = new JTextArea();
frame.getContentPane().add(textArea, BorderLayout.CENTER);
textArea.setLineWrap(true);
textArea.setFont(f.deriveFont(40f));
JScrollBar scrollBar = new JScrollBar();
frame.getContentPane().add(scrollBar, BorderLayout.EAST);
}
}
use JScrollPane rather than JScrollBar
Wrong:
JScrollBar scrollBar = new JScrollBar();
Right:
JScrollPane scroller = new JScrollPane(textArea);
you can set the size of this ScrollPane like so:
Dimension size = new Dimension (0, 50);
scroller.setPreferredSize(size);
NOTE: When you use JScrollPanes, be sure to put where you want it in parentheses, or it will not show up.
JTextArea textArea = new JTextArea();
textArea.setLineWrap(true);
textArea.setFont(f.deriveFont(40f));
JScrollPane scrollPane = new JScrollPane(textArea);
frame.getContentPane().add(scrollPane, BorderLayout.CENTER);
OMG sorry guys. I imported JScrollBar not JScrollPane. Thank you all. I'm going to test this fix and get back to you.
Edit:
It works. Thank you guys!!!
Please look below for Edits.
So I've looking over numerous "solutions" to fix my problem, but I just can't seem to get it working.
This is what my application looks like with the code below:
Basically, I want to set the location of a button, but I can't manage to do so. Here is my code:
package me.cervinakuy.application;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridLayout;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ControlPanel3 extends JFrame {
JPanel panel = new JPanel();
JButton startRobo = new JButton();
JButton stopRobo = new JButton();
JButton restartRobo = new JButton();
public ControlPanel3() {
// setLayout(null);
setSize(1000, 700);
setResizable(false);
setLocation(450, 150);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setBackground(new Color(45, 48, 55));
setTitle("Espin Software | Control Panel");
setVisible(true);
startRobo.setIcon(new ImageIcon(getClass().getResource("/resources/startRobo.png")));
stopRobo.setIcon(new ImageIcon(getClass().getResource("/resources/stopRobo.png")));
restartRobo.setIcon(new ImageIcon(getClass().getResource("/resources/restartRobo.png")));
startRobo.setBorder(null);
stopRobo.setBorder(null);
restartRobo.setBorder(null);
startRobo.setLocation(100, 100);
panel.add(startRobo);
panel.add(stopRobo);
panel.add(restartRobo);
panel.setOpaque(false);
add(panel);
validate();
}
}
EDIT:
I have now managed to create a GUI of what I was initially looking for, however, I have a new problem. Buttons are now pressable from different parts of the GUI, rather than only on the image. For those interested, here is what I have been able to accomplish:
New GUI look.
Updated Code:
package me.cervinakuy.application;
import java.awt.Color;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ControlPanel3 extends JFrame {
JPanel panel = new JPanel();
JButton startRobo = new JButton();
JButton stopRobo = new JButton();
JButton restartRobo = new JButton();
public ControlPanel3() {
// setLayout(null);
setSize(1000, 700);
setResizable(false);
setLocation(450, 150);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
getContentPane().setBackground(new Color(45, 48, 55));
setTitle("Espin Software | Control Panel");
setVisible(true);
startRobo.setIcon(new ImageIcon(getClass().getResource("/resources/startRobo.png")));
stopRobo.setIcon(new ImageIcon(getClass().getResource("/resources/stopRobo.png")));
restartRobo.setIcon(new ImageIcon(getClass().getResource("/resources/restartRobo.png")));
startRobo.setBorder(null);
stopRobo.setBorder(null);
restartRobo.setBorder(null);
panel.setLayout(null);
startRobo.setLocation(200, 200);
startRobo.setBounds(5, -95, 300, 300);
stopRobo.setBounds(5, 0, 300, 300);
restartRobo.setBounds(5, 95, 300, 300);
panel.add(startRobo);
panel.add(stopRobo);
panel.add(restartRobo);
panel.setOpaque(false);
add(panel);
validate();
}
}
There are typically a number of ways to layout components that end with the same effect. In this example, we use a panel to contain the buttons in a column (buttonContainer using a GridLayout) then a panel to restrict that container to the top (buttonConstrainPanel using a BorderLayout) then a container to put that panel on the left (ui with BorderLayout).
It could also be achieved using a single GridBagLayout or a GroupLayout, though the logic of achieving it might not be as simple.
The focus border seen on the blue button indicates the limits of where a mouse click would activate the button.
import java.awt.*;
import java.net.MalformedURLException;
import java.net.URL;
import javax.swing.*;
import javax.swing.border.EmptyBorder;
public class ThreeButtonAlignedLeft {
private JComponent ui = null;
private String prefix = "http://i.stack.imgur.com/";
private String[] suffix = {"gJmeJ.png","T5uTa.png","wCF8S.png"};
ThreeButtonAlignedLeft() {
try {
initUI();
} catch (MalformedURLException ex) {
ex.printStackTrace();
}
}
public void initUI() throws MalformedURLException {
if (ui!=null) return;
ui = new JPanel(new BorderLayout(4,4));
ui.setBorder(new EmptyBorder(4,4,4,4));
JPanel buttonContainer = new JPanel(new GridLayout(0, 1, 5, 5));
for (int ii=0; ii<suffix.length; ii++) {
JButton b = new JButton(new ImageIcon(new URL(prefix + suffix[ii])));
b.setBorderPainted(false);
b.setMargin(new Insets(0,0,0,0));
b.setContentAreaFilled(false);
buttonContainer.add(b);
}
JPanel buttonConstrainPanel = new JPanel(new BorderLayout(0, 0));
buttonConstrainPanel.add(buttonContainer, BorderLayout.PAGE_START);
ui.add(buttonConstrainPanel, BorderLayout.LINE_START);
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
ThreeButtonAlignedLeft o = new ThreeButtonAlignedLeft();
JFrame f = new JFrame(o.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
I'm having a problem trying to change JPanels by using buttons. I have a JFrame with 2 panels, 1 of them is for the buttons, which i want them to always be showed. The other one is the one that i will be switching everytime i press one ot the buttons of the other panel. The problem is that everytime i press them nothing really ever displays, i keep my buttons but the other panel that i call does not appear.
Code for one of the buttons is as follows
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
ReparacaoPanel r = new ReparacaoPanel(this, this.jPanel1);
this.getContentPane().remove(this.jPanel1);
this.getContentPane().add(r);
//this.setContentPane(r);
this.visiblePanel.setVisible(false);
this.visiblePanel = r;
this.pack();
this.setVisible(true);
r.setLocation(200, 200);
this.getContentPane().revalidate();
this.repaint();
}
If i try to use "this.setContentPane(r);" (it sets the frame to only show the panel) the panel shows. But when i try to call it as i'm trying to do in the code above nothing is showed apart from the panel that has the buttons.
I have no idea what i'm doing wrong, it does not seem to be a problem with the JPanel that i'm trying to call as it shows if used alone.
Anyone can help me out?
Consider this working example for switching manually between panels. Which produces this output.
.........
Some tiny NumberPanel
Every new instance shows another number in the center.
import javax.swing.JPanel;
public class NumberPanel extends JPanel {
private static int counter = 0;
public NumberPanel() {
setLayout(new BorderLayout(0, 0));
JLabel lblNewLabel = new JLabel("" + counter++);
lblNewLabel.setHorizontalAlignment(SwingConstants.CENTER);
add(lblNewLabel);
}
}
Setting up a frame
private void initialize() {
frame = new JFrame();
frame.setBounds(100, 100, 450, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new JPanel();
frame.getContentPane().add(panel, BorderLayout.SOUTH);
JButton btnNewButton = new JButton("New button");
btnNewButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
frame.getContentPane().remove(numberPanel);
numberPanel = new NumberPanel();
frame.getContentPane().add(numberPanel, BorderLayout.CENTER);
frame.pack();
}
});
panel.add(btnNewButton);
numberPanel = new NumberPanel();
frame.getContentPane().add(numberPanel, BorderLayout.CENTER);
frame.pack();
}
Testprogram
import java.awt.BorderLayout;
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TestPanelSwitch {
private JFrame frame;
private NumberPanel numberPanel;
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
try {
TestPanelSwitch window = new TestPanelSwitch();
window.frame.setVisible(true);
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
public TestPanelSwitch() {
initialize();
}
private void initialize() {
// see above
}
}
Back to the Question
I think you only need to pack your frame, like in the anonymous ActionListener.
frame.getContentPane().remove(numberPanel);
numberPanel = new NumberPanel();
frame.getContentPane().add(numberPanel, BorderLayout.CENTER);
frame.pack();
EDIT
As leonidas mentioned it is also possible to revalidate the frame. This requires only to replace the upper call to pack by theese.
frame.invalidate();
frame.validate();