Related
I am just throwing together a quick and dirty GUI to display some data when I ran into an odd issue. The last label I add to the JFrame doesn't want to be positioned or display the border I put on it, so it looks like this:
Here is my code:
public DisplayData (Connection tConn)
{
ID = tID;
conn = tConn;
setupObjects();
setupFrame();
}
private void setupObjects()
{
JLabel caseLabel = new JLabel ("Case #:");
JLabel dateLabel = new JLabel ("Date:");
JLabel reportLabel = new JLabel ("Report:");
JLabel offenceLabel = new JLabel ("Offence:");
JLabel descriptionLabel = new JLabel ("Description:");
this.add(caseLabel);
this.add(dateLabel);
this.add(reportLabel);
this.add(offenceLabel);
this.add(descriptionLabel);
caseLabel.setBounds(50, 50, 130, 25); //x, y, width, height
dateLabel.setBounds(50, 100, 130, 25);
reportLabel.setBounds(50, 150, 130, 25);
offenceLabel.setBounds(50, 200, 130, 25);
descriptionLabel.setBounds(100, 50, 130, 25);
caseLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
dateLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
reportLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
offenceLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
descriptionLabel.setBorder(BorderFactory.createLineBorder(Color.BLACK));
}
private void setupFrame()
{
this.setTitle("Data Display");
this.setSize (650, 700); //Width, Height
this.setLocation(300, 10);
this.setResizable(false);
this.setVisible(true);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setLayout(null);
}
Yes, I know I should be using a proper layout manager, but like I said i just wanted something quick and dirty. Plus, I will not be beaten by something that should be this simple. Any ideas would be appreciated.
EDIT:
As Compass and Neophyte pointed out, my order of operations was off. Flipped my method calls and all is good again in the world. Thanks for the 2nd pair of eyes.
Contrary to the original poster's strategy, or any of the answers so far, the best approach to this problem is to use layouts.
Here is an example that shows how easy it is to position fields using layouts, and to change the GUI on later updates to the specification.
import java.awt.*;
import javax.swing.*;
import javax.swing.border.*;
public class CourtDetailsGUI {
private JComponent ui = null;
static final String[] FIELD_NAMES = {
"Case #:",
"Date:",
"Report:",
"Offence:",
"Plaintiff:",
"Defendant:"
};
CourtDetailsGUI(int num) {
initUI(num);
}
public void initUI(int num) {
if (ui != null) {
return;
}
ui = new JPanel(new BorderLayout(4, 4));
ui.setBorder(new EmptyBorder(4, 4, 4, 4));
ui.add(getFieldsPanel(num), BorderLayout.PAGE_START);
JTextArea ta = new JTextArea(5, 40);
JScrollPane sp = new JScrollPane(ta);
JPanel p = new JPanel(new GridLayout());
p.add(sp);
p.setBorder(new TitledBorder("Details"));
ui.add(p);
}
private JPanel getFieldsPanel(int num) {
JPanel outerPanel = new JPanel(new FlowLayout());
JPanel innerPanel = new JPanel(new GridLayout(0, 1, 15, 15));
outerPanel.add(innerPanel);
for (int ii=1; ii<num; ii++) {
JLabel l = new JLabel(FIELD_NAMES[ii]);
l.setBorder(new LineBorder(Color.BLACK));
innerPanel.add(l);
}
return outerPanel;
}
public JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception useDefault) {
}
for (int ii=0; ii<FIELD_NAMES.length; ii++) {
CourtDetailsGUI o = new CourtDetailsGUI(ii+1);
JFrame f = new JFrame("Data " + (ii+1));
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(o.getUI());
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
}
}
};
SwingUtilities.invokeLater(r);
}
}
Your order of operations is incorrect.
You initially call setupObjects();
This plays your objects out onto a JFrame, which has the default LayoutManager of BorderLayout.
By using the default add(Component comp) method, with BorderLayout, you end up putting the component into null for BorderLayout, which is not supposed to be normal. Furthermore, the reason you can't see the border for this object is because the border is actually the size of the frame. If you explicitly set a region for BorderLayout, then you'll see it work, but setting to no region seems to just break BorderLayout.
Additional add calls appear to free the previous item from the BorderLayout null management, allowing the bounds to take over.
Afterwards, you call setupFrame(); which removes the layout manager, but does not refresh what is currently rendered.
This sets the layout to null, which does nothing to how the frame is displayed, but just removes the layout.
To avoid this issue, call setupFrame(); prior to setupObjects();, and then setVisible(true) can be called at the end of setupObjects();
i read some contributions about this but nothing helped.
I try to make a little application with a gui,
but the problem is, if i click the button, no animation appears and nothing happens. I hope you can help me.
That's the code:
public class StartFrame extends JFrame{
JTextField eingabe;
JLabel inhalt;
JButton button;
JCheckBox fett;
JCheckBox kursiv;
JCheckBox groß;
JPanel panel;
StartFrame(int sizeWidth, int sizeHeight, String title){
setSize(sizeWidth, sizeHeight);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setTitle(title);
setLocationRelativeTo(null);
setLayout(null);
inhalt = new JLabel("TeXt");
inhalt.setBounds(0, 0, 500, 60);
Font font = inhalt.getFont().deriveFont(Font.ITALIC, 15);
inhalt.setFont(font);
inhalt.setToolTipText("Das ist ein Text");
add(inhalt);
button = new JButton("HIER");
button.setBounds(100, 10, 100, 50);
button.addActionListener(new ClickListener());
button.setEnabled(false);
add(button);
eingabe = new JTextField();
eingabe.setBounds(300, 50, 150, 25);
eingabe.addCaretListener(new SchreibkopfListener());
add(eingabe);
panel = new JPanel();
panel.setLayout(null);
panel.setBounds(10, 200, 150, 100);
add(panel);
fett = new JCheckBox("Fett");
fett.setBounds(0, 0, 150, 25);
fett.addItemListener(new FettListener());
panel.add(fett);
kursiv = new JCheckBox("Kursiv");
kursiv.setBounds(0, 25, 150, 25);
panel.add(kursiv);
groß = new JCheckBox("Groß");
groß.setBounds(0, 50, 150, 25);
panel.add(groß);
setVisible(true);
}
private class ClickListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent arg0) {
inhalt.setText(eingabe.getText());
}
}
private class SchreibkopfListener implements CaretListener{
#Override
public void caretUpdate(CaretEvent arg0) {
String inTextField = eingabe.getText();
inTextField = inTextField.trim();
if(inTextField.isEmpty()){
button.setEnabled(false);
}else{
button.setEnabled(true);
}
}
}
private class FettListener implements ItemListener{
#Override
public void itemStateChanged(ItemEvent arg0) {
if(fett.isSelected()){
Font font = inhalt.getFont().deriveFont(Font.BOLD, 15);
inhalt.setFont(font);
}else{
Font font = inhalt.getFont().deriveFont(Font.ITALIC, 15);
inhalt.setFont(font);
}
}
}
}
Here's a wonderful lesson into why null layouts suck (and you should stop using them)
Let's just take a little look at these lines...
nhalt = new JLabel("TeXt");
inhalt.setBounds(0, 0, 500, 60);
inhalt.setToolTipText("Das ist ein Text");
//...
button = new JButton("HIER");
button.setBounds(100, 10, 100, 50);
So, the label starts at 0x0 and expands through 500x60, okay, the button starts at 100x10 and expands through 100x50, this means that the label and the button actually collide
Now, I know what you're thinking, but I add the button after the label, but this isn't how components actually get painted/respond to events.
Components are painted in reverse order of how they are added, this means that the label actually resides OVER the button (in terms of the Z-order), this also means that the label receives events BEFORE the button, this is important because...
inhalt.setToolTipText("Das ist ein Text");
installs a MouseListener on the JLabel, which consumes ALL mouse events, preventing the button from been notified.
So, long answer short, don't use null layouts (and take the time to better understand how the API actually works :P)
This question already has an answer here:
Java aligning components in panels
(1 answer)
Closed 6 years ago.
I add a JTextField to my game in the bottom left corner using a nested BorderLayout inside my main panel's BorderLayout.SOUTH. This works fine, but then when I add a button to go right next to it, my JTextField dissapears. Can someone please help?
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class BlackjackGUI{
private JFrame frame;
private JPanel panel, panelLeft, panelBottom;
private JButton newGameBtn, dealBtn, hitBtn, standBtn;
private JLabel placeBetLbl, playerMoneyLbl;
private JLabel playerCard1Lbl, playerCard2Lbl, playerCard3Lbl,
playerCard4Lbl, playerCard5Lbl, playerCard6Lbl, playerCard7Lbl;
private JLabel dealerCard1Lbl, dealerCard2Lbl, dealerCard3Lbl, dealerCard4Lbl,
dealerCard5Lbl, dealerCard6Lbl, dealerCard7Lbl;
private JLabel playerCardValueLbl, dealerCardValueLbl;
private JLabel spacer1, spacer2;
private JTextField betInputBox;
public BlackjackGUI(){
createForm();
addTextField();
addButtons();
addLabels();
frame.add(panel);
frame.setVisible(true);
}
public void createForm() {
frame = new JFrame("Blackjack");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1200,800);
panel = new JPanel();
panel.setLayout(new BorderLayout());
Color c = new Color(0, 100, 0);
panel.setBackground(c);
panelLeft = new JPanel();
Color panelLeftBG = new Color (23, 25, 100);
panelLeft.setBackground(panelLeftBG);
panel.add(panelLeft, BorderLayout.WEST);
panelBottom = new JPanel();
Color panelBottomBG = new Color (56, 12, 10);
panelBottom.setBackground(panelBottomBG);
panelBottom.setLayout(new BorderLayout());
panel.add(panelBottom, BorderLayout.SOUTH);
}
public void addButtons() {
newGameBtn = new JButton("New Game");
panelLeft.add(newGameBtn, BorderLayout.WEST);
newGameBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
});
dealBtn = new JButton("Deal");
dealBtn.setPreferredSize(new Dimension (100, 50));
panelBottom.add(dealBtn, BorderLayout.WEST);
newGameBtn.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent event) {
System.exit(0);
}
});
}
public void addTextField() {
betInputBox = new JTextField("£25.00");
betInputBox.setFont(new Font("Gill Sans MT", Font.PLAIN, 35));
betInputBox.setHorizontalAlignment(SwingConstants.RIGHT);
betInputBox.setPreferredSize(new Dimension(175,50));
panelBottom.add(betInputBox, BorderLayout.WEST);
}
public void addLabels() {
placeBetLbl = new JLabel("Place your bets!");
placeBetLbl.setFont(new Font("Gill Sans MT", Font.PLAIN, 35));
panelBottom.add(placeBetLbl);
playerMoneyLbl = new JLabel("£2,500");
playerMoneyLbl.setFont(new Font("Gill Sans MT", Font.PLAIN, 35));
panelBottom.add(playerMoneyLbl, BorderLayout.EAST);
}
public static void main(String[] args) {
new BlackjackGUI();
}
}
Excerpt from the BorderLayout javadoc:
Each region may contain no more than one component, and is identified
by a corresponding constant: NORTH, SOUTH, EAST, WEST, and
CENTER.
Your are first adding the text field and then the button to the same region (WEST), thus button just replaces the text field.
To solve the issue you can use FlowLayout for the panelBottom:
panelBottom.setLayout(new FlowLayout(FlowLayout.LEFT));
For some reason my scrollbar is appearing but it is not working. What is suppose to happen is using the scrollbar to scroll through the text of the textarea. Can somebody please explain why this isnt working?
import java.io.*;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class App extends JFrame{
private JPanel paneel;
public App(){
paneel = new AppPaneel();
setContentPane(paneel);
}
public static void main(String args[]){
JFrame frame = new App();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(400, 400);
frame.setTitle("Auto Clicker");
frame.setVisible(true);
}
}
class AppPaneel extends JPanel{
private JTextField delayField, xLocation, yLocation;
private JTextArea listArea;
private JButton addButton, saveButton, runButton;
private JScrollPane scroll;
public AppPaneel(){
setLayout(null);
delayField = new JTextField();
delayField.setBounds(10, 10, 85, 25);
delayField.setText("delay in ms");
xLocation = new JTextField();
xLocation.setBounds(105, 10, 85, 25);
xLocation.setText("X position");
yLocation = new JTextField();
yLocation.setBounds(200, 10, 85, 25);
yLocation.setText("Y position");
addButton = new JButton("Add");
addButton.setBounds(295, 10, 75, 24);
addButton.addActionListener(new AddHandler());
listArea = new JTextArea();
listArea.setBounds(10, 45, 360, 180);
scroll = new JScrollPane(listArea);
scroll.setVerticalScrollBarPolicy(ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS);
scroll.setBounds(370, 45, 20, 180);
saveButton = new JButton("Save");
saveButton.setBounds(10, 230, 85, 24);
runButton = new JButton("Run (F1)");
runButton.setBounds(105, 230, 85, 24);
runButton.addActionListener(new RunHandler());
add(delayField);
add(xLocation);
add(yLocation);
add(addButton);
add(listArea);
add(saveButton);
add(runButton);
add(scroll);
}
class AddHandler implements ActionListener{
public void actionPerformed(ActionEvent a){
listArea.setText(listArea.getText() + delayField.getText() + ", " + xLocation.getText() + ", " + yLocation.getText() + ", " + "click;" + "\n");
}
}
class RunHandler implements ActionListener{
private Robot bot;
private String text;
int foo = Integer.parseInt("1234");
public void actionPerformed(ActionEvent b) {
try{
text = listArea.getText();
bot = new Robot();
for(String line : text.split("\\n")){
int delay = Integer.parseInt((line.substring(0, 4)));
int xpos = Integer.parseInt((line.substring(6, 10)));
int ypos = Integer.parseInt((line.substring(12, 16)));
bot.mouseMove(xpos, ypos);
Thread.sleep(delay);
bot.mousePress(InputEvent.BUTTON1_MASK);
bot.mouseRelease(InputEvent.BUTTON1_MASK);
}
}
catch (AWTException | InterruptedException e){
e.printStackTrace();
}
}
}
}
Don't use null layouts and setBounds as it is messing up your program. We tell folks time and time again not to do this for a reason -- by setting the JTextArea's bound you constrain its size so it won't grow when it needs to. The solution, as always -- learn and use the layout managers. Set the JTextArea's column and row properties but not its bounds, its size, or its preferred size.
Next don't do this: Thread.sleep(delay); in your Swing application as it will put the entire application to sleep. Use a Swing Timer instead for any delays. The tutorials can help you use this.
For a non-functional layout example:
import java.awt.BorderLayout;
import java.awt.GridLayout;
import javax.swing.*;
#SuppressWarnings("serial")
public class App2 extends JPanel {
private static final int GAP = 5;
private JTextField textField1 = new JTextField(GAP);
private JTextField textField2 = new JTextField(GAP);
private JTextField textField3 = new JTextField(GAP);
private JTextArea textArea = new JTextArea(15, 30);
public App2() {
JPanel topPanel = new JPanel(new GridLayout(1, 0, GAP, GAP));
topPanel.add(textField1);
topPanel.add(textField2);
topPanel.add(textField3);
topPanel.add(new JButton("Add"));
textArea.setWrapStyleWord(true);
textArea.setLineWrap(true);
JScrollPane scrollPane = new JScrollPane(textArea);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
JPanel bottomPanel = new JPanel(new GridLayout(1, 0, GAP, GAP));
bottomPanel.add(new JButton("Save"));
bottomPanel.add(new JButton("Run (F1)"));
bottomPanel.add(new JLabel(""));
bottomPanel.add(new JLabel(""));
setBorder(BorderFactory.createEmptyBorder(GAP, GAP, GAP, GAP));
setLayout(new BorderLayout(GAP, GAP));
add(topPanel, BorderLayout.PAGE_START);
add(scrollPane, BorderLayout.CENTER);
add(bottomPanel, BorderLayout.PAGE_END);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Auto Clicker");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new App2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
Your whole approach is wrong. You should not be using a null layout. For example:
scroll.setBounds(370, 45, 20, 180);
you are setting the width to 20. Well how to you expect the text area to display in 20 pixels? This is the problem with using setBounds(...), the random numbers you use have no meaning. Let a layout manager do its job. Also:
add(listArea);
The problem is that a component can only have a single parent. You originally created the JScrollPane with the text area which is correct.
But then you add the text area directly to the frame which removes it from the scroll pane so the scroll panel will no longer function.
Get rid of that statement and just add the scroll pane to the frame.
However, I don't recommend you use this as your final solution. I just wanted to mention the current problems so you hopefully understand the benefits of using layout managers and so that you don't try to share components again.
The proper solution is to use layout managers as has been suggested by the "Community Wiki". Then the layout manager will determine the size and location of each component so you don't need to worry about calculating your pixel values correctly.
I have a simple GUI program I'm trying to get to work. When the user presses the bottom button I'm trying to get some shapes to paint. When I get rid of the if(buttonClicked) in paint() everything shows up fine but paint() seems to be auto-executed and the shapes appear without any button click. When I add surround the paint() body with if(buttonClicked), regardless of how the ButtonHandler class handles it, the rest of my components do not even show up in the frame. I have no idea why this is happening. Test the code with and without the if logic in paint() and you will see what's going on.
public class GUI extends JFrame {
//declare components
Container container;
JPanel centerPanel, northPanel, southPanel, eastPanel, westPanel, mouseClickPanel;
JLabel topLabel;
JTextArea textArea;
JButton buttonA, buttonB, drawButton;
boolean buttonClicked;
public GUI(String title) {
super(title); // invoke JFrame Constructor
container = getContentPane();
container.setLayout(new BorderLayout());
centerPanel = new JPanel();
centerPanel.setBackground(Color.CYAN);
northPanel = new JPanel();
northPanel.setBackground(Color.GREEN);
southPanel = new JPanel();
southPanel.setBackground(Color.MAGENTA);
eastPanel = new JPanel();
eastPanel.setBackground(Color.ORANGE);
westPanel = new JPanel();
westPanel.setBackground(Color.YELLOW);
mouseClickPanel = new JPanel();
mouseClickPanel.setBackground(Color.GRAY);
mouseClickPanel.setPreferredSize(new Dimension(400, 400));
topLabel = new JLabel("Press either button to make something happen");
topLabel.setFont(new Font("Calibri", Font.PLAIN, 16));
topLabel.setHorizontalAlignment(JLabel.CENTER);
textArea = new JTextArea(3, 20);
textArea.setEditable(false);
buttonA = new JButton("Press Here");
buttonB = new JButton("Press Here");
drawButton = new JButton("Press here");
container.add(centerPanel, BorderLayout.CENTER);
container.add(northPanel, BorderLayout.NORTH);
container.add(southPanel, BorderLayout.SOUTH);
container.add(eastPanel, BorderLayout.EAST);
container.add(westPanel, BorderLayout.WEST);
northPanel.add(topLabel, BorderLayout.NORTH);
centerPanel.add(buttonA);
centerPanel.add(textArea);
centerPanel.add(buttonB);
centerPanel.add(mouseClickPanel);
centerPanel.add(drawButton);
buttonClicked = false;
ButtonHandler buttonHandler = new ButtonHandler(buttonA, drawButton, textArea, buttonClicked);
buttonA.addActionListener(buttonHandler); // add actionListeners to buttonA and B
buttonB.addActionListener(buttonHandler);
drawButton.addActionListener(buttonHandler);
setSize(525, 600);
setVisible(true);
}
public void paint(Graphics g) {
if (buttonClicked) {
super.paint(g);
g.setColor(Color.RED);
g.fillOval(150, 150, 50, 50);
g.draw3DRect(200, 200, 50, 50, true);
}
}
}
Handler class:
public class ButtonHandler extends JFrame implements ActionListener {
private JButton buttonA, drawButton;
private JTextArea textArea;
boolean buttonClicked;
public ButtonHandler(JButton buttonA, JButton drawButton, JTextArea textArea, boolean buttonClicked) {
this.buttonA = buttonA;
this.textArea = textArea;
this.drawButton = drawButton;
this.buttonClicked = buttonClicked;
}
#Override
public void actionPerformed(ActionEvent e) {
if (e.getSource() == buttonA) {
textArea.setText(" <- You pressed left button");
}
else if (e.getSource() == drawButton) {
textArea.setText("You pressed button to draw rectangle");
buttonClicked = true;
repaint();
}
else {
textArea.setText("You pressed right button ->");
}
}
}
Take super.paint(g); out of the if statement. Have it as the first line. Otherwise, no painting at all (including the JPanel internals such as background) will happen unless the button is clicked.