JScrollPane doesn't work with JTextArea - java

I'm trying to add the ScrollPane to my TextArea, but it doesn't appear.
Here's the code:
import javax.swing.*;
public class PracownikGui extends JFrame {
private JPanel Panelek;
private JTextArea Tekscik;
private JScrollPane Skrol;
public PracownikGui() {
setMinimumSize(new Dimension(600, 600));
setLocationRelativeTo(null);
setContentPane(Panelek);
setResizable(false);
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
Tekscik();
public void Tekscik() {
Tekscik = new JTextArea(2, 10);
Skrol = new JScrollPane( Tekscik );
Tekscik.setSize(300, 300);
Tekscik.setLocation(20, 70);
Tekscik.setEditable(true);
Tekscik.setLineWrap(true);
add(Tekscik);
}}
Any help, please.

You're shooting yourself in the foot by setting a JTextArea's size or preferredSize since this prevents it from expanding into the JScrollPane:
Tekscik.setSize(300, 300);
set its rows and columns only.
Also you need to add the JScrollPane to the GUI, not the JTextArea.
Also, while null layouts and setBounds() or setSize(...) and setLocation(...) might seem to Swing newbies like the easiest and best way to create complex GUI's, the more Swing GUI'S you create the more serious difficulties you will run into when using them. They won't resize your components when the GUI resizes, they are a royal witch to enhance or maintain, they fail completely when placed in scrollpanes, they look gawd-awful when viewed on all platforms or screen resolutions that are different from the original one.
e.g.,
import javax.swing.*;
public class PracownikPanel extends JPanel {
private JTextArea tekscik = new JTextArea(5, 25);
public PracownikPanel() {
tekscik.setLineWrap(true);
tekscik.setWrapStyleWord(true);
JScrollPane skrol = new JScrollPane(tekscik);
skrol.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
add(skrol);
}
private static void createAndShowGui() {
PracownikPanel mainPanel = new PracownikPanel();
JFrame frame = new JFrame("PracownikPanel");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

I made quite a few changes to your code. Your code wouldn't run.
Here's the GUI I created.
As you can see, there's a vertical scroll bar. The default action for the scroll bar is that it doesn't appear until you've filled the JTextArea with text.
Here are the important changes I made to your code.
Class names start with a capital letter. Method names and variable names start with a lower case letter.
A Swing application must start with a call to the SwingUtilities invokeLater method. This ensures that the Swing components are created and used on the Event Dispatch thread (EDT). Since the invokeLater method requires a Runnable, I had the PracownikGui class implement Runnable.
You use Swing components. You don't extend Swing components, or any other Java class, unless you want to override one of the methods in that class.
I removed all of the sizing and positioning statements, except for the statement that defines the rows and columns of the JTextArea. Hovercraft Full Of Eels explained this, but you use Swing layouts to get the arrangement of Swing components you want. The default layout for a JPanel is the FlowLayout. The default layout for a JFrame is the BorderLayout.
I added the JScrollPane to the JPanel. I added the JPanel to the JFrame.
Here's the code.
package com.ggl.testing;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public class PracownikGui implements Runnable {
public static void main(String[] args) {
SwingUtilities.invokeLater(new PracownikGui());
}
private JFrame frame;
private JPanel panelek;
private JTextArea tekscik;
private JScrollPane skrol;
#Override
public void run() {
frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
panelek = new JPanel();
tekscik(panelek);
frame.setContentPane(panelek);
frame.setResizable(false);
frame.pack();
frame.setVisible(true);
}
public void tekscik(JPanel panelek) {
tekscik = new JTextArea(2, 20);
tekscik.setEditable(true);
tekscik.setLineWrap(true);
skrol = new JScrollPane(tekscik);
panelek.add(skrol);
}
}

Related

Troubles with Java Swing

I have the following code:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class Main
{
public static void main(String[] args)
{
Window window = new Window("This is a title", 450, 350);
JButton buttonExit = new Button("Exit", 75, 25);
window.addElement(buttonExit);
window.build();
}
}
class Window // extend the current class
{
public Window window;
public JFrame frame;
public JPanel panel;
public String title;
// instantiate object with the constructor
public Window(String title, int width, int height)
{
this.frame = new JFrame(title);
this.frame.setPreferredSize(new Dimension(width, height));
this.frame.setLocationRelativeTo(null); // centers the main window relative to the center of the screen dimension
this.panel = new JPanel();
this.panel.setPreferredSize(new Dimension(width, height));
//this.panel.setLayout(new FlowLayout());
this.frame.add(panel);
}
public void build()
{
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame.pack(); // removes all unnecessary pixel space from the form
this.frame.setVisible(true);
this.frame.setSize(frame.getPreferredSize());
}
public void addElement(JButton element)
{
this.panel.add(element);
}
}
class Button extends JButton // extend the current class
{
public Button(String text, int width, int height)
{
JButton button = new JButton();
button.setPreferredSize(new Dimension(width, height));
button.setText(text);
button.setVisible(true);
new ButtonHandler(button);
}
}
class ButtonHandler implements ActionListener
{
public ButtonHandler(JButton button)
{
button.addActionListener(this);
}
public void actionPerformed(ActionEvent actionEvent) {
System.exit(0);
}
}
I have two problems with this:
The button is compressed and won't show its text
I cannot get the event handler to work and don't appear to get why
As a side note, I know that I don't specify a LayoutManager here, but I had this implemented before and it didn't solve my issue (I tried the FlowLayoutManager and the GridBagLayout [this would be my desired one, due to its flexibility]).
Can someone tell me, what I am doing wrong here? I've only worked with C# and WPF/WinForms before...
Issue 1:
Your custom Button class is-a JButton but also has-a JButton (named button) in the constructor.
The problem here is you install the ButtonHandler class to the button of the constructor, not the custom Button itself (which is referred to as this inside the constructor).
Issue 2:
When you set the [preferred] size of the JFrame property named frame (in the custom class named Window), you are not setting the frame's contents' [preferred] size, but the size of the whole JFrame, which includes the bar located at the top of the frame (which has the title of the frame).
That lets the contents of the frame to have a space less than the preferred size, because the preferred size is set to the whole frame.
I know, you are also setting the preferred size of the JPanel named panel, which is added to the frame, but when you pack the frame, then the preferred size of the frame is prioritized rather than the preferred size of the contents of the frame, so that's probably why you are seeing the button compressed.
Let me demonstrate what I mean, with a bit of code:
import java.awt.Dimension;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class TestFramePrefSz {
public static void main(final String[] args) {
SwingUtilities.invokeLater(() -> {
final JFrame frame = new JFrame("Testing JFrame preferred size");
final JPanel contents = new JPanel();
contents.setPreferredSize(new Dimension(200, 200));
frame.setPreferredSize(new Dimension(200, 200));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(contents);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
System.out.println(contents.getSize());
});
}
}
As you can see, the dimension object printed (which is the actual size of the panel) is about 184x161 rather than 200x200 requested, because the preferred size of the frame is also set to 200x200 (which includes the title of the frame etc...).
The solution, is to only set the preferred size of the contents, not the frame (in this particular scenario at least).
So you should:
Remove the line this.frame.setSize(frame.getPreferredSize()); inside the build method.
Remove the line this.frame.setPreferredSize(new Dimension(width, height)); inside the constructor of the custom class named Window.
Issue 3:
The line this.frame.setLocationRelativeTo(null); inside the constructor of the custom class named Window, is not effective in that place.
Imagine that, when you call this method, it has to determine the location of the frame to set it.
So it needs to know first of the size of the screen and then the size of the frame itself.
But what is the size of the frame at the point where you call this method? It is about 0x0. Not the preferred size as you might expect.
That makes the calculation of the frame's location to be such that the frame will not be centered at the screen.
That's because the preferred size is a property of the frame, which is a different property than the size.
So you either have to setSize prior making the call, or better to set the preferred size of the contents of the frame (ie this.panel), then call pack on the frame and finally call the method this.frame.setLocationRelativeTo(null).
Then you are free to set the frame to visible to see where it is located in the screen (ie should be centered).
So the solution is to follow a pattern like the following:
Create the frame, add the contents of the frame to it and set the contents' preferred size.
Call pack on the frame (remember this call will change the size of the frame, according to the preferred sizes of the contents of the frame or the frame's itself).
Call setLocationRelativeTo(null) on the frame.
Call setVisible(true) on the frame.
If you take a look at your code, you are instead doing:
Create the frame.
Set the preferred size of the frame.
Call setLocationRelativeTo(null) on the frame (but the size of the frame is not set yet).
Add the contents of the frame to it (ie the panel).
Call addElement which adds more content to the panel.
Call pack on the frame (remember the preferred size of the frame is set up to this point, so it will override any other preferred sizes, such as the contents' preferred size).
Call setVisible(true) on the frame.
Call setSize on the frame, with the preferred size of it. So you are overwriting the size the frame has had from step 6.
I don't know what you're using as a tutorial. I recommend the Oracle tutorial, Creating a GUI With JFC/Swing. You can skip the Netbeans section, but I recommend going through the rest of the sections.
I created the following GUI.
The Exit button works, disposing of the GUI. The X in the upper right also disposes of the GUI.
Here's the runnable example code. The explanation follows the code.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class JButtonExample implements Runnable{
public static void main(String[] args) {
SwingUtilities.invokeLater(new JButtonExample());
}
private JFrame frame;
#Override
public void run() {
frame = new JFrame("This is a title");
frame.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
frame.addWindowListener(new WindowAdapter() {
#Override
public void windowClosing(WindowEvent e) {
exitProcedure();
}
});
frame.add(createMainPanel(), BorderLayout.CENTER);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createMainPanel() {
JPanel panel = new JPanel(new BorderLayout());
panel.setPreferredSize(new Dimension(300, 200));
panel.setBorder(BorderFactory.createEmptyBorder(
75, 100, 75, 100));
JButton button = new JButton("Exit");
button.addActionListener(new ExitListener(this));
panel.add(button, BorderLayout.CENTER);
return panel;
}
public void exitProcedure() {
frame.setVisible(false);
frame.dispose();
System.exit(0);
}
public class ExitListener implements ActionListener {
private JButtonExample example;
public ExitListener(JButtonExample example) {
this.example = example;
}
#Override
public void actionPerformed(ActionEvent event) {
example.exitProcedure();
}
}
}
I make a call to the SwingUtilities invokeLater method from the main method. This method makes sure that the Swing components are created and executed on the Event Dispatch Thread.
I separate the JFrame code from the JPanel code. This is so I can focus on one part of the GUI at a time.
The JFrame methods have to be called in a specific order. This is the order that I use for most of my Swing applications.
The WindowListener (WindowAdapter) gives my code control over the closing of the JFrame. This will allow the Exit button actionListener to close the JFrame. A WindowListener is not a simple concept.
The JFrame defaultCloseOperation is usually set to EXIT_ON_CLOSE. In order for the WindowListener to work, I had to set the defaultCloseOperation to DO_NOTHING_ON_CLOSE.
I let the JFrame determine its own size by using the pack method.
I set the preferred size of the JPanel.
I created an empty border for the JPanel, so the JButton would expand to fill the rest of the JPanel. That's what happens to the component placed in the center of a BorderLayout.
I created an ExitListener class. Because it's an inner class, I didn't have to create a constructor or pass the JButtonExample instance. I created a constructor so you can see how it's done, and how the actionListener method can execute the exitProcedure method of the JButtonExample class.
I hope this JButton example is helpful. The WindowListener is a bit advanced for a simple example, but you can see how it's done.

Java swing JScrollPane not scrollable

This is the add(main) version
This is the add(scroll) version
Im trying to get a window full of lables and make it scrollable, this is my code for that purpose:
public class JobHistoryListScreen extends JFrame implements View
{
#Override
public void showScreen()
{
setSize(800, 800);
setLayout(new BorderLayout());
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel main = new JPanel();
main.setSize(500,500);
JScrollPane scroll = new JScrollPane(main,JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setSize(500,500);
//Font
//Font david50 = new Font("David", Font.BOLD, 50);
for(int i=0; i<1000; i++)
{
JLabel empty = new JLabel("No jobs to display!");
empty.setBounds(0,i+250,400,100);
empty.setFont(david50);
main.add(empty);
}
add(main);
setVisible(true);
}
public static void main(String[] args) {
JobHistoryListScreen v = new JobHistoryListScreen();
v.showScreen();
}
}
For some reason the window gets filled with the labels but is not scrollable at all.
Learn about layout managers. Refer to Laying Out Components Within a Container. Default for JPanel is FlowLayout and because the JPanel is inside a JScrollPanel, the labels will not wrap. And since you set the horizontal scroll bar policy to NEVER, there is no horizontal scroll bar and hence you cannot scroll horizontally. Try using BoxLayout to display all the labels one under the other. Alternatively you could use a GridLayout with 0 (zero) rows and 1 (one) column. Refer to the tutorial for more details.
EDIT
Here is my modified version of your code. Explanatory notes appear after the code.
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.WindowConstants;
public class JobHistoryListScreen implements Runnable {
private JFrame frame;
#Override // java.lang.Runnable
public void run() {
showScreen();
}
public void showScreen() {
frame = new JFrame("Jobs");
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
JPanel main = new JPanel(new GridLayout(0, 1));
JScrollPane scroll = new JScrollPane(main,
JScrollPane.VERTICAL_SCROLLBAR_ALWAYS,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER);
scroll.setPreferredSize(new Dimension(500, 500));
Font david50 = new Font("David", Font.BOLD, 50);
for(int i=0; i<1000; i++) {
JLabel empty = new JLabel("No jobs to display!");
empty.setFont(david50);
main.add(empty);
}
frame.add(scroll);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
JobHistoryListScreen v = new JobHistoryListScreen();
// Launch Event Dispatch Thread (EDT)
EventQueue.invokeLater(v);
}
}
I don't know what interface View is so I removed that part.
No need to extend class JFrame.
No need to explicitly call setSize() on JFrame. Better to call pack().
Default content pane for JFrame is JPanel and default layout manager for that JPanel is BorderLayout so no need to explicitly set.
No need to call setSize() on JPanel.
Call setPreferredSize() rather than setSize() on JScrollPane.
Add the JScrollPane to the JFrame and not the JPanel.
No need to call setBounds() because GridLayout handles this.
Explicitly launch EDT (Event Dispatch Thread) by calling invokeLater().
Here is a screen capture of the running app. Note the vertical scroll bar.

How can I append a JTextArea from a method that doesn't contain the JTextArea?

I'm making some test code to practice OOP, and I want to append a JTextArea from the "writeToArea" to the "initialize" method where the JTextArea is defined and initialized. I already tried to directly call the "output" variable, but this returns an "output cannot be resolved" error. I want so that whenever I call the "writeToArea" method in the main class, I'll be able to add lines to the "output" JTextArea in the "initialize" method.
Here's the main class:
public class Pangea {
public static void main(String[] args) {
UI.initialize();
UI.writeToArea();
}
}
Here's the initialize class:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Font;
import javax.swing.JFrame;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.JTextField;
public class UI {
static void initialize() {
System.out.println("Initializing GUI.");
JFrame frame = new JFrame();
Font myFont = new Font("Courier", Font.BOLD, 14);
JTextField input = new JTextField("");
JTextArea output = new JTextArea("Initiated Succesfully.");
output.setWrapStyleWord(true);
output.setLineWrap(true);
input.setFont(myFont);
output.setFont(myFont);
input.setForeground(Color.WHITE);
output.setForeground(Color.WHITE);
input.setBackground(Color.BLACK);
input.setCaretColor(Color.WHITE);
output.setBackground(Color.BLACK);
output.setEditable(false);
JScrollPane jp = new JScrollPane(output);
frame.setTitle("PANGEA RPG [0.01 ALPHA][WIP]");
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(input, BorderLayout.SOUTH);
frame.add(jp, BorderLayout.CENTER);
frame.pack();
frame.setSize(800, 500);
frame.setVisible(true);
System.out.println("GUI Initialized.");
}
static void writeToArea() {
System.out.println("\"writeToArea\" running.");
output.append("Hello!");
System.out.println("\"writeToArea\" finished.");
}
}
I've tried to do something similar to this: Updating jtextarea from another class but it didn't work. If anyone has any suggestions I'd be very thankful.
The main error in your code is the lack of OOP design. Making all static is poor design.
Also swing is event based, so you should append text to the textArea when an event happens. See the example i write for you.
public class UI {
private JPanel panel;
private JTextArea output;
public UI(){
initialize();
}
private void initialize() {
panel = new JPanel();
Font myFont = new Font("Courier", Font.BOLD, 14);
final JTextField input = new JTextField(""); // must be declared final cause you use it in anonymous class, you can make it instance variable if you want to as textArea
//add an actionListener then when you press enter this will write to textArea
input.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent evt){
writeToArea(input.getText());
}
});
output = new JTextArea("Initiated Succesfully",50,100);// let the component determinate its preferred size.
output.setWrapStyleWord(true);
output.setLineWrap(true);
input.setFont(myFont);
output.setFont(myFont);
input.setForeground(Color.WHITE);
output.setForeground(Color.WHITE);
input.setBackground(Color.BLACK);
input.setCaretColor(Color.WHITE);
output.setBackground(Color.BLACK);
output.setEditable(false);
JScrollPane jp = new JScrollPane(output);
panel.setLayout(new BorderLayout());
panel.add(input, BorderLayout.SOUTH);
panel.add(jp, BorderLayout.CENTER);
}
private void writeToArea(String something) {
System.out.println("\"writeToArea\" running.");
output.append(something);
System.out.println("\"writeToArea\" finished.");
}
public JPanel getPanel(){
return panel;
}
}
And in your client code
public class Pangea {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run(){
createAndShowGUI();
}
});
}
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event dispatch thread.
*/
private static void createAndShowGUI() {
//Create and set up the window.
System.out.println("Initializing GUI.");
JFrame frame = new JFrame();
frame.setTitle("PANGEA RPG [0.01 ALPHA][WIP]");
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Add contents to the window.
frame.add(new UI().getPanel());
frame.pack();//sizes the frame
frame.setVisible(true);
System.out.println("GUI Initialized.");
}
}
Here you have a tutorial with better examples than this How to Use Text Areas
I remove your setSize and use pack()
The pack method sizes the frame so that all its contents are at or
above their preferred sizes. An alternative to pack is
to establish a frame size explicitly by calling setSize or setBounds
(which also sets the frame location). In general, using pack is
preferable to calling setSize, since pack leaves the frame layout
manager in charge of the frame size, and layout managers are good at
adjusting to platform dependencies and other factors that affect
component size.
Read the section from the Swing tutorial on How to Use Text Areas. It will show you how to better structure your code so that you don't use static methods and variables everywhere.
Once you have a panel that has a reference to the text area you can add methods that allow you to update the text area on the panel.

Newbie JLayeredPane issue

I just can't get past square one on JLayeredPanes. (See my original question of yesterday. I have been studying the JLayeredPane tutorial and API. These tutorials are geared somewhat differently to what I am ultimately trying to produce.
Going back to square one, I took Oracle's JFrame Example and modified it to include Layered panes.
Here is the code:
package components;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* FrameDemo.java requires no other files. */
public class FrameDemo {
/**
* 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("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel mainLayer = new JPanel(new BorderLayout());
mainLayer.setPreferredSize(new Dimension(640, 480));
frame.setContentPane(mainLayer);
frame.getLayeredPane().add(mainLayer, JLayeredPane.DEFAULT_LAYER, 0);
JLabel emptyLabel = new JLabel("LABEL");
emptyLabel.setPreferredSize(new Dimension(320, 240));
mainLayer.add(emptyLabel, BorderLayout.NORTH);
JPanel subLayer = new JPanel(new BorderLayout());
JLabel subLabel = new JLabel("SUBLABEL");
subLabel.setPreferredSize(new Dimension( 200, 100));
subLabel.setBackground(Color.YELLOW);
subLayer.add(subLabel, BorderLayout.SOUTH);
subLayer.setVisible(true);
subLabel.setVisible(true);
frame.getLayeredPane().add(subLayer, JLayeredPane.PALETTE_LAYER, 0);
//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();
}
});
}
}
Why doesn't it work? IOW, why doesn't the sublabel show up? It's at a higher level than the main layer.
One thought is why am I adding mainLayer to both the Content Pane and the Layered Pane? If I don't do that, nothing shows up. I.e, by commenting out this line, I just get a blank frame.
// frame.setContentPane(mainLayer);
Obviously, I'm not understanding something. But what is it?
I should add that obviously, this simple demo can be done without Layered Panes. But my ultimate goal is to have a layer that can be turned on and off programatically. But I can't even get this simple case to work. If I can get over this hump, I think the rest will be easier.
ADDENDUM:
What I want to acheive is illustrated by the following Code, which is very similar to what TrashGod set up below and it works. There is a JLayeredPane with a constant layer (layered at Integer(0)) and a floating layer layered initially at Integer(-1) but togglable by the F7 and F8 keystrokes between the Integer(-1) layer and the Integer(1) layer, thereby allowing it to float above or below the constant layer.
package components;
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
/* MyLayeredPaneDemo.java requires no other files. */
public class MyLayeredPaneDemo {
private JFrame frame;
private JLayeredPane mainPanel;
private JPanel constantLayer;
private JPanel floatingLayer;
/**
* Create the GUI and show it. For thread safety,
* this method should be invoked from the
* event-dispatching thread.
*/
private MyLayeredPaneDemo() {}
private void createAndShowGUI() {
//Create and set up the window.
this.frame = new JFrame("MyLayeredPaneDemo");
this.frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.frame.setPreferredSize(new Dimension(640, 480));
mainPanel = new JLayeredPane();
constantLayer = new JPanel(new BorderLayout(0,0));
floatingLayer = new JPanel(new BorderLayout(0,0));
// constantLayer.setPreferredSize();
constantLayer.setOpaque(true);
constantLayer.setBackground(Color.BLUE);
JLabel constantLabel = new JLabel("MAIN LAYER");
constantLayer.setPreferredSize(new Dimension(640, 480));
constantLayer.add(constantLabel, BorderLayout.CENTER);
JLabel subLabel = new JLabel("SUB LAYER");
floatingLayer.setBackground(Color.YELLOW);
floatingLayer.add(subLabel, BorderLayout.SOUTH);
floatingLayer.setOpaque(true);
floatingLayer.setVisible(true);
floatingLayer.setVisible(true);
subLabel.setBackground(Color.YELLOW);
mainPanel.add(constantLayer, new Integer(0), 0);
constantLayer.setBounds(0,0,640,480);
mainPanel.add(floatingLayer, new Integer(-1), 0);
floatingLayer.setBounds(100, 360, 300, 90 );
frame.add(mainPanel, BorderLayout.CENTER);
//Display the window.
mapKeyToAction(frame.getRootPane(),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
KeyStroke.getKeyStroke(KeyEvent.VK_F7, 0),
"Hide Layer",
new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("F7 pressed");
mainPanel.setLayer(floatingLayer, new Integer(-1));
}
});
mapKeyToAction(frame.getRootPane(),
JComponent.WHEN_ANCESTOR_OF_FOCUSED_COMPONENT,
KeyStroke.getKeyStroke(KeyEvent.VK_F8, 0),
"Show Layer",
new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("F8 pressed");
mainPanel.setLayer(floatingLayer, new Integer(1));
}
});
frame.pack();
frame.setVisible(true);
frame.getRootPane().setFocusable(true);
boolean ok = frame.getRootPane().requestFocusInWindow();
System.out.println("focus ok: " + ok);
}
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() {
new MyLayeredPaneDemo().createAndShowGUI();
}
});
}
private static void mapKeyToAction(JComponent component,
int whichMap, KeyStroke keystroke,String key, Action action) {
component.getInputMap(whichMap).put(keystroke, key);
component.getActionMap().put(key, action);
}
}
However, I am having trouble getting this to work in my real case. The difference between the two is that here, my JLayeredPane is owned by the Frame, whereas in my real application, I want the JLayeredPane to be owned by a JPanel is that some levels down in the containment hierarchy from the Frame, and whose size is set by a GridBagLoyout in its parent, and the size is therefore unknowable at the time its constructor is called, making it difficult to call setBounds() which I need to do on a child of a JLayeredPane.
FURTHER ADDENDUM. I know that the Oracle Tutorials mention a case where Layouts rather than absolute positioning is used with a JLayeredPane. The difference between this case and mine is that in my case the layers occupy the same horizontal space on different layers, whereas in this case, the components on different layrers occupy different horizontal spaces. It's almost as if we need a 3D Layout Manager!
"By default, a layered pane has no layout manager."—How to Use Layered Panes
Addendum: I need to avoid using the Frame's layered pane and instead add a layered pane to the window.
Yes, The Root Pane is an instance of JRootPane, which contains a JLayeredPane. In particular, "The layered pane contains the menu bar and content pane, and enables Z-ordering of other components."
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class FrameDemo {
private static void createAndShowGUI() {
JFrame frame = new JFrame("FrameDemo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JLayeredPane mainLayer = new JLayeredPane();
frame.add(mainLayer, BorderLayout.CENTER);
JLabel label = new JLabel("LABEL", JLabel.CENTER);
label.setBounds(100, 100, 200, 100);
label.setOpaque(true);
label.setBackground(Color.cyan);
mainLayer.add(label, 1);
JPanel subLayer = new JPanel(new BorderLayout());
JLabel subLabel = new JLabel("SUBLABEL", JLabel.CENTER);
subLabel.setBounds(20, 20, 200, 100);
subLabel.setOpaque(true);
subLabel.setBackground(Color.yellow);
subLayer.add(subLabel, BorderLayout.SOUTH);
mainLayer.add(subLabel, 2);
frame.pack();
frame.setSize(320, 240);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
createAndShowGUI();
}
});
}
}
The solution I came up and thanks to trashgod which I expect is good advice too is to implement ComponentListener and capture the component resize event. At that point you can get the actual bounds of the container and use it to set the actual bounds of the layer JPanels which are always in some fixed relation to the bounds of the component that contains them. It works.
Trashgod's solution would also work I believe but I have not tried it.

Why does my JFrame stay empty, if I subclass JPanel and JFrame?

I'm trying to write custom JFrame and JPanel for my Java application. Currently, I just want to have a JPanel with a start button in the very middle of the screen. So, here's the code I have:
package gui;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class SubitizingFrame extends JFrame implements KeyListener {
public SubitizingFrame() {
super("Subitizing");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
addKeyListener(this);
add(new LaunchPanel());
pack();
setVisible(true);
}
public void keyPressed(KeyEvent e) {
if(e.getKeyCode() == KeyEvent.VK_F5)
System.out.println("F5 pressed");
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
and here is my panel:
package gui;
import instructions.Settings;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JPanel;
#SuppressWarnings("serial")
public class LaunchPanel extends JPanel implements ActionListener {
private JButton startButton;
public LaunchPanel() {
int width = Settings.getScreenSizeX(), height = Settings.getScreenSizeY();
setPreferredSize(new Dimension(width, height));
setLayout(null);
startButton = new JButton("Start");
startButton.setLocation((width/2) - (startButton.getWidth()/2), (height/2) - (startButton.getHeight()/2));
add(startButton);
}
public void actionPerformed(ActionEvent arg0) {
// TODO Auto-generated method stub
}
}
But when the application launches, I don't see anything. Just a big gray screen.
Do not use a null layout. If you simply use the default layout manager of JPanel (i.e. FlowLayout), the JButton with "automagically" be placed in the center. Also, in order to place the JFrame in the middle of the screen, invoke setLocationRelativeTo(null).
Since it's hard to tell what you mean by "screen", this example shows how you center a JButton in a JPanel in a JFrame, that is then centered on the monitor.
public final class CenterComponentsDemo {
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable(){
#Override
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI(){
final JFrame frame = new JFrame("Center Components Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ButtonPane());
frame.setSize(new Dimension(300, 100)); // Done for demo
//frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
private static class ButtonPane extends JPanel{
public ButtonPane(){
super();
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
setBackground(Color.PINK);
final JButton button = new JButton("Start");
button.setAlignmentX(Component.CENTER_ALIGNMENT);
add(Box.createVerticalGlue());
add(button);
add(Box.createVerticalGlue());
}
}
}
Recommendations:
Avoid using null layout as this makes your app difficult to upgrade and maintain and makes it potentially very ugly or even non-usable on boxes with different OS's or screen resolutions.
If you have your JPanel use a GridBagLayout and add a single component to it without using GridBagConstraints, it will be placed in the center of the JPanel.
You almost never have to or should extend JFrame and only infrequently need to extend JPanel. Usually it's better to enhance your GUI classes through composition rather than inheritance.
Avoid having your "view" or gui classes implement your listener interfaces. This is OK for "toy" programs, but as soon as your application gains any appreciable size or complexity, this gets hard to maintain.
If you don't use any LayoutManager (which btw you probably should), then you'll need to set the size of the panel as well (along with its position).
Although we strongly recommend that you use layout managers, you can perform layout without them. By setting a container's layout property to null, you make the container use no layout manager. With this strategy, called absolute positioning, you must specify the size and position of every component within that container. One drawback of absolute positioning is that it does not adjust well when the top-level container is resized. It also does not adjust well to differences between users and systems, such as different font sizes and locales.
From: http://download.oracle.com/javase/tutorial/uiswing/layout/using.html
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class LaunchPanel extends JPanel {
private JButton startButton;
public LaunchPanel() {
int width = 200, height = 100;
setPreferredSize(new Dimension(width, height));
setLayout(new GridBagLayout());
startButton = new JButton("Start");
add(startButton);
setBorder( new LineBorder(Color.RED, 2));
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JOptionPane.showMessageDialog(null, new LaunchPanel());
}
});
}
}
addKeyListener(this);
Don't use KeyListeners. Swing was designed to be used with Key Bindings. Read the section from the Swing tutorial on How to Use Key Bindings for more information.
The tutorial also has a section on Using Layout Manager which you should read. You should not create GUI's with a null layout.

Categories

Resources