I'm programming a cookie clicker game remake and when I scale the JFrame window, something white appears. It disappears as soon as you hover the cursor over the button(when refreshes) and I need to fix that, because it does even the same when you launch the game.
Here's a screenshot(UNSCALED | SCALED): http://s3.postimg.org/xomifomhf/bandicam_19.png
this is the whole code of this game:
package cookieclicker.tominocz;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import javax.swing.BorderFactory;
import javax.swing.Icon;
import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static int num1;
static Icon icon1 = new ImageIcon("cookie.png");
static JButton b1 = new JButton(icon1);
static JButton b2 = new JButton("You got " + num1 + " Cookies!");
#SuppressWarnings("serial")
public static void main(String[] args) {
File save = new File(".\\gamesave.cookieclicker");
if (save.exists()) {
loadGame();
}
JFrame f = new JFrame("Cookie Clicker Beta v0.1");
b2.setBackground(Color.cyan);
b2.setPreferredSize(new Dimension(10000, 14));
JPanel buttonPanel1 = new JPanel(new GridLayout(1, 1));
buttonPanel1.setBackground(Color.DARK_GRAY);
b2.setEnabled(false);
b2.setBorder(null);
buttonPanel1.add(b2);
JPanel buttonPanel2 = new JPanel(new GridLayout(1000, 1));
buttonPanel2.setBackground(Color.DARK_GRAY);
buttonPanel2.setEnabled(false);
buttonPanel2.add(new JButton("Grandma"));
buttonPanel2.add(new JButton(""));
buttonPanel2.add(new JButton(""));
JPanel east = new JPanel(new GridBagLayout());
JPanel north = new JPanel(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.anchor = GridBagConstraints.NORTH;
gbc.weighty = 1;
north.add(buttonPanel1, gbc);
east.add(buttonPanel2, gbc);
JPanel center = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
};
center.setBorder(BorderFactory.createLineBorder(Color.BLACK));
f.add(east, BorderLayout.EAST);
f.add(north, BorderLayout.NORTH);
f.add(center);
f.pack();
f.setSize(600, 400);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
b1.setBackground(Color.lightGray);
b1.setBorder(null);
f.add(b1);
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Object source = e.getSource();
if (source instanceof JButton) {
addCookies();
}
}
});
}
public static void addCookies() {
saveGame();
b2.setText("You got " + ++num1 + " Cookies!");
if (num1 == 1) {
b2.setText(" You got " + 1 + " Cookie! ");
} else {
b2.setText("You got " + num1 + " Cookies!");
}
System.out.println(num1);
}
public static void saveGame() {
try {
BufferedWriter writer = new BufferedWriter(new FileWriter(
".\\gamesave.cookieclicker"));
writer.write(String.valueOf(1 + num1));
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static void loadGame() {
try (BufferedReader br = new BufferedReader(new FileReader(
".\\gamesave.cookieclicker"))) {
String SavedGame;
while ((SavedGame = br.readLine()) != null) {
num1 = Integer.parseInt(SavedGame);
b2.setText("You got " + num1 + " Cookies!");
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
And also the other thing is, that the dark grey strip is hiding 3 buttons.
Don't count the 4th one, that's the one showing the ammount of cookies you have :).
Now where could the problem be?
You have two components sharing the CENTRE position of the frame's BorderLayout, center and b1.
b1, been the last component added, is getting the attention of the layout manager and is been laid out when the frame is resized, center is not and is remaining at the last size/position it was set to (because you called pack, which forced the frame to layout it's child components, but then you added b1 after it).
BorderLayout can only manage a single component at each of it's five pre-defined positions
Make a decision about who should be in the centre...
You should also have a read of Should I avoid the use of set(Preferred|Maximum|Minimum)Size methods in Java Swing? and stop messing with the preferred size of your components, let them make their own decisions in combination with appropriate layout managers
I'd also encourage you to move the content of your main method some where else (may be the class's constructor), this way, you fields won't need to be static and it solve a ton of other issues you might have in the future
First of all:
b2.setPreferredSize(new Dimension(10000, 14));
Don't specify preferred sizes for components. Each component is responsible for determining its own preferred size. Let the layout manager determine the size.
JPanel buttonPanel2 = new JPanel(new GridLayout(1000, 1));
Don't use random numbers when defining the GridLayout. If you want one column then just use: new GridLayout(0, 1). Now all components added will be displayed in the first row.
Now for your problem:
f.add(center);
f.pack();
You add an empty panel to the CENTER of the BorderLayout. Then you pack the frame so the panel now has a valid size.
f.add(b1);
But then you add a second component to the "CENTER". However BorderLayout will only manage the size of the last component added.
Swing will paint() the last component added first, so the button is painted, then the panel is painted over top of it.
If you move the mouse over the center, then the mouse event is passed to the button and the rollover logic is invoked so the button is painted.
If you resize the frame, the buttons size is recalculated by the layout manager and components are repainted. Again, the center panel is painted last so you see part of the button with the panel on top.
I don't know why you have the center panel so I can't make a specific suggestion other than to say, get rid of it. Again the main problem is you are trying to add two components to the center. Don't do this!
Related
I am developing a simple application, and am currently working on the gui design using Swing. In my program I have a JPanel which I would like to have a background color black like so:
JPanel playerPanel = new JPanel();
playerPanel.setOpaque(true);
playerPanel.setBackground(Color.BLACK);
This code works fine. However, the problem is when I assign a Layout Manager to the panel:
JPanel playerPanel = new JPanel();
playerPanel.setOpaque(true);
playerPanel.setBackground(Color.BLACK);
playerPanel.setLayout(new BoxLayout(playerPanel, BoxLayout.PAGE_AXIS));
For some reason, this makes the black color of the panel go away. This happens no matter where I place the .setLayout(...) command, before or after the .setBackground(...) and .setOpaque(true).
Why is this, and how do I work around this? How do I keep a black JPanel that uses a BoxLayout manager?
Verify that your panel's content is not obscuring the altered background. Resize the example below, which I've artificially enlarged, to see the effect.
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ComponentAdapter;
import java.awt.event.ComponentEvent;
import java.util.Random;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/57785802/230513
*/
public class BoxTest {
public static final Random random = new Random();
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new BoxTest().create();
}
});
}
void create() {
JPanel panel = new JPanel();
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.setBackground(Color.BLACK);
panel.add(Box.createVerticalGlue());
for (int i = 0; i < 4; i++) {
panel.add(new VariablePanel());
panel.add(Box.createVerticalGlue());
}
JFrame f = new JFrame("BoxTest");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(panel);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
f.setSize(f.getWidth(), f.getHeight() + 64);
}
}
/**
* A VariablePanel has a label showing its current size,
* as well as a variable number of text items.
*/
class VariablePanel extends JPanel {
private static final String text =
"Sed ut perspiciatis unde omnis iste natus error sit.";
private final JLabel sizeLabel = new JLabel("Size:");
public VariablePanel() {
this.setLayout(new GridLayout(0, 1));
this.add(sizeLabel);
int count = BoxTest.random.nextInt(5) + 1;
for (int i = 0; i < count; i++) {
this.add(new JLabel(text));
}
this.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
int w = e.getComponent().getWidth();
int h = e.getComponent().getHeight();
sizeLabel.setText("Size: " + w + "\u00d7" + h);
}
});
}
}
Swing components (except JLabel) are opaque by default. This means:
you don't need playerPanel.setOpaque(true)
most components you add to the panel will be opaque and cover the background of your playerPanel.
Also, the BoxLayout respects the maximum size of any component you add to the panel. So if you add a component:
like a JButton which has a defined maximum size, you will see the button on top of the playerPanel and the background will surround the button.
like a JPanel, which does not have a defined maximum size, the panel will be resized to fill the entire area of the playerPanel and you won't see the background of the playerPanel.
If you want to see the background of the playerPanel show through a component added to the playerPanel, then you need to use setOpaque(false) on the component. For example:
JPanel child = new JPanel();
child.setOpaque( false );
playerPanel.add( child );
I have a GUI program which includes JLabels and JButtons and basically I want a layout that would help me display them as follows:
Label1 Button1
Label2 Button2
Label3 Button3
.....
Is there a layout that would allow me to achieve the above result?
I have looked at this example but is too complex and was wondering if there is anything automated that I can use?
This is one of the few things for which I'd recommend (a utility method and) GroupLayout as seen in this answer.
You can use GridLayout. Documentation here.
This is just for simplicity, and for your question. GUI is really dependent on what you would like to do and is really a thing that can be hardly automated..., and i don't think you only want those 6 elements on your GUI, but theoretically this will do it:
import java.awt.BorderLayout;
import java.awt.EventQueue;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class GUITest {
private Box labelbox = new Box(BoxLayout.Y_AXIS);
//Y_AXIS means they are placed vertically in the box
private Box buttonbox = new Box(BoxLayout.Y_AXIS);
private JFrame frame = new JFrame();
private JPanel panel = new JPanel();
public void makeGUI1() {
for (int i = 1; i <= 3; i++) {
//if you want to save the references, you should make
//an ArrayList<JLabel> and add each of them to it
JLabel label = new JLabel("Label " + i);
labelbox.add(Box.createVerticalStrut(5));
//these are for giving the labels some extra space
//between them vertically to be in line with the buttons
labelbox.add(label);
labelbox.add(Box.createVerticalStrut(10)); //these are too
}
for (int i = 1; i <= 3; i++) {
//if you want to save the references, you should make
//an ArrayList<JButton> and add each of them to it
JButton button = new JButton("Button " + i);
buttonbox.add(button);
}
panel.add(labelbox, BorderLayout.EAST);
//you can find picture of each constant:
//http://download.java.net/jdk7/archive/b123/docs/api/java/awt/BorderLayout.html
panel.add(buttonbox, BorderLayout.WEST);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
GUITest guitest = new GUITest();
guitest.makeGUI1();
}
});
}
}
You can also use obj.setBounds(LeftSpaceParameter,TopSpaceParameter) with which you can place the gui elements or objects at any position of your choice. You need to put the default layout to null
yet gridLayout is much easier. .
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
public class SideNotes {
public static JPanel panel = new JPanel();
private List<String> notes = new ArrayList<String>();
private static JButton add = new JButton("Add note");
public SideNotes() {
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(add);
loadNotes();
add.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
addNote();
}
});
}
public void addNote() {
String note = JOptionPane.showInputDialog("Enter note: ", null);
notes.add(note);
JLabel label = new JLabel(note);
panel.add(label);
panel.revalidate();
panel.repaint();
}
private void loadNotes() {
for (int i = 0; i < notes.size(); i++) {
JCheckBox jcb = new JCheckBox(notes.get(i), false);
panel.add(jcb);
panel.revalidate();
panel.repaint();
}
}
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setSize(200, 400);
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
panel.add(add);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
new SideNotes();
}
}
Why isn't my JCheckBox showing up? The text shows up but not the actual box. What's the deal?
I have edited my post to contain all of my code in case that helps solve the issue.
needmoretextneedmoretextneedmoretextneedmoretextneedmoretextneedmoretextneedmoretext
Possible reasons:
panel has not been added to GUI
panel has been added but for some reason is not visible.
panel is too small to show the child component. This can happen for instance if you set a component's size or preferredSize or if you place it in a FlowLayout-using container without thought.
panel uses null layout.
panel's layout manager is not one that easily accepts a new component -- think GroupLayout for this one.
There are other unspecified layout manager problems going on. Do you call pack() on your GUI? Do you use null layout or absolute positioning anywhere? Do you need to put panel in a JScrollPane?
Consider creating and posting an sscce for better help.
Edit
Your posted code doesn't ever add any JCheckBoxes to the JPanel, just JLabels. To prove this is so, click on the labels and you'll see that they don't respond to clicks.
Your code grossly over-uses static fields. Get rid of all static modifiers on all variables. They should all be instance variables. The only static anything in your code above should be the main method, and that's it. If this causes errors, then fix the errors, but not by making fields static.
Give your SideNotes class a method, getPanel() that returns the panel field.
Create a SideNotes instance in the beginning of your main method. Then call the above method on the instance to get the JPanel for the JFrame. i.e., frame.add(sideNotes.getPanel());.
Don't add JLabels to your GUI (I've no idea why you're doing this). Add JCheckBoxes in the actionPerformed method.
Every time you press the button, a new Note (JLabel) is added to the panel. But you never call loadNotes() after adding a new Note. So the JLabel is added but not its respective JCheckBox as intended.
Besides of this I'd suggest you make this change:
public void addNote() {
String note = JOptionPane.showInputDialog("Enter note: ", null);
if(notes != null) {
notes.add(note);
JLabel label = new JLabel(note);
panel.add(label);
panel.add(new JCheckBox(note, false));
panel.revalidate();
panel.repaint();
}
}
So you don't need to call loadNotes() and update the GUI just once.
I have a JTabbed pane, which has a varying number of tabs. When the number of tabs is greater than 4, I get extra spacing/padding at the bottom of each tab panel. The picture below shows this (on the left you see the extra spacing, on the right you see no extra spacing).
Here is the exact code I used to get those pictures:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JDialog;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;
public class DialogTest {
public static void main(String[] args) {
new DialogTest();
}
public DialogTest() {
JDialog dialog = new MyDialog();
dialog.pack();
dialog.setVisible(true);
}
class MyDialog extends JDialog {
public MyDialog() {
super(null, ModalityType.APPLICATION_MODAL);
final JTabbedPane tabs = new JTabbedPane();
final int numTabs = Integer.parseInt(JOptionPane.showInputDialog("Number of tabs:"));
setPreferredSize(new Dimension(400, 200));
for (int i = 1; i <= numTabs; i++) {
tabs.addTab("Tab"+i, new MyPanel(i));
}
setLayout(new BorderLayout());
add(tabs, BorderLayout.NORTH);
}
}
class MyPanel extends JPanel {
public MyPanel(int text) {
final JLabel label = new JLabel("THIS IS A PANEL" + text);
label.setFont(label.getFont().deriveFont(18f));
label.setBackground(Color.cyan);
label.setOpaque(true);
add(label);
setBackground(Color.red);
}
}
}
I've tried numerous things including many different layout managers. I can't for the life of me get rid of that extra spacing. Any help would be great.
final JTabbedPane tabs = new JTabbedPane();
tabs.setTabLayoutPolicy(JTabbedPane.SCROLL_TAB_LAYOUT); // ADD THIS!
The reason the other example behaves as it does is that the pane wraps the tabs to the next line & presumes that once we have gone beyond as many tabs as it might naturally display in a single line, it must increase the preferred size to include that extra line of tabs.
I have a JPanel with GroupLayout with 3 JLabels in it. I also have a hidden JButton in it.
I have added a MouseListener to JPanel showing the button in mouseEntered and hide the button in mouseExited events respectively.
At this time, their is space for button between 2 labels and their only the button is shown or hidden using setVisible(). When the btn is visible, the labels below it goes down making space for button and if the btn is hidden it again comes to its original size.
What I want - in mouseEntered, the button should show on the label itself (let it be overlap) and I should be able to click on the button. This all should happen very smoothly without screen flickering. Similarly in mouseExited, the button should be removed.
How do I achieve this ? Can anyone help me with this.
UPDATE
#Andrew, Thanks I tried with JLayeredPane and it does work. Though the button is not set to visible false. Here's my mouseMoved code :
public void mouseMoved(MouseEvent e) {
if (e.getComponent() == layeredPane) {
if (! startCustomBtn.isVisible())
startCustomBtn.setVisible(true);
startCustomBtn.setLocation(e.getX()-55, e.getY()-30);
} else {
if (startCustomBtn.isVisible()) {
startCustomBtn.setVisible(false);
revalidate();
}
}
}
Layout of the JPanel :
private void layeredLayout() {
layeredPane = new JLayeredPane();
layeredPane.addMouseMotionListener(this);
Insets insets = this.getInsets();
Dimension size = rateLabel.getPreferredSize();
rateLabel.setBounds(insets.left + 45, insets.top + 15, size.width, size.height);
size = imageLabel.getPreferredSize();
imageLabel.setBounds(insets.left + 15, insets.top + 40, size.width, size.height);
size = label.getPreferredSize();
label.setBounds(insets.left + 45, insets.top + imageLabel.getWidth() + 20 , size.width, size.height);
size = startCustomBtn.getPreferredSize();
startCustomBtn.setBounds(insets.left + 45, insets.top + 40 + size.height, size.width, size.height);
layeredPane.add(rateLabel, new Integer(0));
layeredPane.add(imageLabel, new Integer(1));
layeredPane.add(label, new Integer(2));
layeredPane.add(startCustomBtn, new Integer(1), 0);
setLayout(new BoxLayout(this, BoxLayout.PAGE_AXIS));
add(layeredPane);
}
Strange - I tried the layout with null, FlowLayout, but couldn't see anything. When tried with BoxLayout, components showed up.
REsult :
Main screen has a JPanel with Gridlayout(2, 3) and in each cell this JPanel (MyPanel) is added. When I come out from 1 cell (i.e. MyPanel) the button of that panel should be hidden which is not happening with the above code. What can be the reason ? I also added revalidate() & also repaint() but nothing works. ????
What I want - in mouseEntered, the button should show on the label
itself (let it be overlap) and I should be able to click on the
button. This all should happen very smoothly without screen
flickering. Similarly in mouseExited, the button should be removed.
As JLabel extends from JComponent you can add componentes to label itself, just need to set a LayoutManager first. This fact is well explained in this question.
Sample Code
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.border.LineBorder;
public class Demo {
private void initGUI(){
final JButton button = new JButton("Hello!");
button.setVisible(false);
final JLabel testLabel = new JLabel("Welcome!");
testLabel.setPreferredSize(new Dimension(200, 30));
testLabel.setBorder(new LineBorder(Color.GRAY, 1));
testLabel.setLayout(new BorderLayout());
testLabel.add(button, BorderLayout.EAST);
button.addMouseListener(new MouseAdapter() {
#Override
public void mouseExited(MouseEvent e) {
Point mousePosition = MouseInfo.getPointerInfo().getLocation();
if(testLabel.contains(mousePosition)){
testLabel.dispatchEvent(new MouseEvent(testLabel, MouseEvent.MOUSE_ENTERED, System.currentTimeMillis(), 0, mousePosition.x, mousePosition.y, 0, false));
} else {
testLabel.dispatchEvent(new MouseEvent(testLabel, MouseEvent.MOUSE_EXITED, System.currentTimeMillis(), 0, mousePosition.x, mousePosition.y, 0, false));
}
}
});
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
JOptionPane.showMessageDialog(null, "The button was pressed!");
Point mousePosition = MouseInfo.getPointerInfo().getLocation();
testLabel.dispatchEvent(new MouseEvent(testLabel, MouseEvent.MOUSE_EXITED, System.currentTimeMillis(), 0, mousePosition.x, mousePosition.y, 0, false));
}
});
testLabel.addMouseListener(new MouseAdapter(){
#Override
public void mouseEntered(MouseEvent e) {
JLabel label = (JLabel) e.getSource();
label.setText("Here is the Button!");
button.setVisible(true);
}
#Override
public void mouseExited(MouseEvent e) {
Point point = e.getPoint();
point.setLocation(point.x - button.getX(), point.y - button.getY()); //make the point relative to the button's location
if(!button.contains(point)) {
JLabel label = (JLabel) e.getSource();
label.setText("The button is gone!");
button.setVisible(false);
}
}
});
JPanel content = new JPanel(new FlowLayout());
content.setPreferredSize(new Dimension(300,100));
content.add(testLabel);
JFrame frame = new JFrame("Demo");
frame.setContentPane(content);
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Demo().initGUI();
}
});
}
}
Output
Update
As #nIcEcOw pointed out (thanks!), there's an annoying flickering generated by mouse events' transition. I improved the example fixing this and another untreated aspects like "what happens when mouse exits from JButton?"
Questions like this are kind of frustrating. There is almost enough information to describe what you want, or what the problem is, but not quite.
It seems that you want label-label-label until the mouse enters the panel, then you want the appearance to be label-button-label. It's hard to imagine me wanting a UI to act like this.
Is there something about the appearance of the button you don't like, that you want it only to appear on mouse-over-panel? Can the button's appearance be altered so that it looks the way you want it to look, without all this hocus-pocus with the middle label and the button?
I don't have any idea why you mention a timer -- nothing that you describe is being timed, as near as I can tell. In addition, you should be able to boil down what you have to a small runnable example and post it, so that someone can see what you've got and what it does.