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

I'm trying to create a simple JList with a scrollbar, and therefore i need to have the JList within a JScrollPane. So far, so good. However, for some reason i can't resize/position the JScrollPane!? It sounds logic that everything inside it should stretch to 100%, so if i set the JScrollPane to be 300px wide, the elements inside will be as well. Is that correct?
While you're at it, please critisize and give me hints if i should change something or optimize it.
Anyhow, here's the code:
package train;
import java.awt.*;
import javax.swing.*;
public class GUI {
private DefaultListModel loggerContent = new DefaultListModel();
private JList logger = new JList(loggerContent);
GUI() {
JFrame mainFrame = new JFrame("title");
this.addToLog("testing testing");
this.addToLog("another test");
// Create all elements
logger = new JList(loggerContent);
JScrollPane logWrapper = new JScrollPane(logger);
logWrapper.setBounds(10, 10, 20, 50);
// Add all elements
mainFrame.add(logWrapper);
// Show everything
mainFrame.setSize(new Dimension(600, 500));
mainFrame.setVisible(true);
}
public void addToLog(String inputString) {
int size = logger.getModel().getSize();
loggerContent.add(size, inputString);
}
}
Thanks in advance,
qwerty
EDIT: Here's a screenshot of it running: http://i.stack.imgur.com/sLGgQ.png

The setVisibleRowCount() method of JList is particularly convenient for this, as suggested in the relevant tutorial. ListDemo is a good example.
Addendum:
please critisize and give me hints…
Well, since you ask: Don't invoke public methods in the constructor; make them private or invoke them after the constructor finishes. There's no need to find the last index for add(), when addElement() is available. Also, be sure to construct your GUI on the event dispatch thread .
import java.awt.*;
import javax.swing.*;
/** #see http://stackoverflow.com/questions/5422160 */
public class ListPanel extends JPanel {
private DefaultListModel model = new DefaultListModel();
private JList list = new JList(model);
ListPanel() {
list.setVisibleRowCount(5);
}
public void append(String inputString) {
model.addElement(inputString);
}
private void init() {
for (int i = 0; i < 10; i++) {
this.append("String " + String.valueOf(i));
}
JFrame mainFrame = new JFrame("GUI");
mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JScrollPane jsp = new JScrollPane(list);
mainFrame.add(jsp);
mainFrame.pack();
mainFrame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new ListPanel().init();
}
});
}
}

The bounds & size of a component are generally ignored over that of it's preferred size and the constraints of the layout being used by the container.
To solve this problem, learn how to use layouts & apply them appropriately.

Try to put your JScrollPane inside a JPanel and add the panel to the frame.
JPanel panel = new JPanel();
panel.add (logWrapper);
mainFrame.add(panel);
Then set the bounds of the panel instead of the JScrollpane
panel.setBounds(10, 10, 20, 50);
The probles is that Swing uses layout managers to control child bounds property. Adding a JScrollpane directly to the main frame, doesn't allow you to choose right bounds properly.

Related

Why can't I see the JList?

Main Class
public class Main {
public static void main(String[] args) {
new CredentialManager();
}
}
Main Window Class
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class CredentialManager extends JFrame implements ActionListener {
public CredentialManager() {
View.credentialManager.setSize(1200, 800);
View.credentialManager.setResizable(false);
View.credentialManager.setLayout(null);
View.credentialManager.setLocationRelativeTo(null);
View.credentialManager.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
View.credentialManager.setVisible(true);
View.btnCredentialManagerManageUser.setBounds(550, 50, 120, 30);
View.btnCredentialManagerSignOut.setBounds(50, 50, 95, 30);
View.listCredentials.setBounds(100,100, 75,75);
View.btnCredentialManagerManageUser.addActionListener(this);
View.btnCredentialManagerSignOut.addActionListener(this);
View.credentialManager.add(View.btnCredentialManagerManageUser);
View.credentialManager.add(View.btnCredentialManagerSignOut);
View.credentialManager.add(View.scrollPaneCredentials);
View.scrollPaneCredentials.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
View.listModelCredentials.addElement("Credential1");
View.listModelCredentials.addElement("Credential2");
View.listModelCredentials.addElement("Credential3");
View.listModelCredentials.addElement("Credential4");
View.listModelCredentials.addElement("Credential5");
View.listModelCredentials.addElement("Credential6");
}
#Override
public void actionPerformed(ActionEvent actionEvent) {
if (actionEvent.getSource().equals(View.btnCredentialManagerManageUser)) {
System.out.println("Manager");
} else if (actionEvent.getSource().equals(View.btnCredentialManagerSignOut)) {
System.out.println("Sign Out");
}
}
}
View Class with the Swing elements
import javax.swing.*;
public class View {
static JFrame credentialManager = new JFrame("Credential Manager");
static JButton btnCredentialManagerManageUser = new JButton("Manage User");
static JButton btnCredentialManagerSignOut = new JButton("Sign Out");
static DefaultListModel<String> listModelCredentials = new DefaultListModel<>();
static JList<String> listCredentials = new JList(listModelCredentials);
static JScrollPane scrollPaneCredentials = new JScrollPane(listCredentials);
}
When I execute the main class the list does not appear. I expect the main frame to contain the two button(which appears) and the list with the scrollbar but it does not appear. I have tried many things but any of them work.
the list with the scrollbar but it does not appear
If the scrollpane has a size, it will then display with the JList inside, albeit in the top left corner.
View.scrollPaneCredentials.setSize(new Dimension(300, 300));
To give you an idea of why this occurs, make the following changes:
View.credentialManager.setLayout(new FlowLayout());
// ^--- changed from "null"
... and then add these to the bottom of your constructor:
View.credentialManager.pack();
View.credentialManager.setVisible(true);
// ^---- move this to bottom
Notice, the layout manager will display your list, albeit on the far right.
To get a layout manager that's just right, check out this documentation: https://docs.oracle.com/javase/tutorial/uiswing/layout/using.html
A necessary premise, I don't understand the need of absolute positioning elements in Swing instead of using layout managers.
Said that, you try to position listCredentials, that is inside a scroll pane, instead of the scroll pane itself, so replace:
View.listCredentials.setBounds(100,100, 75,75);
with
View.scrollPaneCredentials.setBounds(100,100, 75,75);

Put one JPanel onto another JPanel from a different Java class

I'm developing Java Swing application. My application has two Java classes. Inside class1.java, I include JFrame, JButton and JPanel (panel1). When I click the button I want to hide panel1 and should be shown panel2 of class2.java. I tried this method in button actionPerformed method of class1.java. But it was not working.
class2 pnl = new class2();
this.remove(panel1);
this.add(pnl);
this.validate();
this.repaint();
Analysis
You simply want the JComponents to be displayed on the JFrame. We can achieve this by using a single JPanel, but adding and removing the JComponents from it, during the JButton's action listener.
Without looking at your actual code, it is better to make a manageable way to reach code and instantiated Objects. The code listed below, creates a nice and manageable way to do so.
Achieving This
The entire class is listed below with comments for explanations.
package swing;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class MultiPaneledFrame {
JFrame frame = new JFrame();
JPanel window = new JPanel();
// As you can see, we create an array containing all your JComponents.
// We have two of these, to simulate multiple JPanel's.
List<JComponent> window1Contents = new ArrayList<JComponent>();
List<JComponent> window2Contents = new ArrayList<JComponent>();
// NOTE: The above Lists can instead be stuck in their own class like asked for,
// and instantiated on Class invocation.
JButton goto2 = new JButton("Goto Panel 2");
JButton goto1 = new JButton("Goto Panel 1");
int panelToShow = 0; // 0 - First "panel".
// 1 - Second "panel".
// Main method of class. Change 'Multi_Paneled_Frame' to the name of your Class.
public MultiPaneledFrame() {
// Execute anything else you want here, before we start the frame.
window1Contents.add(goto2);
window2Contents.add(goto1);
// Here is where I personally am setting the coordinates of the JButton's on the JPanel.
goto2.setPreferredSize(new Dimension(200, 100));
goto1.setPreferredSize(new Dimension(200, 100));
//goto2.setBounds(5, 5, 150, 30); < Used for 'null' layout.
//goto1.setBounds(5, 5, 150, 30); < Used for 'null' layout.
goto2.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addComponents(panelToShow = 1);
}
});
goto1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addComponents(panelToShow = 0);
}
});
initialiseFrame();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
new MultiPaneledFrame();
}
});
}
private void initialiseFrame() {
frame.setSize(600, 400); // Change it accordingly.
// Optional
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
// Needed
frame.setVisible(true);
frame.add(window);
window.setLayout(new BorderLayout()); // Assuming your using a BorderLayout.
//window.setLayout(null); < Uses 'null' layout.
addComponents(panelToShow);
// I always like to make sure that everything is on the frame nicely.
frame.repaint();
frame.validate();
}
private void addComponents(int panelNo) {
if (panelNo == 0) {
for (JComponent component : window1Contents) {
window.removeAll(); // We're removing everything that it contains and replacing it...
window.revalidate();
window.add(component, BorderLayout.CENTER);
//window.add(component); < Uses 'null' layout.
// Since we are using the first panel, we are adding
// everything from the first list of components to the window...
}
} else {
for (JComponent component : window2Contents) {
window.removeAll(); // We're removing everything that it contains and replacing it...
window.revalidate();
window.add(component, BorderLayout.CENTER);
//window.add(component); < Uses 'null' layout.
// Since we are using the second panel, we are adding
// everything from the second list of components to the window...
}
}
// Refreshes the frame.
frame.repaint();
frame.validate();
}
}
Conclusion
Although there are countless ways to achieve something like this, the way I have given, is semi-efficient, and very flexible. Feel free to edit the code, or drop a question if you have any concerns, and I will be happy to respond.
PS: This code was tested and works on a Macbook Air running OS X 10.11 and Java Version 8 Update 65.
CardLayout should be your solution. In this tutorial they show how to switch from panel to another one by selecting a value in ComboBox.
A little bit of explanation for the CarLayout:
The CardLayout lets you place different panel on top of each other but shows only one at the time. With your code, you select the one you want to display.
Initialisation:
this.setLayout(new CardLayout());
class1 pnl1 = new class1();
class2 pnl2 = new class2();
this.add(pnl1, "PANEL1");
this.add(pnl2, "PANEL2");
On your button actionPerformed:
CardLayout cl = (CardLayout)(this.getLayout());
cl.show(this, "PANEL2");

Java: JTextField won't appear

public class HandleUI {
public static void setUpUI(){
JPanel jPan = new JPanel();
FlowLayout flow = new FlowLayout();
jPan.setLayout(flow);
txtFld = new JTextField();
txtFld.setSize(550,5);
jPan.add(txtFld);
jPan.setSize(10,200);
MainClass.mainFrame.add(jPan);
int gapX = MainClass.mainFrame.getX()-(txtFld.getX()/2);
}
//Instance variables.
public static JTextField txtFld;
public JButton [] buttons;
}
public class MainClass {
public static void main (String [] args){
int frameX = Constants.FRAME_WIDTH;
int frameY = Constants.FRAME_HEIGHT;
mainFrame = new JFrame();
mainFrame.setSize(frameX,frameY);
mainFrame.setResizable(false);
mainFrame.setVisible(true);
HandleUI.setUpUI();
}
//Instance variables
public static JFrame mainFrame;
}
It's supposed to show JTextField, but as you might have guessed - JFrame shows nothing. I didn't type in imports on purpose, but they are all there. I can't find the problem. Can anyone help?
1.) Simply write:
JTextField tField = new JTextField(10);
Here In the constructor you are passing the number of columns, which is sufficient for a layout like FlowLayout to set the size of the JTextField
2.) The line mainFrame.setVisible(true); must be the last line of the main method. You need to put the code at main() method, inside SwingUtilities.invokeLater(...) thingy.
3.) Instead of setting size on the JFrame use JFrame.pack(), to set the window to the preferred size.
4.) Creation of unnecessary static members is a design flaw. Try to keep yourself away from such thingies.
5.) Read a bit about Concurrency in Swing
One Example Program for help(Use the order of lines as specified in this answer):
import java.awt.*;
import javax.swing.*;
public class Example {
private void displayGUI() {
JFrame frame = new JFrame("Example Demo");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
JPanel contentPane = new JPanel();
JTextField tField = new JTextField(10);
contentPane.add(tField);
frame.setContentPane(contentPane);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
Runnable runnable = new Runnable() {
#Override
public void run() {
new Example().displayGUI();
}
};
EventQueue.invokeLater(runnable);
}
}
You have to call setVisible(true) on your JFrame AFTER having initialised your UI.
Simply pulling the following line:
HandleUI.setUpUI();
... right before:
mainFrame.setVisible(true);
... will do the trick.
As a side note, I'd like to point out that setting the size of your text field won't really work like you did. You probably would use setPreferredSize(Dimension) instead. Or, even better, organise your UI only using Layouts and not manually setting any component's size.

JTabbedPane within JScrollPane only showing one item at a time

When I launch my application, it launches the JFrame and loads up the JTabbedPane which contains the JScrollPane, yet it only shows one component inside it at a time. I have tried everything, and still I cannot solve the problem...
Here is my code:
package test;
import java.awt.*;
import javax.swing.*;
public class Main extends JFrame{
public Main()
{
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,500);
JPanel pane=new JPanel();
pane.setLayout(new BorderLayout());
UIManager.put("TabbedPane.contentOpaque", false);
JTabbedPane tabbedPane=new JTabbedPane();
JScrollPane scrollPane=new JScrollPane(pane);
tabbedPane.setPreferredSize(new Dimension(getWidth(),getHeight()));
for(int i = 0; i < 10; i++) pane.add(new JLabel("label22222222222222222222222222222222222222222222222222222222"+i));
//pane.add(scrollPane,BorderLayout.CENTER);
tabbedPane.add("Test",scrollPane);
add(tabbedPane);
}
public static void main(String[] args) {
Main main=new Main();
main.setVisible(true);
}
}
Please help me, I have no idea what I am doing wrong.
Your pane JPanel uses BorderLayout and you're adding components in a default fashion, or BorderLayout.CENTER. This is the expected behavior to show only the last component added.
You should consider using another layout such as GridLayout. Also, Google and read the "laying out components in a container" tutorial and understand the layouts that you're using.
Also, consider using a JList to display your data rather than a grid of JLabels.
As an aside, you should format your code for readability, not compactness. Don't put for loops on one line only. In fact all loops and blocks should go into curly braces to prevent your later editing your code, adding another line and thinking that it's in the loop when it's not.
Edit
For example, using a JList:
import javax.swing.*;
public class Main2 {
private static final int MAX_CELLS = 30;
private static void createAndShowGUI() {
final DefaultListModel<String> listModel = new DefaultListModel<>();
final JList<String> myList = new JList<>(listModel);
myList.setVisibleRowCount(8);
for (int i = 0; i < MAX_CELLS; i++) {
listModel.addElement("label22222222222222222222222222222222222222222222222222222222" + i);
}
JTabbedPane jTabbedPane = new JTabbedPane();
jTabbedPane.add("Test", new JScrollPane(myList));
JFrame frame = new JFrame("Main2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(jTabbedPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGUI();
}
});
}
}

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.

Categories

Resources