I have set a game menu screen on my JFrame using a layered panel. I want to remove all the components from the JFrame and add a new content panel. I've spent a few hours trying to work through this issue but I am unable to come up with a good solution.
public myJFrame ()
{
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout());
setSize (1030, 727);
setVisible(true);
newGame = new JButton();
loadGame = new JButton();
quitGame = new JButton();
newGame.setBounds(new Rectangle(10,600,200,110));
newGame.setIcon(pic1);
newGame.setOpaque(false);
newGame.setContentAreaFilled(false);
newGame.setBorderPainted(false);
loadGame.setBounds(new Rectangle(210,600,200,110));
loadGame.setIcon(pic2);
loadGame.setOpaque(false);
loadGame.setContentAreaFilled(false);
loadGame.setBorderPainted(false);
quitGame.setBounds(new Rectangle(410,600,200,110));
quitGame.setIcon(pic3);
quitGame.setOpaque(false);
quitGame.setContentAreaFilled(false);
quitGame.setBorderPainted(false);
background.setBounds(0,0,1030,727);
titletext.setBounds(0,0,726,170);
lp = getLayeredPane();
lp.add(titletext, new Integer (4)); //* I messed around with these integers quite a bit. *//
lp.add(newGame, new Integer (20)); //* They don't seem to be logically adding to the panel. *//
lp.add(loadGame, new Integer(20)); //* I want the background and titletext to appear behind *//
lp.add(quitGame, new Integer (20)); //* the JButtons. *//
lp.add(background, new Integer (1));
newGame.addActionListener(this);
loadGame.addActionListener(this);
quitGame.addActionListener(this);
}
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj == newGame)
{
System.out.println("New Game");
lp.remove(4);
getContentPane().add(mop, "Center"); //* Removes myWelcomePanel mwp and adds myOptionsPanel mop *//
repaint();
mop.startgame.addActionListener(this);
}
I have tried repainting, revalidating and removing only certain components but nothing seems to work. I would expect the code posted above to remove the titletext image, but it removes the background image instead.
I would prefer not to mess with the second content pane because it contains a number of objects which I have configured previously.
JLayeredPane.remove(int) refers to the child's absolute index, not its layer position. Why don't you either remove by reference, or simply set the child component's visibility to hidden?
I found a way to make it work. It seems that I must remove each component individually instead of using removeAll(). I identified a new GridLayout sp and set that as the layout. Finally, I had to use revalidate instead of repaint. Repaint left me with a blank screen. If anyone could comment on what is happening here, I would appreciate it!
public void actionPerformed(ActionEvent event)
{
Object obj = event.getSource();
if (obj == newGame)
{
System.out.println("New Game");
lp.remove(background);
lp.remove(titletext);
lp.remove(newGame);
lp.remove(loadGame);
lp.remove(quitGame);
getContentPane().setLayout(sp);
getContentPane().add(mop, "Center");
revalidate();
mop.startgame.addActionListener(this);
}
Related
I have recently begun working with JComponents to create GUI systems. Everything is working but the bottom and right sides of the JFrame do not get painted over and remain white.
Screenshot of running GUI:
In the screenshot you can see the 'drknBtn' is displayed correctly; this is because I hovered over it with the mouse before taking the picture. Hovering over the buttons refreshes them and they appear as normal. Due to this, I would assume the panel that holds them, 'bottomPnl' is covering that white space, but that panels background is not showing at the bottom portion. Any ideas on what could cause this? I have tried calling 'bottomPnl.repaint()' directly before calling pack(), but no change.
My code is below.
Note: For each JComponent, I created a class extending that component. This way I could set default values for the components in the constructors of these classes instead of doing each one individually. I'll list the relevant properties of the Frame and Panels.
Frame: setSize(width,height); setResizeable(false); setLocationRelativeTo(null);
Panel: setLayoutManager(from contructor); setPreferredSize(new Dimension(width,height)); same for setMinimumSize and setMaximumSize.
public Display(String title, int w, int h){
width=w;
height=h;
frame = new FrameUI(title,w,h);
//parent panel
parentPnl= new PanelUI(width,height, new FlowLayout(FlowLayout.CENTER,0,0));
parentPnl.setBackground(new Color(100,175,175));
//top panel
topPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
topPnl.setBackground(new Color(100,175,175));
chooseFileBtn = new ButtonUI("Browse...",topPnl.getWidth()/4,(int)(topPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
fc = new FileChooserUI();
fc.setFileFilter(new FileNameExtensionFilter("Image files", ImageIO.getReaderFileSuffixes()));
int result = fc.showOpenDialog(null);
try {
if (result == JFileChooser.APPROVE_OPTION) {
picture.setIcon(new ImageIcon(ImageIO.read(fc.getSelectedFile()).getScaledInstance(picture.getWidth(),picture.getHeight(), 0)));
}
} catch (Exception iOException) {
}
}
});
//middle panel
midPnl= new PanelUI((int)(width*.85),(int)(height*.7), new FlowLayout(FlowLayout.CENTER,0,0));
midPnl.setBackground(new Color(75,125,125));
picture = new LabelUI("",midPnl.getWidth(),midPnl.getHeight());
picture.setBackground(new Color(75,125,125));
picture.setVisible(true);
picture.setOpaque(true);
picture.setIcon(null);
//bottom panel
bottomPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
bottomPnl.setBackground(new Color(100,175,175));
ltnBtn = new ButtonUI("Lighten Picture",bottomPnl.getWidth()/3,(int)(bottomPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
}
});
ltnBtn.setBackground(Color.LIGHT_GRAY);
ltnBtn.setForeground(Color.BLACK);
drknBtn = new ButtonUI("Darken Picture",bottomPnl.getWidth()/3,(int)(bottomPnl.getHeight()*.9),new ActionListener(){
#Override
public void actionPerformed(ActionEvent e) {
}
});
drknBtn.setBackground(Color.DARK_GRAY);
drknBtn.setForeground(Color.WHITE);
//add UI Objects
topPnl.add(chooseFileBtn);
midPnl.add(picture);
bottomPnl.add(ltnBtn);
bottomPnl.add(drknBtn);
parentPnl.add(topPnl);
parentPnl.add(midPnl);
parentPnl.add(bottomPnl);
Container contentPane = frame.getContentPane();
contentPane.add(parentPnl);
frame.pack();
frame.setVisible(true);
}
}
topPnl= new PanelUI(width,(int)(height*.15), new FlowLayout(FlowLayout.CENTER,0,0));
looks to me like you are manually trying to control the size of the panels and therefore the size of the components added to your panels. Your calculations are wrong and some components aren't displayed properly. Also all your sizes are fixed at creation time and will not adjust if the size of the frame ever changes.
Don't try to control the sizes manually. Use layout managers to dynamically size components based on the properties of the component.
I fail to see why you would want a button to be 15% of the space available to the frame.
If you want the button to be larger than normal you can set extra empty space around the text of the button by using:
button.setMargin( new Insets(50, 50, 50, 50) );
Then just add the button to a panel using a FlowLayout and let the layout manager do its job.
The default layout for a frame is a BorderLayout, so you can then add the "topPnl" to the frame using:
frame.add(topPnl, BorderLayout.PAGE_START);
The other panels can then be added using:
frame.add(midPnl, BorderLayout.CENTER);
frame.add(bottomPnl, BorderLayout.PAGE_END);
This is how Swing was designed to be used with layout managers.
Read the section from the Swing tutorial on How to Use BorderLayout for more information and examples.
The main point is use methods like setMargin(...), to provide hints to the component on what their preferred size should be.
I fixed the problem by removing the 'setSize()' method in the FrameUI constructor. However, I still do not understand how you could dynamically size panels as you said while still maintaining the proportions I want for them. Thank you #camickr for the pointers, my original problem is fixed. I'll look into more javadocs and tutorials on layout managers and such.
I was just having a problem with this:
public class Sales extends JPanel{
ArrayList<JPanel> panes;
ArrayList<String> tabs;
JTabbedPane tp;
public Sales(Dimension d){
setSize(d);
setLayout(null);
tp = new JTabbedPane();
Font f = new Font("Arial",Font.PLAIN,32);
tp.setFont(f);
for(Menu menu : FileManager.menus){
JPanel tmp = new JPanel();
/*int s = (int) Math.ceil(Math.sqrt(menu.products.size()));
tmp.setLayout(new GridLayout(s,s));
System.out.println("size" + s);
for(Product p : menu.products){
p.setFont(f);
tmp.add(p);
}*/
tp.addTab(menu.name,null,tmp,"What is this?");
}
tp.setBounds(0,0,getWidth(),getHeight());
add(tp);
}
}
Where Sales is just added to a simple JFrame:
public Main(){
super("HoboGames Pos System");
setUndecorated(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
sale = new Sales(getSize());
add(sale);
}
Everything works, except the components don't paint until the window is updated because of a click or something. So it's a blank screen until you click stuff.(Sorry, I cut some corners on things like making it full screen...)
They don't update because you've made the window visible before you've finished adding components to it.
Try something like...
GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice().setFullScreenWindow(this);
sale = new Sales(getSize());
add(sale);
setVisible(true);
Alternatively, you could call revalidate and repaint on the frame after you've finished adding your components, but to be honest, it's simpler the first way.
Side Note
Using setSize on a component is strongly discouraged, you should be relying an appropriate layout managed, like BorderLayout to make the decision about the size of the component.
Just started using Swing and I chose null layout because I came from the world of Flash and I'm used to specifying every single pixel and size. I'll pick up on layouts later.
The problem right now is the JTextArea that is the view of the JScrollPane only appears half the time I run the program. I can't select it or type in it because it's not there. The pane appears 100% of the time though. If I hit tab a couple times the focus will land on the text area then it'll appear.
I've removed a huge chunk of the code that has nothing to do with the problem. That's why some of the code looks weird like the MenuItemSelected.
There are so many repaints at the end of the PathsPanel's constructor because it was my "last ditch" effort to fix this problem because I read in many threads that repaint usually fixes things not showing up.
Also if I comment out the setText above the repaints, the text area will show up even less often.
DayPlanner
public DayPlanner () {
//win is a JFrame
win.setDefaultCloseOperation (JFrame.DISPOSE_ON_CLOSE);
win.setLayout (null);
win.setResizable (false);
win.setVisible (true);
MenuItemSelected ("Paths");
win.repaint ();
}
public void MenuItemSelected (String command) {
ChangePanel (PathsPanel.class, pathsPanel);
}
private void ChangePanel (Class panelClass, Component ref) {
activePanel = new PathsPanel (this);
pathsPanel = (PathsPanel) activePanel;
win.add (activePanel);
win.repaint ();
}
}
PathsPanel
public PathsPanel (DayPlanner parent) {
//extends JPanel
this.parent = parent;
setLayout (null);
setBounds (0, 0, 500, 500);
txtMessages = new JTextArea ();
txtMessages.setOpaque (true);
txtMessages.setBackground (Color.RED);
JScrollPane scpMessage = new JScrollPane (txtMessages, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_AS_NEEDED);
scpMessage.setBounds (0, 0, 500, 500);
txtMessages.setText ("AWD");
txtMessages.repaint ();
scpMessage.repaint ();
repaint ();
}
I bet this has something to do with not using a LayoutManager!
I have a JTabbedPane with some tabs and a lot of unused extra space next to the tabs. So I'm trying to use it and place some buttons there (like in Eclipse). I put the buttons on a GlassPane:
JPanel glasspane = getPanelWithButtons();
// panel with FlowLayout.RIGHT
frame.setGlassPane(glasspane);
glasspane.setOpaque(false);
glasspane.setVisible(true);
This works, and I still can click through on the other elements of my gui (most search results I found are about how to prevent this). The only problem so far is that the mouse pointer doesn't change to that double-ended horizontal arrow when it hovers over the bar of a JSplitPane. How can I get this behaviour back?
EDIT
I found that no mouse changing events from any component under the glass pane are shown. Those components would change the mouse cursor to a hand cursor, zoom lenses and others. None of these mouse pointer changes have an effect any more. I guess this is because with the glass pane, the mouse pointer change needs to be made to the glass pane, but I don't want to do all the mouse pointer changing manually.
Well. I figure out how to do it.
Although I spend more than 5 hours to understand all things behind, but the solution is very simple.
Just overwrite 'public boolean contains(int x, int y)' method of glass panel.
public static void main(String[] args)
{
final JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setSize(800, 600);
final JSplitPane panel = new JSplitPane(JSplitPane.HORIZONTAL_SPLIT, new JPanel(), new JPanel());
frame.getContentPane().add(panel, BorderLayout.CENTER);
final JPanel glassPane = new JPanel(){
#Override
public boolean contains(int x, int y)
{
Component[] components = getComponents();
for(int i = 0; i < components.length; i++)
{
Component component = components[i];
Point containerPoint = SwingUtilities.convertPoint(
this,
x, y,
component);
if(component.contains(containerPoint))
{
return true;
}
}
return false;
}
};
glassPane.setOpaque(false);
JButton button = new JButton("haha");
button.addActionListener(new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
System.out.println("haha");
}
});
glassPane.add(button);
glassPane.setBorder(BorderFactory.createLineBorder(Color.red));
frame.setGlassPane(glassPane);
//try to comment out this line to see the difference.
glassPane.setVisible(true);
frame.setVisible(true);
}
Well. I have found a solution to the problem "place buttons next to the tabs". I don't use a glass pane any more, but place the buttons directly:
buttonpanel.setBounds(...);
frame.getLayeredPane().add(buttonpanel, 1);
This works and solves my problem. It is a bit more complicated and involves doing layout by hand and listening to frame resize events, though.
Since I still would like to know how to accomplish this with a glass pane, I'm not accepting this answer. Maybe someone comes up with a solution for a glass pane.
The structure likes this:
I have a Frame and two button - btnA, btnB
when I press btnA, a createPanel() function will return a panelA to be displayed in the frame,
and so does btnB.
btnA and btnB can be switched.
Before I add the panel into the frame, I use a clearPanel() function to clear the existing panels in the frame.
but the question is when I resize or click the panel, I can see the previous panels that should be removed already.
Is there anything I lost?
public void actionPerformed(ActionEvent e) {
String buttonString = e.getActionCommand();
if (buttonString.equals("A")) {
clearPanel();
A = new APanel();
this.getContentPane().add(A.createPanel(), BorderLayout.CENTER);
this.pack();
componentMap.put("A", A);
btnB.setEnabled(true);
btnA.setEnabled(false);
}
else if (buttonString.equals("B")) {
clearPanel();
chart = new BPanel();
this.getContentPane().add(B.createPanel(), BorderLayout.CENTER);
this.pack();
componentMap.put("B", B);
btnA.setEnabled(true);
btnB.setEnabled(false);
}
}
private void clearPanel() {
if (!componentMap.isEmpty()) { // I store panels in a HashMap
for (Object o: componentMap.values()) {
this.getContentPane().remove((JPanel)o);
}
this.getContentPane().invalidate();
componentMap.clear();
}
}
You are adding A.createPanel() and B.createPanel() to the contentPane but you store A and B in your componentMap. Therefore, when you call this.getContentPane().remove((JPanel)o);, you are doing this on A and/or B which are not in the content pane and therefore you don't remove anything.
You could use a simpler/safer approach if you want to clear the content pane:
this.getContentPane().removeAll();