Base and extended windows in Java using inheritance - java

I've got one class called WindowTemplate that is the base for other (more complex) windows. It is an abstract class and then I'm trying to use the "extend" trick to add more stuff to the new window, keeping the original "skeleton". That is my problem though, because if I run WindowTemplate.createWindow(); or a_Welcome.createWindow(); (they should be point to the same thing), I get my "base" window. But when I run a_Welcome window = new a_Welcome(); (what should be the base + new stuff) I get only the extra bits that I added without the original features. Here is my code:
package windows;
import java.awt.*;
import javax.swing.*;
public abstract class WindowTemplate extends JFrame {
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
public static void createWindow() {
JFrame myFrame = new JFrame("My first window");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
myFrame.setSize(550, 450);
myFrame.setLocationRelativeTo(null);
// JLabel emptyLabel = new JLabel("");
// emptyLabel.setPreferredSize(new Dimension(550, 450));
// myFrame.getContentPane().setLayout(new CardLayout());
// myFrame.getContentPane().add(emptyLabel, BorderLayout.CENTER);
// myFrame.pack();
}
}
the class with new window and some extra stuff (ignore a_):
package windows;
import java.awt.*;
import javax.swing.*;
public class a_Welcome extends WindowTemplate {
public a_Welcome() {
JPanel area = new JPanel();
JLabel text = new JLabel("One line another line and another line"); // , JLabel.CENTER);
// text.setBounds(80, 400, 400, 50);
add(area);
// area.setLayout(null);
area.add(text, new CardLayout());
// area.add(text); // , BorderLayout.CENTER);
Font font = new Font("SansSerif", Font.BOLD, 30);
text.setFont(font);
text.setForeground(Color.green);
area.setBackground(Color.darkGray);
area.setSize(550, 450);
}
}
// timer-after 5 seconds-go to the next window (countdown in the bottom right corner)
and the main:
package windows;
public class Launcher {
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() {
// WindowTemplate.createWindow();
// a_Welcome.createWindow();
a_Welcome window = new a_Welcome();
window.setVisible(true);
}
});
}
}
Thanks for your help!

Static method createWindow() always creates a new JFrame which is not a superclass of the WindowTemplate. Constructor of the a_Window is adding components to the WindowTemplate which hasn't been initialized since the static createWindow() creates an independent frame.
I would suggest you to change the static createWindow() into WindowTemplate constructor and try running main once again.
package windows;
import java.awt.*;
import javax.swing.*;
public abstract class WindowTemplate extends JFrame {
/**
* Create the GUI and show it. For thread safety, this method should be
* invoked from the event-dispatching thread.
*/
public WindowTemplate () {
JFrame myFrame = new JFrame("My first window");
myFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
myFrame.setVisible(true);
myFrame.setSize(550, 450);
myFrame.setLocationRelativeTo(null);
// JLabel emptyLabel = new JLabel("");
// emptyLabel.setPreferredSize(new Dimension(550, 450));
// myFrame.getContentPane().setLayout(new CardLayout());
// myFrame.getContentPane().add(emptyLabel, BorderLayout.CENTER);
// myFrame.pack();
}
}

You have another JFrame defined in the static createWindow() method. This means that you are adding the components to this frame that is scoped to the createWindow() method only and in the constructor you are adding to the a_Welcome instance.
You should do something like this
public class BaseWindow() {
//Constructor
public BaseWindow() {
init();
}
public void init() {
//add basic components
}
}
public class SubClassWindow() {
public SubClassWindow() {
super();
}
#Override
public void init() {
super.init(); //important so you get the base stuff
//add other components
}
}
Code not tested.

Another approach you might consider would be to have a JFrame that is just a wrapper and compose the window by adding a panel. Let's say you want a toolbar at the top of every window you're creating. Each window would have different buttons on the toolbar and a different set of components at the bottom. This way you are doing composition instead of inheritance, because inheritance can get ugly later on. (For discussions on that point, see this, this, and this for starters)
That would look something like:
public interface AppPanel {
List<JButton> getToolbarButtons();
boolean okToClose();
JPanel getGui();
}
public MyPanel extends JPanel implements AppPanel {
//standard swing components stuff set up here
public List<JButton> getToolbarButtons() {
//set up buttons and their actions
return buttonList;
}
public boolean okToClose() {
//ask user if they want to save, etc.
return true;
}
public JPanel getGui() {
return this;
}
}
public AppFrame extends JFrame {
private AppPanel panel;
public static AppFrame createFrame(AppPanel panel) {
AppFrame frame = new AppFrame(panel);
return frame;
}
public AppFrame(AppPanel panel) {
super();
this.panel = panel;
add(panel.getGui(), someLayoutConstraints);
panel.getToolbarButtons(); //do stuff with the buttons
//...
this.addWindowListener(new WindowAdapter() {
public void WindowClosing(WindowEvent e) {
if (panel.isOkToClose()) {
setVisible(false);
}
}
});
}
}

Related

Switching between panels LayeredPane [duplicate]

This question is about Frames, Java and Processing.
This questions sounds pretty convoluted but its really not. I'll try keep this to a simple minimum. I'm creating a small ball in a maze game to get my head around physics and rendering. It's been a good experience so far but I've hit a bit of a brick wall.
The general layout I decided on was to contain PApplets within a AWT Frame and have the Frame close. The reason for this is because I was told that you should only have on instance of a Papplet at a time.
PApplet is the Applet class in Processing, a rendering library.
I have 3 classes here including the main
public class Menu extends PApplet
{
//images and buttons
PImage background, playbtn1, playbtn2, hsbtn1, hsbtn2, abbtn1, abbtn2, exbtn1, exbtn2;
FBox pBtn, hBtn, eBtn;
FWorld menu;
//simple constructor
public Menu()
{
}
public void setup()
{
size(600, 400);
smooth();
Fisica.init(this);
menu = new FWorld();
//loading and placing images
background = loadImage("MenuAlt.jpg");
System.out.println(background);
playbtn1 = loadImage("play1.gif");
playbtn2 = loadImage("play2.gif");
hsbtn1 = loadImage("high1.gif");
hsbtn2 = loadImage("high2.gif");
exbtn1 = loadImage("exit1.gif");
exbtn2 = loadImage("exit2.gif");
//loading and placing buttons
pBtn = new FBox(120, 150);
pBtn.setPosition(135, 215);
pBtn.setDrawable(false);
hBtn = new FBox(120, 150);
hBtn.setPosition(295, 215);
hBtn.setDrawable(false);
eBtn = new FBox(120, 150);
eBtn.setPosition(455, 215);
eBtn.setDrawable(false);
//add item to world
menu.add(pBtn);
menu.add(hBtn);
menu.add(eBtn);
}
public void draw()
{
image(background, 0, 0);
image(playbtn1, 80, 140);
image(hsbtn1, 237, 135);
image(exbtn1, 400, 140);
mouseOver();
menu.draw();
}
//close this frame an open a new level, high score or exit
//depending on what the use clicks
public void mousePressed()
{
FBody pressed = menu.getBody(mouseX, mouseY);
if (pressed == pBtn)
{
System.out.println("play game");
this.getParent().getParent().getParent().getParent().setVisible(false);
ExampleFrame x = new ExampleFrame(new Level("level1.txt"));
x.setLocation(this.getParent().getParent().getParent().getParent().getLocation());
}
if (pressed == hBtn)
{
System.out.println("high scores");
this.getParent().getParent().getParent().getParent().setVisible(false);
/* these are just for finding the parent
System.out.println(this.getName());
System.out.println(this.getParent().getName());
System.out.println(this.getParent().getParent().getName());
System.out.println(this.getParent().getParent().getParent().getName());
System.out.println(this.getParent().getParent().getParent().getParent().getName());
*/
ExampleFrame x = new ExampleFrame(new HighScores()); //for testing, you can change this to new menu()
x.setLocation(this.getParent().getParent().getParent().getParent().getLocation());
}
if (pressed == eBtn)
{
System.out.println("exit");
System.exit(0);
}
}
the exampleFrame class
public class ExampleFrame extends JFrame
{
PApplet app;
public ExampleFrame(PApplet emApp)
{
super("Ball Maze Game");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocation(200, 200);
app = emApp;
setSize(615,438);
setVisible(true);
setLayout(new BorderLayout());
add(app, BorderLayout.CENTER);
app.init();
}
}
the main
public class Main
{
public static void main(String[] args)
{
ExampleFrame x = new ExampleFrame(new Menu());
}
}
What needs to happen when mousePressed == ebtn is all the stuff in the Frame will be removed and a Highscores Screen will be loaded. highscores is almost the same as menu. There is no need to post code as there is enough here.
The second class is the one which acts as a frame and holds the PApplet
Bottom line, has anyone have any idea how to call the Frame methods from the PApplet or another way to remove all PApplets contents and load another PApplet in?
What needs to happen when mousePressed == ebtn is all the stuff in the Frame will be removed and a Highscores Screen will be loaded
The demo. below of a nested CardLayout adds an ActionListener instead of a MouseListener. It reacts to both mouse and keyboard input.
There are a multitude of other ways to include more than one GUI element in the same screen space. Off the top of my head, JTabbedPane, JSplitPane, JDesktopPane/JInternalFrame, popping the high scores in a JDialog or JOptionPane..
Screenshots
CardLayoutDemo.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class CardLayoutDemo {
public static void main(String[] args) {
Runnable r = new Runnable () {
public void run() {
final JRadioButton game = new JRadioButton("Game", true);
JRadioButton highScores = new JRadioButton("High Scores");
ButtonGroup bg = new ButtonGroup();
bg.add( game );
bg.add( highScores );
JPanel buttons = new JPanel(new
FlowLayout(FlowLayout.CENTER, 5, 5));
buttons.add( game );
buttons.add( highScores );
JPanel gui = new JPanel(new BorderLayout(5,5));
gui.add(buttons, BorderLayout.SOUTH);
final CardLayout cl = new CardLayout();
final JPanel cards = new JPanel(cl);
gui.add(cards);
cards.add(new JLabel("Level 1"), "game");
cards.add(new JLabel("High Scores"), "scores");
ActionListener al = new ActionListener(){
public void actionPerformed(ActionEvent ae) {
if (game.isSelected()) {
cl.show(cards, "game");
} else {
cl.show(cards, "scores");
}
}
};
game.addActionListener(al);
highScores.addActionListener(al);
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
In order to answer How to call the Frame methods from the PApplet?, I have modified your code snippet to bare minimum. In this modified version when the user click mouse button a System.out is fired.
Now there are two ways in which you can access your Frame object. But before that let me state these two points:
When you create a PApplet like new ExampleFrame(new Menu()); and add it in your JFrame like this add(app, BorderLayout.CENTER); then a complex hierarchy of windows/panels are created.
Like this:
javax.swing.JPanel
javax.swing.JLayeredPane
javax.swing.JRootPane
test.ExampleFrame
PApplet provides a public field for setting and accessing your frame object. And amazingly it is called frame :). You can set it before calling app.init();
>>Code
** Checkout the comments in the code**
Modified ExampleFrame.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
import processing.core.PApplet;
public class ExampleFrame extends JFrame
{
private static final long serialVersionUID = 4792534036194728580L;
PApplet app;
public ExampleFrame(PApplet emApp)
{
super("Ball Maze Game");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocation(200, 200);
app = emApp;
setSize(615,438);
setVisible(true);
setLayout(new BorderLayout());
add(app, BorderLayout.CENTER);
// Setting my frame object
app.frame = this;
app.init();
}
// Sample Method
public void sampleMethod(String msg)
{
System.out.println("I think '"+ msg +"' called me !!");
}
}
Modified Menu.java
import java.awt.Container;
import processing.core.PApplet;
import processing.core.PImage;
public class Menu extends PApplet
{
private static final long serialVersionUID = -6557167654705489372L;
PImage background;
static String tab = "";
//simple constructor
public Menu()
{
}
public void setup()
{
size(600, 400);
smooth();
background = loadImage("C:/temp/background.jpg");
}
public void draw()
{
image(background, 0, 0);
}
public void mousePressed()
{
Container p = getParent();
tab = "";
// FIRST WAY OF ACCESSING PARENT FRAME
while(p != null)
{
//printParentTree(p);
if(p instanceof ExampleFrame)
{
ExampleFrame myframe = (ExampleFrame)p;
myframe.sampleMethod("First Way");
break;
}
p = p.getParent();
}
// SECOND WAY OF ACCESSING PARENT FRAME
if(frame != null && (frame instanceof ExampleFrame))
{
ExampleFrame myframe = (ExampleFrame)p;
myframe.sampleMethod("Second Way");
}
}
void printParentTree(Container p)
{
System.out.println(tab+p.getClass().getName());
tab +='\t';
}
}
Checkout the public void mousePressed() method.
For completeness, I am also including Main.java.
public class Main {
public static void main(String[] args){
new ExampleFrame(new Menu());
}
}
Now to answer Remove all PApplets contents and load another PApplet in
Well I have not tested it. But you can add a JPanel to your JApplet and do all your drawing on that i.e creating child controls etc. When feel like redrawing then call JPanel.removeAll(). Which as per javadoc:
Removes all the components from this
container. This method also notifies
the layout manager to remove the
components from this container's
layout via the removeLayoutComponent
method.
After this call repaint on the JPanel. Try it out, it might work :).

Calling different screen thru button from a screen

Hello I would like to ask how can I call my Main menu screen from MainScreen? and kindly explain a little more details about Listener.
below is my prepared code:
public class MainScreen {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel();
frame.add(panel);
placeComponents(panel);
frame.setVisible(true);
}
private static void placeComponents(JPanel panel) {
JLabel WelcomeNote = new JLabel("Welcome");
panel.add(WelcomeNote);
JButton Start = new JButton("Start");
panel.add(Start);
//Insert action for Start button here
}
}
public class MainMenu {
public static void main(String[] args){
JFrame frame = new JFrame();
JPanel panel = new JPanel();
frame.add(panel);
placeComponents(panel);
frame.setVisible(true);
}
private static void placeComponents(JPanel panel) {
JLabel menuLbl = new JLabel("Main Menu");
panel.add(menuLbl);
}
}
What is wrong?
You cannot have two main methods in a single file in Java.
Program
Here is a demo program to change windows.
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
import java.util.*;
class First extends JFrame
{
JLabel jlb = new JLabel("Label in First Window");
JButton jb = new JButton("Next Window");
First()
{
super("First Windows");
//Set this frame
this.setSize(350,250);
this.setLayout(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Setting size of components
jlb.setBounds(10,10,200,40);
jb.setBounds(10,120,150,40);
add(jlb);
add(jb);
jb.addActionListener((e)->{
this.setVisible(false);
new Second();
});
setVisible(true);
}
}
class Second extends JFrame implements ActionListener
{
JLabel jlb = new JLabel("Label in Second Window");
JButton jb = new JButton("Prev. Window");
Second()
{
super("Second Window");
this.setSize(350,250);
this.setLayout(null);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Setting size of components
jlb.setBounds(10,10,200,40);
jb.setBounds(10,120,150,40);
add(jlb);
add(jb);
jb.addActionListener(this);
setVisible(true);
}
public void actionPerformed(ActionEvent e)
{
this.setVisible(false);
new First();
}
}
class StartHere
{
public static void main(String[] args) {
Runnable r = ()->{
new First();
};
r.run();
}
}
Understanding the above program.
The StartHere class has a main method. It is just used for calling the first window you like. I could even call Second using new Second().
First and Second are similar codes.
Both of them have buttons. On each button (or JButton) I have added a method named addActionListner(this). This method fires up an ActionEvent which as you can see in Second class is captured by actionPerformed method. This method is declared in Functional Interface, ActionListener. The 'this' passed in Second class is you telling where the actionPerformed method is present in your code. The parameter is an ActionListener. Hence, you have to implement ActionListener for the class where you define actionPerformed.
Bonus
The First class doesn't seem to follow the norms described above. I passed a strange syntax. It is a new feature included in Java 8.
See this Oracle tutorial about Lambda Expressions.

Run new GUI window from an event of another class

I have 2 classes. Both implements runnable to create the GUI. The first one is the main, and the second one is the secondary class.
I want within the actionlistener of the main class to startup the secondary class.
Here is the code (the two classes are separated files):
public class Main implements Runnable
{
private JTextField txt1, txt2;
private JLabel lbl1, lbl2;
public void run()
{
JFrame frame = new JFrame("Secondary");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = frame.getContentPane();
JPanel background = new JPanel();
background.setLayout(new BoxLayout(background, BoxLayout.LINE_AXIS));
.........
// Horizontally adding the textbox and button in a Box
Box box = new Box(BoxLayout.Y_AXIS);
......
background.add(box);
pane.add(background);
frame.pack();
frame.setVisible(true);
}
private class SListener implements ActionListener
{
public void actionPerformed(ActionEvent a)
{
Secondary s = new Secondary();
}
}
public static void main (String[] args)
{
Main gui = new Main();
SwingUtilities.invokeLater(gui);
}
}
public class Secondary implements Runnable
{
private JTextField txt1, txt2;
private JLabel lbl1, lbl2;
public Secondary()
{
Secondary gui = new Secondary();
SwingUtilities.invokeLater(gui);
}
public void run()
{
JFrame frame = new JFrame("Secondary");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = frame.getContentPane();
JPanel background = new JPanel();
background.setLayout(new BoxLayout(background, BoxLayout.LINE_AXIS));
.........
// Horizontally adding the textbox and button in a Box
Box box = new Box(BoxLayout.Y_AXIS);
......
background.add(box);
pane.add(background);
frame.pack();
frame.setVisible(true);
}
}
I want to keep the code in two files, I don't want to mixed the two classes in one file.
As you can see from the code, in the Secondary class, in it's constructor I create an Instance of the Secondary class and I run the gui so that when the Instance of this class is created in the Main class, to run the gui.
Unfortunately this technique is not working.
Any ideas?
Thanks
The following line are complety wrong:
public Secondary(){
Secondary gui = new Secondary();
SwingUtilities.invokeLater(gui);
}
Each time you call new Secondary() somewhere in your code, the above code will be triggered, which in turn calls new Secondary() again, and again, and again, ... and your program is blocked.
You probably want to replace it either by
public Secondary(){
SwingUtilities.invokeLater(this);
}
which will avoid the loop, but this is weird behaviour for a constructor.
It makes much more sense to switch to an empty constructor (or delete it all together)
public Secondary(){
}
and rewrite your listener to
public void actionPerformed(ActionEvent a){
Secondary s = new Secondary();
SwingUtilities.invokeLater( s );
}
I would recommend that you completely re-design your program. I find that it is most helpful to gear my GUI's towards creation of JPanels, not top level windows such as JFrame, which can then be placed into JFrames or JDialogs, or JTabbedPanes, or swapped via CardLayouts, wherever needed. I find that this greatly increase the flexibility of my GUI coding, and is exactly what I suggest that you do. So...
Your first class creates a JPanel that is then placed into a JFrame.
In the first class's ActionListener, create an instance of the 2nd class, place it into a JDialog (not a JFrame), and then display it.
For example,
import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class TwoWindowEg {
public TwoWindowEg() {
// TODO Auto-generated constructor stub
}
private static void createAndShowGui() {
GuiPanel1 mainPanel = new GuiPanel1();
JFrame frame = new JFrame("Main GUI");
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();
}
});
}
}
class GuiPanel1 extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 650;
private GuiPanel2 guiPanel2 = new GuiPanel2(); // our second class!
private JDialog dialog = null; // our JDialog
public GuiPanel1() {
setBorder(BorderFactory.createTitledBorder("GUI Panel 1"));
add(new JButton(new LaunchNewWindowAction("Launch New Window")));
add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class LaunchNewWindowAction extends AbstractAction {
public LaunchNewWindowAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
if (dialog == null) {
// get the Window that holds this JPanel
Window win = SwingUtilities.getWindowAncestor(GuiPanel1.this);
dialog = new JDialog(win, "Second Window", ModalityType.APPLICATION_MODAL);
dialog.add(guiPanel2);
dialog.pack();
}
dialog.setVisible(true);
}
}
}
class GuiPanel2 extends JPanel {
public GuiPanel2() {
setBorder(BorderFactory.createTitledBorder("GUI Panel 1"));
add(new JLabel("The second JPanel/Class"));
add(new JButton(new DisposeAction("Exit", KeyEvent.VK_X)));
}
}
class DisposeAction extends AbstractAction {
public DisposeAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
Component comp = (Component) e.getSource();
Window win = SwingUtilities.getWindowAncestor(comp);
win.dispose();
}
}
Alternatively, you could swap JPanel "views" using a CardLayout, but either way, you will want to avoid showing two JFrames. Please have a look at The Use of Multiple JFrames, Good/Bad Practice?.

How can i add the JPanel class into JFrame form class in netbeans?

I created two classes in netbeans;One of them is a JPanel form and another is a JFrame form;
How can i add the JPanel class into JFrame form class?
I wrote this code in constructor of JFrame form Class but ,it didnt work.
public JFrameClass() {
initComponents();
this.getContentPane().add(jpc = new JPanelClass());
jpc.setVisible(true);
this.pack();
this.setVisible(true);
}
You need to make sure the JPanelClass is visible from where your JFrameClass is.
Then do the following:
JPanelClass jpc = new JPanelClass()
this.getContentPane().add(jpc);
Also, there is no need to call jpc.setVisible(true);
The resulting code should be:
public JFrameClass() {
initComponents();
JPanelClass jpc = new JPanelClass()
getContentPane().add(jpc);
pack();
setVisible(true);
}
How can i add the JPanel class into JFrame form class in netbeans?
In your JFrame class just set your JPanel and add its to Container.
JPanel panel = new JPanelClass();
controls.add(panel);
Note: You should have some private void method named for example createAndAddCompontents() and call it in your constructor.
public JFrameClass() {
...
createAndAddCompontents();
}
Then when you want to execute your Application so in main() method you should call it similar like this:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
YouJFrameClass initAndShowComponents = new YouJFrameClass();
initAndShowComponents.setVisible(true);
}
});
set the bounds of the JPanel so that the container knows where to draw it
public void run() {
NewJFrame frame = new NewJFrame();
NewJPanel panel = new NewJPanel();
panel.setBounds(0, 0, 200, 200);
frame.add(panel);
frame.setVisible(true);
}

Calling awt Frame methods from subclass

This question is about Frames, Java and Processing.
This questions sounds pretty convoluted but its really not. I'll try keep this to a simple minimum. I'm creating a small ball in a maze game to get my head around physics and rendering. It's been a good experience so far but I've hit a bit of a brick wall.
The general layout I decided on was to contain PApplets within a AWT Frame and have the Frame close. The reason for this is because I was told that you should only have on instance of a Papplet at a time.
PApplet is the Applet class in Processing, a rendering library.
I have 3 classes here including the main
public class Menu extends PApplet
{
//images and buttons
PImage background, playbtn1, playbtn2, hsbtn1, hsbtn2, abbtn1, abbtn2, exbtn1, exbtn2;
FBox pBtn, hBtn, eBtn;
FWorld menu;
//simple constructor
public Menu()
{
}
public void setup()
{
size(600, 400);
smooth();
Fisica.init(this);
menu = new FWorld();
//loading and placing images
background = loadImage("MenuAlt.jpg");
System.out.println(background);
playbtn1 = loadImage("play1.gif");
playbtn2 = loadImage("play2.gif");
hsbtn1 = loadImage("high1.gif");
hsbtn2 = loadImage("high2.gif");
exbtn1 = loadImage("exit1.gif");
exbtn2 = loadImage("exit2.gif");
//loading and placing buttons
pBtn = new FBox(120, 150);
pBtn.setPosition(135, 215);
pBtn.setDrawable(false);
hBtn = new FBox(120, 150);
hBtn.setPosition(295, 215);
hBtn.setDrawable(false);
eBtn = new FBox(120, 150);
eBtn.setPosition(455, 215);
eBtn.setDrawable(false);
//add item to world
menu.add(pBtn);
menu.add(hBtn);
menu.add(eBtn);
}
public void draw()
{
image(background, 0, 0);
image(playbtn1, 80, 140);
image(hsbtn1, 237, 135);
image(exbtn1, 400, 140);
mouseOver();
menu.draw();
}
//close this frame an open a new level, high score or exit
//depending on what the use clicks
public void mousePressed()
{
FBody pressed = menu.getBody(mouseX, mouseY);
if (pressed == pBtn)
{
System.out.println("play game");
this.getParent().getParent().getParent().getParent().setVisible(false);
ExampleFrame x = new ExampleFrame(new Level("level1.txt"));
x.setLocation(this.getParent().getParent().getParent().getParent().getLocation());
}
if (pressed == hBtn)
{
System.out.println("high scores");
this.getParent().getParent().getParent().getParent().setVisible(false);
/* these are just for finding the parent
System.out.println(this.getName());
System.out.println(this.getParent().getName());
System.out.println(this.getParent().getParent().getName());
System.out.println(this.getParent().getParent().getParent().getName());
System.out.println(this.getParent().getParent().getParent().getParent().getName());
*/
ExampleFrame x = new ExampleFrame(new HighScores()); //for testing, you can change this to new menu()
x.setLocation(this.getParent().getParent().getParent().getParent().getLocation());
}
if (pressed == eBtn)
{
System.out.println("exit");
System.exit(0);
}
}
the exampleFrame class
public class ExampleFrame extends JFrame
{
PApplet app;
public ExampleFrame(PApplet emApp)
{
super("Ball Maze Game");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocation(200, 200);
app = emApp;
setSize(615,438);
setVisible(true);
setLayout(new BorderLayout());
add(app, BorderLayout.CENTER);
app.init();
}
}
the main
public class Main
{
public static void main(String[] args)
{
ExampleFrame x = new ExampleFrame(new Menu());
}
}
What needs to happen when mousePressed == ebtn is all the stuff in the Frame will be removed and a Highscores Screen will be loaded. highscores is almost the same as menu. There is no need to post code as there is enough here.
The second class is the one which acts as a frame and holds the PApplet
Bottom line, has anyone have any idea how to call the Frame methods from the PApplet or another way to remove all PApplets contents and load another PApplet in?
What needs to happen when mousePressed == ebtn is all the stuff in the Frame will be removed and a Highscores Screen will be loaded
The demo. below of a nested CardLayout adds an ActionListener instead of a MouseListener. It reacts to both mouse and keyboard input.
There are a multitude of other ways to include more than one GUI element in the same screen space. Off the top of my head, JTabbedPane, JSplitPane, JDesktopPane/JInternalFrame, popping the high scores in a JDialog or JOptionPane..
Screenshots
CardLayoutDemo.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
class CardLayoutDemo {
public static void main(String[] args) {
Runnable r = new Runnable () {
public void run() {
final JRadioButton game = new JRadioButton("Game", true);
JRadioButton highScores = new JRadioButton("High Scores");
ButtonGroup bg = new ButtonGroup();
bg.add( game );
bg.add( highScores );
JPanel buttons = new JPanel(new
FlowLayout(FlowLayout.CENTER, 5, 5));
buttons.add( game );
buttons.add( highScores );
JPanel gui = new JPanel(new BorderLayout(5,5));
gui.add(buttons, BorderLayout.SOUTH);
final CardLayout cl = new CardLayout();
final JPanel cards = new JPanel(cl);
gui.add(cards);
cards.add(new JLabel("Level 1"), "game");
cards.add(new JLabel("High Scores"), "scores");
ActionListener al = new ActionListener(){
public void actionPerformed(ActionEvent ae) {
if (game.isSelected()) {
cl.show(cards, "game");
} else {
cl.show(cards, "scores");
}
}
};
game.addActionListener(al);
highScores.addActionListener(al);
JOptionPane.showMessageDialog(null, gui);
}
};
SwingUtilities.invokeLater(r);
}
}
In order to answer How to call the Frame methods from the PApplet?, I have modified your code snippet to bare minimum. In this modified version when the user click mouse button a System.out is fired.
Now there are two ways in which you can access your Frame object. But before that let me state these two points:
When you create a PApplet like new ExampleFrame(new Menu()); and add it in your JFrame like this add(app, BorderLayout.CENTER); then a complex hierarchy of windows/panels are created.
Like this:
javax.swing.JPanel
javax.swing.JLayeredPane
javax.swing.JRootPane
test.ExampleFrame
PApplet provides a public field for setting and accessing your frame object. And amazingly it is called frame :). You can set it before calling app.init();
>>Code
** Checkout the comments in the code**
Modified ExampleFrame.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
import processing.core.PApplet;
public class ExampleFrame extends JFrame
{
private static final long serialVersionUID = 4792534036194728580L;
PApplet app;
public ExampleFrame(PApplet emApp)
{
super("Ball Maze Game");
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocation(200, 200);
app = emApp;
setSize(615,438);
setVisible(true);
setLayout(new BorderLayout());
add(app, BorderLayout.CENTER);
// Setting my frame object
app.frame = this;
app.init();
}
// Sample Method
public void sampleMethod(String msg)
{
System.out.println("I think '"+ msg +"' called me !!");
}
}
Modified Menu.java
import java.awt.Container;
import processing.core.PApplet;
import processing.core.PImage;
public class Menu extends PApplet
{
private static final long serialVersionUID = -6557167654705489372L;
PImage background;
static String tab = "";
//simple constructor
public Menu()
{
}
public void setup()
{
size(600, 400);
smooth();
background = loadImage("C:/temp/background.jpg");
}
public void draw()
{
image(background, 0, 0);
}
public void mousePressed()
{
Container p = getParent();
tab = "";
// FIRST WAY OF ACCESSING PARENT FRAME
while(p != null)
{
//printParentTree(p);
if(p instanceof ExampleFrame)
{
ExampleFrame myframe = (ExampleFrame)p;
myframe.sampleMethod("First Way");
break;
}
p = p.getParent();
}
// SECOND WAY OF ACCESSING PARENT FRAME
if(frame != null && (frame instanceof ExampleFrame))
{
ExampleFrame myframe = (ExampleFrame)p;
myframe.sampleMethod("Second Way");
}
}
void printParentTree(Container p)
{
System.out.println(tab+p.getClass().getName());
tab +='\t';
}
}
Checkout the public void mousePressed() method.
For completeness, I am also including Main.java.
public class Main {
public static void main(String[] args){
new ExampleFrame(new Menu());
}
}
Now to answer Remove all PApplets contents and load another PApplet in
Well I have not tested it. But you can add a JPanel to your JApplet and do all your drawing on that i.e creating child controls etc. When feel like redrawing then call JPanel.removeAll(). Which as per javadoc:
Removes all the components from this
container. This method also notifies
the layout manager to remove the
components from this container's
layout via the removeLayoutComponent
method.
After this call repaint on the JPanel. Try it out, it might work :).

Categories

Resources