My JTextArea continues on for infinity, despite adding a LineWrap etc - java

package swingtraining;
import javax.swing.*;
import java.awt.*;
import static java.awt.Color.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import static javafx.scene.paint.Color.TEAL;
import static javax.swing.JFrame.EXIT_ON_CLOSE;
public class MyFrame extends JFrame{
public MyFrame(){
setSize(500,500);
setVisible(true);
setResizable(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLocationRelativeTo(null);
};
public static class MyPanel extends JPanel{
public MyPanel(){
setLayout(new GridBagLayout());
setBackground(BLACK);
setOpaque(true);
GridBagConstraints gbc1 = new GridBagConstraints();
GridBagConstraints gbc2 = new GridBagConstraints();
gbc1.insets = new Insets(200,0,0,200);
gbc1.ipadx = 100;
gbc1.ipady = 100;
gbc1.gridx = 1;
gbc1.gridy = 1;
gbc2.insets = new Insets(0,200,200,0);
gbc2.ipadx = 150;
gbc2.ipady = 10 ;
gbc2.gridx = 1;
gbc2.gridy = 1;
JTextArea jta1 = new JTextArea();
jta1.setLineWrap(true);
jta1.setWrapStyleWord(true);
JButton jb1 = new JButton("Have a banana!");
jb1.setToolTipText("Button prints Banana.");
ActionListener action1 = new ActionListener() {
#Override
public void actionPerformed(ActionEvent ae) {
System.out.println("\nBananas!");
}
};
jb1.addActionListener(action1);
add(jta1,gbc2);
add(jb1,gbc1);
};
};
public static void main(String args[]) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
MyFrame jf1 = new MyFrame();
MyPanel jp1 = new MyPanel();
jf1.add(jp1);
}
});
}
}
The code is just a simple JFrame, Panel, with a Button that prints bananas, and a JTextArea. When typed into without the LineWrap etc, it simply extends itself depending on which direction input is being applied. (pressing enter to go down in the area pulls it upwards/downwards, and typing into it pulls it to the left and to the right, making it bigger.) This makes sense, I didn't add a LineWrap or anything. However, adding those;
jta1.setLineWrap(true);
jta1.setWrapStyleWord(true);
I get the following result;
Just a picture to show what I'm talking about:
What I'm aiming for is a JTextArea that has limits, when those are exceeded creates a scrollbar, and doesn't change size at all.
Suggestions?

What I'm aiming for is a JTextArea that has limits, when those are exceeded creates a scrollbar, and doesn't change size at all.
JTextArea jta1 = new JTextArea();
Your text area doesn't have preferred size so it keeps growing. You need to create the text area with a preferred size. This is done by using:
JTextArea jta1 = new JTextArea(5, 30);
Now the layout manager can use the preferred size of the text area and the scrollpane will display the scrollbars when the preferred size of the text area is greater than the size of the scroll pane.
when those are exceeded creates a scrollbar, and doesn't change size at all.
And as pointed out by MadProgrammer if you want a scrollbar then you also need to actually add your text area to a JScrollPane and then add the scrollpane to the frame. So you would also need to use code like:
//add(jta1,gbc2);
add(new JScrollPane(jta1), gbc);
Read the section from the Swing tutorial on How to Use Text Areas for more information and examples.

Related

Java Swing trying to add a stationary JButton over a JScrollPane

I am trying to make a text editor with a button that appears at the bottom right of the editor regardless if you scroll up or down and appears over the text area
import javax.swing.*;
import java.awt.*;
public class Problem{
public static void main(String[] args){
//Setting up the frame
JFrame window = new JFrame();
window.setSize(600, 400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Making the LayeredPane
JLayeredPane LP = new JLayeredPane();
LP.setLayout(new BorderLayout());
//Making the ScrollPane and JTextArea
JTextArea textArea = new JTextArea(100,50);
textArea.setText("Test Text");
JScrollPane back = new JScrollPane();
back.setViewportView(textArea);
//Making the panel that appears in the front of the text
JPanel front = new JPanel();
front.setLayout(null);
front.setBackground(new Color(0,0,0,0));
front.setOpaque(false);
JButton button = new JButton("test");
button.setBounds(200,200,50,20);
front.add(button);
LP.add(back,BorderLayout.CENTER);
LP.setLayer(back,0,0);
LP.add(front,BorderLayout.CENTER);
LP.setLayer(front,1,0);
window.add(LP);
window.setVisible(true);
}
}
I am seeing just the JButton with a white background, if I don't add the second layer "front" I see my back JScrollPane with the JTextArea
Caveat
I'm not a fan of this is idea. It's not a "common" UX concept that many desktop users would be presented with and there are a number of, arguably, better solutions which leverage the pre-existing experience of users.
This requires some "hacking" to get to work, so, there's no guarantee that it will work on all platforms or continue to work into the future.
Why doesn't it work?
This is a rather technical question which delves deep into the core of how Swing, and in particular, the JScrollPane work. Let's just say, I don't have the time or desire to dig into, but I know the JScrollPane is heavy optimised, which may be affecting the way in which anything which overlays it gets updated - or it could just be the way that the painting system works.
Runnable example...
This takes the idea by camickr (all credit to him), but instead of using a OverlayLayout, makes use of a GridBagLayout to position the button. Why? Because the GridBagLayout gives me more control over the position of the button - it's a personal thing.
import java.awt.*;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import javax.swing.*;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setLayout(new BorderLayout());
JTextArea textArea = new JTextArea(40, 40);
try (Reader reader = new InputStreamReader(getClass().getResourceAsStream("/resources/StarWarsNewHope.txt"))) {
textArea.read(reader, "A long list");
} catch (IOException exp) {
exp.printStackTrace();
}
JButton button = new JButton("Am I in your way yet");
JPanel contentPane = new JPanel() {
#Override
public boolean isOptimizedDrawingEnabled() {
return false;
}
};
contentPane.setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
// Change this to reposition the button some where else
gbc.anchor = GridBagConstraints.FIRST_LINE_END;
gbc.insets = new Insets(32, 32, 32, 32);
gbc.ipadx = 16;
gbc.ipady = 16;
contentPane.add(button, gbc);
gbc = new GridBagConstraints();
gbc.gridx = 0;
gbc.gridy = 0;
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.fill = GridBagConstraints.BOTH;
JScrollPane scrollPane = new JScrollPane(textArea);
contentPane.add(scrollPane, gbc);
add(contentPane);
}
}
}
You should probably also look at How to Use Scroll Panes and the section on Providing Custom Decorations for some alternatives
Swing is designed/optimized to display/paint components in 2 dimensions. The vast majority of layout managers will make sure that the components don't overlap.
This means that you can't use a layout manager on your layered pane (if you want the components to overlap). Instead, you must manually set the size/location of components on each layer.
When you use a JLayeredPane the painting of components on each layer is managed so that the higher layer is painted last.
So your code might be changed to something like:
import java.awt.*;
import javax.swing.*;
public class Main
{
public static void main(String[] args)
{
JFrame window = new JFrame();
window.setSize(600, 400);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Making the LayeredPane
JLayeredPane LP = new JLayeredPane();
//Making the ScrollPane and JTextArea
JTextArea textArea = new JTextArea(20,40);
textArea.setText("Test Text");
textArea.setSize( textArea.getPreferredSize() );
JScrollPane back = new JScrollPane( textArea);
back.setSize( textArea.getSize() );
JButton button = new JButton("test");
button.setBounds(200,200,50,20);
LP.add(back, new Integer(0));
LP.add(button, new Integer(1));
window.add(LP);
window.setVisible(true);
}
}
There is one layout manager in the JDK, the OverlayLayout, which is designed to stack components on top of one another. However, even this layout manager does not paint components properly when the components overlap. The trick when using this layout manager is to override the isOptimizedDrawing() method of the panel using the layout manager to make sure all components are repainted all the time. In this case, make sure the bottom panel is always painted before the top panel.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class Main2
{
public static void main(String[] args)
{
JFrame window = new JFrame();
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel contentPane = new JPanel()
{
#Override
public boolean isOptimizedDrawingEnabled()
{
return false;
}
};
contentPane.setLayout( new OverlayLayout(contentPane) );
JPanel top = new JPanel( new GridBagLayout() );
top.setBorder( new EmptyBorder(0, 0, 16, 16) );
top.setOpaque(false);
top.setAlignmentX(1.0f);
top.setAlignmentY(1.0f);
GridBagConstraints gbc = new GridBagConstraints();
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.anchor = GridBagConstraints.LAST_LINE_END;
JButton button = new JButton("test");
top.add(button, gbc);
contentPane.add(top);
//Making the ScrollPane and JTextArea
JTextArea textArea = new JTextArea(10,25);
textArea.setText("Test Text");
JScrollPane back = new JScrollPane( textArea);
back.setAlignmentX(1.0f);
back.setAlignmentY(1.0f);
contentPane.add(back);
window.add(contentPane);
window.pack();
window.setVisible(true);
}
}
The benefit of this approach is that the button will move as the frame is resized.
However, as a user I would still get annoyed with a button appearing over top of the text in my text area.
Edit:
If you really need components to overlap then I would suggest you could:
look at MadProgrammers solution to use a GridBagLayout. This approach gives far more control over the alignment of the components
check out the Overlap Layout which also provides more flexibility when aligning overlapping components
It should be noted that both above approaches may still require you to override the isOptimizedDrawEnabled(...) method to make sure components are painted properly. I am not aware of any layout manager the allows you to overlap components and works without this override.

How do I remove the empty space in a JFrame?

I'm relatively new to Java GUI programming, and although the concepts are coming along well in my head, there's this small issue I'm having with removing the empty space in a JFrame.
If you run the code I have below, you'll notice that there's a considerable gap between the JTextFields and the JButton. I'd like the remove this gap so that the button is touching the very bottom JTextField.
Also, another very minor question. How do I increase the height of the JTextFields so that they have three rows instead of just one?
Anyway here's the code.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class HW4GUI extends JFrame
{
private JButton jbtAction;
private JTextField jtfFName;
private JTextField jtfLName;
private JTextField jtfLibNo;
private static int nextLibNo;
private JPanel textPanel;
public HW4GUI()
{
super("HW4GUI");
makeFrame();
showFrame();
}
public void makeFrame()
{
setLayout(new BorderLayout());
setResizable(false);
textPanel = new JPanel();
textPanel.setLayout(new GridLayout(3,2));
jbtAction = new JButton("Add Borrower");
JLabel FirstNameLabel = new JLabel("FirstName:");
jtfFName = new JTextField(3);
JLabel LastNameLabel = new JLabel("LastName:");
jtfLName = new JTextField(3);
JLabel LibNoLabel = new JLabel("Library Number:");
jtfLibNo = new JTextField(3);
FirstNameLabel.setHorizontalAlignment(JTextField.RIGHT);
LastNameLabel.setHorizontalAlignment(JTextField.RIGHT);
LibNoLabel.setHorizontalAlignment(JTextField.RIGHT);
jtfLibNo.setEditable(false);
textPanel.add(FirstNameLabel);
textPanel.add(jtfFName);
textPanel.add(LastNameLabel);
textPanel.add(jtfLName);
textPanel.add(LibNoLabel);
textPanel.add(jtfLibNo);
add(textPanel, BorderLayout.NORTH);
add(jbtAction, BorderLayout.SOUTH);
}
public void showFrame()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLocationRelativeTo(null);
setSize(400,200);
setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
}
}
Both of these are very easy fixes. For the first question, just set the second to "CENTER" instead of "SOUTH", and for the second question you can make it a JTextarea, which is multiple lines, or a JScrollPane which can scroll so it has a very large size.
P.S. you should always include a main method to run from like this one below.
public static void main(String[] args){
new HW4GUI();
}

Java Swing Component Resizing with GridBagLayout

I've been having trouble with my Java Swing GUI. First of all, I created a panel with the GridBagLayout on it and added all my labels to it. However, I also created a panel to the right of the other JPanel that adds in a button and 2 sliders which are suppose to like match with the labels.
The problem is that the JLabels are smaller than the components from the right of the other panel which, makes it look like this....
ex. water option -- JSLIDER (the jslider looks a lot bigger)
I tried to make the components larger by adding ipadx to a bigger value and also I've tried to set the grid width for the labels panel bigger, but nothing seems to work. It just doesn't respond.
Here is the code:
package gui;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
public class Options extends JFrame{
//water option (button changes text on or off)
//make a button listener
JSlider renderDistance;
JSlider grassDensity;
JButton waterToggleButton;
JLabel rdTitle;
JLabel gdTitle;
JLabel wtTitle;
JButton buttonClose;
static final int RD_MIN_VALUE = 0;
static final int RD_MAX_VALUE = 1000;
static final int RD_INIT_VALUE = 500;
static final int GD_MIN_VALUE = 0;
static final int GD_MAX_VALUE = 1000;
static final int GD_INIT_VALUE = 500;
public Options() {
this.setTitle("Settings");
this.setSize(getMaximumSize());
this.setLocationRelativeTo(null);
createView();
this.setVisible(true);
}
private void createView() {
//Making panels and adding them to window*
JPanel pOptions = new JPanel();
this.add(pOptions);
//These are for the labels that I added so people know which option they are using
JPanel pOptionLabels = new JPanel(new GridBagLayout());
pOptions.add(pOptionLabels);
//These are for the middle columns, the objects like button and slider
JPanel pOptionObjects = new JPanel(new GridBagLayout());
pOptions.add(pOptionObjects);
//Making panels and adding them to window*
//Initializing Objects*
GridBagConstraints gbcLabels = new GridBagConstraints();
GridBagConstraints gbcObjects = new GridBagConstraints();
renderDistance = new JSlider(RD_MIN_VALUE, RD_MAX_VALUE, RD_INIT_VALUE);
grassDensity = new JSlider(GD_MIN_VALUE, GD_MAX_VALUE, GD_INIT_VALUE);
waterToggleButton = new JButton("On");
rdTitle = new JLabel("Render Distance");
gdTitle = new JLabel("Grass Density");
wtTitle = new JLabel("Water Terrain Visibility");
//Initializing Objects*
//Giving objects some attributes using methods*
renderDistance.setMinorTickSpacing(100);
renderDistance.setMajorTickSpacing(500);
renderDistance.setPaintTicks(true);
renderDistance.setPaintLabels(true);
grassDensity.setMinorTickSpacing(100);
grassDensity.setMajorTickSpacing(500);
grassDensity.setPaintTicks(true);
grassDensity.setPaintLabels(true);
gbcLabels.gridx = 0;
gbcLabels.gridy = 0;
gbcLabels.anchor = GridBagConstraints.LINE_START;
gbcObjects.gridx = 0;
gbcObjects.gridy = 0;
waterToggleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if(waterToggleButton.getText().equals("Off")) {
waterToggleButton.setText("On");
}else {
waterToggleButton.setText("Off");
}
}
});
//Giving objects some attributes using methods*
//Add things to panel ex. p.add();
pOptionLabels.add(rdTitle, gbcLabels);
gbcLabels.gridy++;
pOptionLabels.add(gdTitle, gbcLabels);
gbcLabels.gridy++;
pOptionLabels.add(wtTitle, gbcLabels);
gbcObjects.gridx++;
pOptionObjects.add(renderDistance, gbcObjects);
gbcObjects.gridy++;
pOptionObjects.add(grassDensity, gbcObjects);
gbcObjects.gridy++;
pOptionObjects.add(waterToggleButton, gbcObjects);
}
public static void main(String[] args) {
new Options();
}
}
Change the way you are thinking. Instead of trying to layout all the labels and all the "other" components in separate containers, consider putting them all in the same container.
This way, when they are laid out, the layout calculations are made in relationship to all the components in a single context.
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JSlider;
public class Options extends JFrame {
//water option (button changes text on or off)
//make a button listener
JSlider renderDistance;
JSlider grassDensity;
JButton waterToggleButton;
JLabel rdTitle;
JLabel gdTitle;
JLabel wtTitle;
JButton buttonClose;
static final int RD_MIN_VALUE = 0;
static final int RD_MAX_VALUE = 1000;
static final int RD_INIT_VALUE = 500;
static final int GD_MIN_VALUE = 0;
static final int GD_MAX_VALUE = 1000;
static final int GD_INIT_VALUE = 500;
public Options() {
this.setTitle("Settings");
createView();
pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
private void createView() {
//Making panels and adding them to window*
JPanel pOptions = new JPanel();
this.add(pOptions);
//These are for the labels that I added so people know which option they are using
JPanel pOptionLabels = new JPanel(new GridBagLayout());
pOptions.add(pOptionLabels);
GridBagConstraints gbc = new GridBagConstraints();
renderDistance = new JSlider(RD_MIN_VALUE, RD_MAX_VALUE, RD_INIT_VALUE);
grassDensity = new JSlider(GD_MIN_VALUE, GD_MAX_VALUE, GD_INIT_VALUE);
waterToggleButton = new JButton("On");
rdTitle = new JLabel("Render Distance");
gdTitle = new JLabel("Grass Density");
wtTitle = new JLabel("Water Terrain Visibility");
//Initializing Objects*
//Giving objects some attributes using methods*
renderDistance.setMinorTickSpacing(100);
renderDistance.setMajorTickSpacing(500);
renderDistance.setPaintTicks(true);
renderDistance.setPaintLabels(true);
grassDensity.setMinorTickSpacing(100);
grassDensity.setMajorTickSpacing(500);
grassDensity.setPaintTicks(true);
grassDensity.setPaintLabels(true);
gbc.gridx = 0;
gbc.gridy = 0;
gbc.anchor = GridBagConstraints.LINE_START;
gbc.insets = new Insets(2, 2, 2, 2);
waterToggleButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (waterToggleButton.getText().equals("Off")) {
waterToggleButton.setText("On");
} else {
waterToggleButton.setText("Off");
}
}
});
//Giving objects some attributes using methods*
//Add things to panel ex. p.add();
pOptionLabels.add(rdTitle, gbc);
gbc.gridy++;
pOptionLabels.add(gdTitle, gbc);
gbc.gridy++;
pOptionLabels.add(wtTitle, gbc);
gbc.gridx++;
gbc.gridy = 0;
pOptionLabels.add(renderDistance, gbc);
gbc.gridy++;
pOptionLabels.add(grassDensity, gbc);
gbc.gridy++;
pOptionLabels.add(waterToggleButton, gbc);
}
public static void main(String[] args) {
new Options();
}
}
What you are experiencing is technically correct since you have two JPanels with two separate instances of the GridBagLayout manager so the rows (and columns) in one layout manager have no information about the rows in the other.
A single GridBagLayout is the way to go if you plan to use JComponents inside them and create a table like appearance. You can later add others JPanels (with different layout managers or with more GridBagLayouts!) but INSIDE your main GridBagLayout JPanel.
GridBagLayout is a really powerful layout, I would recommend sticking with is and master it since it will help you layout things nicely (and fast) in the long run.
This is a great resource to learn more about the GridBagLayout:
https://docs.oracle.com/javase/tutorial/uiswing/layout/gridbag.html
One small comment: IMO the GridBagLayout is not the best layout manager to use as the "root" layout container. I would instead recommend creating a "root" JPanel with a BorderLayout() and add your GridBagLayout JPanel to the center of the BorderLayout panel add(yourGridPanel, BorderLayout.CENTER) since the BorderLayout usually gives for free the ability for things to fill horizontally/vertically and will let you pack your window nicely at the center (with the possibility of adding more things later to the NORTH/SOUTH/EAST/WEST sides).
Good luck!

BorderLayout, GridLayout, GridBagLayout? Which should I use?

I'm trying to create this basic GUI, but cannot get my panels to setup correctly.(Numbers are pixel sizes)
I've tried using this tutorial as a reference (http://www.youtube.com/watch?v=Kl3klve_rmQ) but, mine never works the same.
My code declares variables in the top of the class, then creates a constructor which add the components (panels, buttons, etc), then it calls the constructor in the main method.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class FinalProject extends JPanel
{
private static final long serialVersionUID = 1L;
static JPanel nav;
static JPanel queue;
static JPanel menu;
GridBagConstraints gbc = new GridBagConstraints();
public FinalProject()
{
nav = new JPanel();
nav.setLayout(new GridBagLayout());
nav.setBackground(Color.RED);
gbc.gridy = 0;
gbc.gridx = 0;
gbc.gridheight = 1;
gbc.gridwidth = 1;
add(nav, gbc);
queue = new JPanel();
queue.setLayout(new GridBagLayout());
queue.setBackground(Color.GREEN);
gbc.gridy = 1;
gbc.gridx = 1;
gbc.gridheight = 1;
gbc.gridwidth = 1;
add(queue, gbc);
menu = new JPanel();
menu.setLayout(new GridBagLayout());
menu.setBackground(Color.BLUE);
gbc.gridy = 2;
gbc.gridx = 2;
gbc.gridheight = 1;
gbc.gridwidth = 1;
add(menu, gbc);
}
public static void main(String[] args)
{
FinalProject p = new FinalProject();
JFrame f = new JFrame();
f.add(nav);
f.add(queue);
f.add(menu);
f.setTitle("Subway");
f.setSize(800, 500);
f.setLocationRelativeTo(null);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
f.setResizable(false);
f.add(p);
}
}
How should I go about getting this layout right? Panels in Panels, Panels independent of each other, etc.?
No, don't use a GridBagLayout for this as you'll be adding more complexity than is actually needed. Myself, I try to avoid using this layout and all its potential pitfalls as much as possible, and usually you can get all you need by nesting JPanels, each using its own more simple layout. For instance here, all you need is a BorderLayout:
Place the top JPanel in the BorderLayout.NORTH position
Place the left JPanel in the BorderLayout.WEST position
Place the center JPanel in the BorderLayout.CENTER position.
That's it.
Again, please check out the Swing and the layout manager tutorials as the information is well presented there.
Edit
Note that nothing shows up on your JFrame because you're not adding your JPanel to the JFrame!
Edit 2
For example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagLayout;
import javax.swing.*;
public class SimpleLayout {
private static final Color GREEN = new Color(200, 255, 200);
private static final Color BLUE = new Color(200, 200, 255);
private static void createAndShowGui() {
JFrame frame = new JFrame("SimpleLayout");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// note that a JFrame's contentPane uses BorderLayout by default
frame.getContentPane().add(new ColorPanel(Color.pink, 800, 80), BorderLayout.NORTH);
frame.getContentPane().add(new ColorPanel(GREEN, 300, 420), BorderLayout.WEST);
frame.getContentPane().add(new ColorPanel(BLUE, 500, 420), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
class ColorPanel extends JPanel {
private static final float FONT_POINTS = 24f;
private int prefW;
private int prefH;
public ColorPanel(Color color, int prefW, int prefH) {
setBackground(color);
this.prefW = prefW;
this.prefH = prefH;
// GBL can be useful for simply centering components
setLayout(new GridBagLayout());
String text = String.format("%d x %d", prefW, prefH);
JLabel label = new JLabel(text, SwingConstants.CENTER);
label.setFont(label.getFont().deriveFont(FONT_POINTS));
label.setForeground(Color.gray);
add(label);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(prefW, prefH);
}
}
This displays as:
GridLayout I've never actually used but I'm not sure if it can do this.
BorderLayout can do this and is simpler to use than GridBagLayout.
GridBagLayout can do this.
In general GridBagLayout is one of the most flexible LayoutManagers and is well worth learning so I'd recommend using it here to get used to it on a simple case before you need it anyway for something more complex.
If you are familiar with HTML then think of GridBagLayout as working like HTML tables.
Very quickly - create the three panels and set the sizes/borders/whatever you need in them.
Add one in cell 0,0 with a colspan of 2.
Add one in cell 1,0
Add one in cell 1,1
After that you are done although you will probably want to specify resize/anchor/etc behaviour.

Java scroll JScrollPane with JPanel within to bottom

I have a JScrollPane with a very high JPanel inside, that is changed dynamically, items being appended at its end. What I want, is to scroll to the bottom of aforementioned JScrollPane in order for the newly appended items to be visible instantly on addition (they are not appended to the scroll pane directly, but to its JPanel, and are private objects, so cannot be referenced.
How can I simply have that scroll pane scroll to the very bottom?
Thanks in advance!
JComponent.scrollRectToVisible(Rectangle). Call that on the JPanel instance.
E.G.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ScrollToNewLabel {
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JPanel gui = new JPanel(new BorderLayout(3,3));
final JPanel panel = new JPanel(new GridLayout(0,1));
JScrollPane scroll = new JScrollPane(panel);
scroll.setPreferredSize(new Dimension(80,100));
gui.add(scroll, BorderLayout.CENTER);
JButton addLabel = new JButton("Add Label");
gui.add(addLabel, BorderLayout.NORTH);
ActionListener listener = new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent ae) {
panel.add(new JLabel("Label " + ++counter));
panel.revalidate();
int height = (int)panel.getPreferredSize().getHeight();
Rectangle rect = new Rectangle(0,height,10,10);
panel.scrollRectToVisible(rect);
}
};
addLabel.addActionListener(listener);
JOptionPane.showMessageDialog(null, gui);
}
});
}
}
Screen shot
E.G. 2
This e.g. is based on Vincent's answer, to use JScrollPane.getVerticalScrollBar().setValue(height). Where height is the preferred height of the panel in pixels.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class ScrollToNewLabel {
public static void main(String[] args) {
SwingUtilities.invokeLater( new Runnable() {
public void run() {
JPanel gui = new JPanel(new BorderLayout(3,3));
final JPanel panel = new JPanel(new GridLayout(0,1));
final JScrollPane scroll = new JScrollPane(panel);
scroll.setPreferredSize(new Dimension(80,100));
gui.add(scroll, BorderLayout.CENTER);
JButton addLabel = new JButton("Add Label");
gui.add(addLabel, BorderLayout.NORTH);
ActionListener listener = new ActionListener() {
int counter = 0;
public void actionPerformed(ActionEvent ae) {
panel.add(new JLabel("Label " + ++counter));
panel.revalidate();
int height = (int)panel.getPreferredSize().getHeight();
scroll.getVerticalScrollBar().setValue(height);
}
};
addLabel.addActionListener(listener);
JOptionPane.showMessageDialog(null, gui);
}
});
}
}
scrollRectToVisible(...) and scrollBar.setValue(...) are the general solutions.
You may be interested in Scrolling a Form which ensures that when you tab to a component the form will scroll automatically to make sure the the component will be visible in the scrollpane. Behind the scenes it uses scrollRectToVisible().
A simple way to move the scrollbar all the way to the bottom is to set its value to 100 like this:
scroll.getVerticalScrollBar().setValue(100);
This causes it to move to the bottom of the viewport. You can add this after the component is added to the panel.
This is how I scroll down programmatically.
I like how it scrolls to the bottom rather smoothly instead of jumping there immediately.
/**
* Scrolls a {#code scrollPane} to its bottom.
*
* #param scrollPane
* the scrollPane that we want to scroll all the way down
*
*/
private void scrollDown(JScrollPane scrollPane) {
JScrollBar verticalBar = scrollPane.getVerticalScrollBar();
int currentScrollValue = verticalBar.getValue();
int previousScrollValue = -1;
while (currentScrollValue != previousScrollValue) {
// Scroll down a bit
int downDirection = 1;
int amountToScroll = verticalBar.getUnitIncrement(downDirection);
verticalBar.setValue(currentScrollValue + amountToScroll);
previousScrollValue = currentScrollValue;
currentScrollValue = verticalBar.getValue();
}
}

Categories