Swing - how to mix JTextField and JTextAreas and have same visual appearance? - java

I am using miglayout to create a form in which there are JTextFields (short input answers) as well as JTextAreas (Longer answers). The problem is twofold.
The border placed around a Scrollpane wrapped text area does not match that of a Text Field.
The width and placement of the textarea/textfield differ, causing them not to line up correctly.
alt text http://grab.by/3O0V
After changing from right/left to right/fill:
alt text http://grab.by/3RMk
You can see that the bounds line up, but that there are still gaps. I tried setting novisualpadding but this did not fix it.
Source code:
package test2;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
import net.miginfocom.swing.MigLayout;
public class Test extends JPanel {
private static final int NUM_CHARACTERS_WIDTH = 20;
private static final int NUM_ROWS = 5;
public Test() {
setLayout(new MigLayout(
"wrap 2",
// Align text labels on the so their right edge meets left edge of the text fields
"[right][left]"
));
add(new JLabel("Text field:"));
add(new JTextField(NUM_CHARACTERS_WIDTH));
add(new JLabel("No scrollpane text area:"));
add(new JTextArea(NUM_ROWS, NUM_CHARACTERS_WIDTH));
add(new JLabel("Scrollpane text area:"));
add(new JScrollPane(new JTextArea(NUM_ROWS, NUM_CHARACTERS_WIDTH)));
add(new JLabel("Text field:"));
add(new JTextField(NUM_CHARACTERS_WIDTH));
}
public static void main(String[] args) {
JFrame frame = new JFrame("");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new Test();
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
What's the preferred way to mix and match jtextfield and jtextareas, while still maintaining visual harmony? I notice now that the text field has a blue highlight around it when focus is in it, as opposed to the text area... another source of visual discontinuity.

I know this question is pretty old, but to get the border for a TextArea to match that of a TextField:
(myTextArea).setBorder(new JTextField().getBorder());
That should give a border to your TextArea like the one around a TextField.

Not sure how you can fix your border problem but to fix your layout situation I would just use springlayout. Springlayout is just a way to better layout your elements within the JPanel. You can find out more about it Java Sun Tutorial
Specifically you use it by setting where you want your North, South, West and East borders of each element. To do this you would have to first take your label calls out of the add so each one can be named. So instead of:
add(new JLabel("Text field:"));
Do:
JLabel myLabelName = new JLabel("Text field:");
add(myLabelName);
For each of your elements (JLabels, JTextAreas and JTextField). Once this is done you can easily set the layout.
Springlayout layout = new SpringLayout();
setLayout(layout);
Then for each of the elements you have to set any of the borders you want. They have to be in the specific order South, the North, West then East. Though you don't have to use all four borders if you don't want to. Here is an example on how to set your first text area, the one on the top.
layout.putConstraint(SpringLayout.NORTH, FirstTextAreaName, 10, SpringLayout.NORTH, this);
layout.putConstraint(SpringLayout.WEST, FirstTextAreaName, this.getWidth()/2, SpringLayout.WEST, this);
layout.putConstraint(SpringLayout.EAST, FirstTextAreaName, -10, SpringLayout.EAST, this);
This example doesn't set the south of the text area but if you did want to it would have to be first. The first line sets the north side of the text area to be 10 pixels away from the top. When setting the other areas you but the previous (above) areas name instead of this and say it is 10 pixels away from the south of the previous one:
layout.putConstraint(SpringLayout.NORTH, SecondTextAreaName, 10, SpringLayout.SOUTH, FirstTextAreaName);
The second line in the above example sets the east side of the text area to start halfway through your main panel. The last, third, line sets the east side of the text area to be 10 pixels from the east side of your main panel.

I know that MiGLAyout (which I love, BTW) has the ability to do special handling for visual alignment vs strict pixel alignment. You may be running into this... The 'al' unit identifier is used for this, but I haven't had to use it so can't provide examples. It would probably be worth downloading the MiG sample project and see if they have the same alignment issue (I'm sure they have panels similar to yours).
For what it's worth, we mix text fields and areas in the same panel quite frequently and don't run into this... We do have to set the border of the scroll pane to be the same as the border of the text field as suggested by Noel Ang.
Also, instead of specifying constraints in the layout constructor, we generally specify them as we add each component - not sure if that makes a difference or not...

For the layout problem, try a columnConstraints value of [right][fill] instead of [right][left].
For the other issue, this appears to be a look-and-feel inconsistency. I ran your code in Windows, and the differences are there too, but less flagrant. My suggestion would be to set identifical borders explicitly for text fields and text areas.
setLayout(new MigLayout(
"wrap 2",
"[right][fill]"
));
JTextField textField;
JScrollPane scrollPane;
add(new JLabel("Text field:"));
textField = new JTextField(NUM_CHARACTERS_WIDTH);
textField.setBorder( new EtchedBorder( EtchedBorder.LOWERED ) );
add(textField);
add(new JLabel("No scrollpane text area:"));
add(new JTextArea(NUM_ROWS, NUM_CHARACTERS_WIDTH));
add(new JLabel("Scrollpane text area:"));
scrollPane = new JScrollPane(new JTextArea(NUM_ROWS, NUM_CHARACTERS_WIDTH));
scrollPane.setBorder( new EtchedBorder( EtchedBorder.LOWERED ) );
add(scrollPane);
add(new JLabel("Text field:"));
textField = new JTextField(NUM_CHARACTERS_WIDTH);
textField.setBorder( new EtchedBorder( EtchedBorder.LOWERED ) );
add(textField);
If you can't get MigLayout to align your components, considering using java.awt.GridBagLayout:
import static java.awt.GridBagConstraints.*;
setLayout( new GridBagLayout() );
GridBagConstraints leftCons = new GridBagConstraints();
leftCons.anchor = NORTHEAST;
leftCons.fill = NONE;
leftCons.weightx = 1.0;
leftCons.gridy = RELATIVE;
leftCons.gridx = 0;
leftCons.insets = new Insets( 4, 8, 4, 8 );
GridBagConstraints rightCons = new GridBagConstraints();
rightCons.anchor = NORTHWEST;
rightCons.fill = HORIZONTAL;
rightCons.weightx = 1.0;
rightCons.gridy = RELATIVE;
rightCons.gridx = 1;
rightCons.insets = leftCons.insets;
add(new JLabel("Text field:"), leftCons);
add(new JTextField(NUM_CHARACTERS_WIDTH), rightCons);
add(new JLabel("No scrollpane text area:"), leftCons);
add(new JTextArea(NUM_ROWS, NUM_CHARACTERS_WIDTH), rightCons);
add(new JLabel("Scrollpane text area:"), leftCons);
add(new JScrollPane(new JTextArea(NUM_ROWS, NUM_CHARACTERS_WIDTH)), rightCons);
add(new JLabel("Text field:"), leftCons);
add(new JTextField(NUM_CHARACTERS_WIDTH), rightCons);

First off +1 for screen shots.
Since you are using Mac, did you try Quaqua Look And Feel? It renders the textboxes/areas properly.

The answer is that MiG Layout folks are working on a fix for their next version.
Hello,
Apple has a nasty habbit of compensating by default and not let the developer decide. This is such a case where they have added a border to make it more visually like OS X. This should be the choice of the layout manager...
MigLayout can compensate for visual bounds like this but it is only done for JTabbedPane in Windows XP. I'm not sure it can be done 100% good in OS X though. I'll have to check. We don't want the text field to just grow into the bounds.
I have added this to the todo list for the next version.

Related

Swing labels won't align properly in BoxLayout

I have a JPanel with several JLabels in it and a JTextPane. I want them to be below each other (so no two two labels on the same line), and aligned to the left. I have tried several things:
Using a BoxLayout with BoxLayout.Y_AXIS works for properly getting all elements below each other. However, while the JTextPane correctly aligns to the left, the JLabels stay centered, even when calling several methods to try and get the alignment to the left (see code below).
Using a GridLayout will correctly put the elements below each other and align them to the left, but then the elements will be vertically spread all over the JPanel with huge spaces between the text lines. I want all elements to be in the top of the panel as far as possible.
private final void init() {
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
add(new JLabel("a"));
final JLabel label = new JLabel();
final JTextPane pane = new JTextPane();
add(label);
add(pane);
// these three lines seem to be ignored
label.setHorizontalAlignment(SwingConstants.LEFT);
label.setAlignmentX(LEFT_ALIGNMENT);
label.setHorizontalTextPosition(SwingConstants.LEFT);
pane.setForeground(Color.RED);
pane.setEditable(false);
}
How can I get this right?
I want them to be below each other (so no two two labels on the same line), and aligned to the left.
You need to set the alignment on all components, even the JTextPane.
add(new JLabel("a"));
How do you expect to change the alignment of that label when you don't have a reference to it?
final JLabel label = new JLabel();
final JTextPane pane = new JTextPane();
add(label);
add(pane);
You don't set the alignment of the above components.
Read the section from the Swing tutorial on Fixing Alignment Problems

Can't left align a JLabel within a JPanel when adding to a BorderLayout

This seems like a simple thing to do, but I can't get it to work.
I have a BorderLayout. I want to use the top part for a title bar. I want to add a JPanel with labels, buttons and other components. However, the PAGE_START part of the border layout won't left align the panel. Here's the situation, with comments in where I've tried to set the alignment.
I noticed that when I don't add a panel to the border layout, and just write the JLabel straight in, it has left alignment by default.
This is not what I want, though, because I am planning on putting a BoxLayout.X_AXIS horizontally through the BorderLayout.PAGE_START title area. Seems to be a reasonable thing to do?
The Container pane argument to the static method is just the single panel on the main JFrame.
public static void addComponentsToPane(Container pane)
{
JLabel jlabel = new JLabel("I want to left align this inside a JPanel");
// Doesn't work: jlabel.setAlignmentX(Component.LEFT_ALIGNMENT);
JPanel jpanel = new JPanel();
//Doesn't work: jlabel.setAlignmentX(Component.LEFT_ALIGNMENT);
jpanel.add(jlabel);
pane.add(jpanel, BorderLayout.PAGE_START);
// Other parts of the BoxLayout (works fine)
JButton button = new JButton("Button 2 (CENTER)");
button.setPreferredSize(new Dimension(200, 100));
pane.add(button, BorderLayout.CENTER);
button = new JButton("Button 3 (LINE_START)");
pane.add(button, BorderLayout.LINE_START);
button = new JButton("Long-Named Button 4 (PAGE_END)");
pane.add(button, BorderLayout.PAGE_END);
button = new JButton("5 (LINE_END)");
pane.add(button, BorderLayout.LINE_END);
}
Even when I tell the panel to left align the label, it doesn't appear left aligned.
Does anyone know what I am doing wrong?
By default a JPanel uses a FlowLayout with "center" alignment.
if you want components "left" aligned, then you need to set the layout on the panel to use a FlowLayout with "left" alignment.
Read the FlowLayout API for the proper constructor to use to set the alignment.
Or you can also read the Swing tutorial on How to Use FlowLayut which gives the constructors and valid values to specify the alignment.

How do I need to implement GridBagLayout

How would I add gridbag layout to my code in order for output box to span the length of gui within the border parameters provided in the following code? I have two other classes that go along with this work fine. When the GUI populates it all works except the output text box doesn't span the length of gui so that the text in text box is cut off and I want to fix this, but I don't know how to do it as I never used gridbaglayout.
Below is the image showing how the GUI is supposed to look:
The code in question is given below:
public ATMGui() {
checkingAcc = new Account(1000);
savingAcc = new Account(2000);
currentSelect = new Account(0);
atmFrame = new JFrame("Automated Teller Machine");
output = new JTextField();
panel = new JPanel();
I believe that the problem may be because I didn't completely declare the size of the output box so it only spans one grid so-to-speak whereas I want it to span both. Can someone help me decide whether I declare the grid differently in the code above or if it goes below? Also, I've read about GridBagLayout and other methods but my problem is that I'm not sure how/where to implement it here.
atmWithdraw = new JButton("Withdraw");
atmDeposit = new JButton("Deposit");
transfer = new JButton("Transfer to");
balance = new JButton("Balance");
atmWithdraw.addActionListener(this);
panel.setLayout(new GridLayout(4, 2));
panel.setBorder(BorderFactory.createEmptyBorder(15, 15, 0, 15));
panel.add(atmWithdraw, 0);
panel.add(atmDeposit, 1);
panel.add(transfer, 2);
panel.add(balance, 3);
panel.add(checking, 4);
panel.add(savings, 5);
panel.add(output, BorderLayout.LINE_END);
atmFrame.add(panel);
atmFrame.setSize(300, 175);
atmFrame.setVisible(true);
i have never used gridbaglayout
Start by reading the section from the Swing tutorial on How to Use GridBagLayout for working examples.
The working example shows how to have a button span the entire width of the frame. You need to play with the "grid width" constraint. The tutorial explain how all the constraints are used.
The other option is to nest layout managers. So maybe you use the standard BorderLayout of the frame. Then you create a panel and add some components to the panel using your GridBagLayout and then add the panel to the CENTER. Then you can add your "output box" to the BorderLayout.PAGE_END. By default the component will fill the horizontal space.
The point is read the tutorials and learn the basics of each layout manager. Then use the appropriate combination of layout managers to get the job done.
My advice is not to use GridBagLayout but rather something a bit easier to use (but just as powerful) like TableLayout (http://www.oracle.com/technetwork/java/tablelayout-141489.html). To use include download the library and include in classpath or use the following maven include: -
<dependency>
<groupId>info.clearthought</groupId>
<artifactId>table-layout</artifactId>
<version>4.3.0</version>
</dependency>
(See https://github.com/nerro/table-layout for more details)
Once included, then you can define your table layout as a simple 2 dimensional array of numbers e.g.
import info.clearthought.layout.TableLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JRadioButton;
import javax.swing.JTextField;
public class Test {
public static void main(String [] args) {
// Create JPanel using TableLayout as layout manager
double PREF = TableLayout.PREFERRED;
double BORDER = 10;
double size[][] = { { BORDER, 0.50, 10, 0.50, 10 }, { 10, PREF, 10, PREF, 10, PREF, 10, PREF, 10 } };
JPanel panel = new JPanel(new TableLayout(size));
panel.add(new JButton("Withdraw"), "1,1");
panel.add(new JButton("Deposit"), "3,1");
panel.add(new JButton("Transfer to"), "1,3");
panel.add(new JButton("Balance"), "3,3");
panel.add(new JRadioButton("Checking"), "1,5");
panel.add(new JRadioButton("Savings"), "3,5");
panel.add(new JTextField(), "1,7,3,7");
// Add to JFrame and Display
JFrame frame = new JFrame("ATM Machine");
frame.setSize(300, 200);
frame.setContentPane(panel);
frame.setVisible(true);
}
}
The previous code produces the following: -
NOTE that the layout is defined as follows: -
double size[][] = { { WIDTH1, WIDTH2, ... }, { HEIGHT1, HEIGHT2, ... } };`
The 2 column widths are specified as 50% or 0.50. All real numbers in the range [0.0..1.0) represents percentages e.g. see the BORDER variable. The constant FILL is allocated 100% of the scalable space and PREFERRED fills to the preferred size of the component. This allows the window to resize nicely.
To add then to this frame it's miles easier than GridBagLayout i.e. simply the cell X/Y positions e.g.
panel.add(new JButton("Withdraw"), "1,1");
panel.add(new JButton("Deposit"), "3,1");
We can span several columns as follows: -
panel.add(new JTextField(), "1,7,3,7");
This spans the JTextField from column 1 to column 3 in row 7.
For more advanced uses see: -
http://www.clearthought.info/sun/products/jfc/tsc/articles/tablelayout/Simple.html

How do I force BoxLayout and JScrollPane to restrict content size?

I encountered an issue when using a JTextField within a BoxLayout within a JScrollPane.
The desired result is a JTextField that scrolls within itself, as you see when you paste a URL that is longer than the width of your screen into the address bar on your web browser. The entire string shifts left or right when you try to move the caret past the boundaries of the text field.
However, when put in a JScrollPane, the text field fails to do this, instead expanding to fit the entire string.
Before the text:
After the text:
The text box increases its width, causing a scroll bar to appear, rather than "internal scrolling" behavior I described before (where the text box remains the same size, but only shows part of the text).
This behavior even occurs when setting the scroll bar policy to JScrollPane.HORIZONTAL_SCROLLBAR_NEVER:
I noticed that the text box performs the "internal scrolling" correctly until the window is resized, at which time the text field resizes itself off the screen.
I require a scroll pane because I need the vertical scroll bar in my application, but I would like the horizontal axis to be restricted to whatever size the window allows to the panel. How can I achieve this?
Code segment for the above examples:
import java.awt.Dimension;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextField;
public class GUI {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
JScrollPane scrollPane = new JScrollPane(panel);
frame.add(scrollPane);
JTextField textField = new JTextField();
panel.add(textField);
frame.setSize(500, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
panel.setLayout(new BoxLayout(panel, BoxLayout.X_AXIS));
// max width = Short.MAX_VALUE so it expands to fill the frame width
// max height = preferred height so the text area height does not expand
textField.setMaximumSize(new Dimension(Short.MAX_VALUE, textField.getPreferredSize().height));
frame.setVisible(true);
}
}
The best solution would still implement a horizontal scroll bar if the combined minimum size of the components in the panel (on the horizontal axis) was greater than the width of the window (say, if I added a button in there to the left of the text field).
Use a different layout manager, something like GridBagLayout, which will generally honor the preferred size of the component, for example
JFrame frame = new JFrame();
JPanel panel = new JPanel(new GridBagLayout());
JScrollPane scrollPane = new JScrollPane(panel);
frame.add(scrollPane);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1;
gbc.fill = gbc.HORIZONTAL;
JTextField textField = new JTextField(10);
panel.add(textField, gbc);
frame.setSize(500, 200);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
A simple solution is to set the preferred width of the text field to its minimum width.
textField.setPreferredSize(new Dimension(textField.getMinimumSize().width, textField.getPreferredSize().height));
The text field will perform the "internal scrolling" behavior, as well as cause a scroll bar to appear if the window shrinks below its minimum size (as described in the original question's final statement, below)
The best solution would still implement a horizontal scroll bar if the combined minimum size of the components in the panel (on the horizontal axis) was greater than the width of the window (say, if I added a button in there to the left of the text field).

Java swing layouts and/or labels not in order

I have got a window that should display the following:
JLablel "Have you used GUI before?" on the top, centered
two radioButtons "Yes" and "No" below it, somewhat in the center, a little bit towards the left
a JButton "NEXT" in the bottom-right corner
All three elements should have green font and darkGrey background.
The problem is that the window which is showing up, does not look like I would like it to.
And this is my code:
yesButton = new JRadioButton(yes);
//yesButton.setMnemonic(KeyEvent.VK_B); // doesn't work?
yesButton.setActionCommand(yes);
noButton = new JRadioButton(no);
// noButton.setMnemonic(KeyEvent.VK_C); // doesn't work?
noButton.setActionCommand(no);
ButtonGroup group = new ButtonGroup();
group.add(yesButton);
group.add(noButton);
nextButton = new JButton("NEXT");
nextButton.setActionCommand(next);
yesButton.addActionListener(this);
noButton.addActionListener(this);
nextButton.addActionListener(this);
JPanel radioPanel = new JPanel(new GridLayout(0, 1));
radioPanel.add(yesButton);
radioPanel.add(noButton);
add(radioPanel, BorderLayout.WEST);
// setBorder(BorderFactory.createEmptyBorder(20,20,20,20));
// radioPanel.setBorder(new EmptyBorder(250, 250, 20, 20));
// there is no difference between the above two, right?
String q = "Have you used GUI before?";
JPanel area = new JPanel(new BorderLayout());
area.setBackground(Color.darkGray);
JLabel textLabel2 = new JLabel("<html><div style=\"text-align: center;\">"
+ q + "</html>", SwingConstants.CENTER);
textLabel2.setForeground(Color.green);
Font font2 = new Font("SansSerif", Font.PLAIN, 30);
textLabel2.setFont(font2);
//textLabel2.setBorder(new EmptyBorder(0, 0, 250, 0)); //top, left, bottom, right
area.add(textLabel2, BorderLayout.NORTH);
area.add(nextButton, BorderLayout.EAST);
add(area, BorderLayout.CENTER);
I feel I'm nearly there, thanks for any help!
--EDIT--
A screenshot:
You need to use nested panels.
for the BorderLayout.NORTH you can add the JLabel directly. You will need to set the horizontal text alignment to center.
for the radio buttons you can create a JPanel with a FlowLayout and then add the buttons to the panel and add the panel to the CENTER.
for the button you add the button to a panel using a FlowLayout that is right aligned, then add the panel to the SOUTH.
There are other choices. You could also use a Vertical BoxLayout as the layout of the main panel and then add child panels to it.
You won't be able to get much control with just a BorderLayout. Try something else like MigLayout or one of the other many many layout managers Java has (GridBag, Box, etc).
In MigLayout it would look something like:
area.setLayout(new MigLayout("fill"));
area.add(textLabel2, "wrap");
area.add(radioPanel, "wrap");
area.add(nextButton, "tag right");

Categories