ActionListener in a separate Class - java

First off this is a homework assignment so explanations and pointers are preferred over flat solutions. We are learning swing and are practicing separate class ActionListeners (bonus question, why would you use a separate class over an inner class, it seems like inner class is simpler and less error prone without losing any real abilities). The problems I have been running into are passing the Frame as a parameter so that the separate class can access the tools it needs, and then using the separate class to actually change the display. The project is supposed to work something like a slideshow, having a timer as a default switch, but also implementing buttons to move manually.
import javax.swing.*;
import java.awt.BorderLayout;
import java.awt.Image;
import java.awt.event.*;
import java.io.File;
public class SliderFrame extends JFrame{
public SliderFrame(){
File file1 = new File("images"); //change as necessary
File file = new File("images\\CMU");
File[] paths;
paths = file.listFiles();
//file1
ImageIcon left = new ImageIcon("backward.png");
ImageIcon right = new ImageIcon("forward.png");
JButton btnLeft = new JButton(left);
btnLeft.addActionListener(new MyActionListener(this));
JButton btnRight = new JButton(right);
btnRight.addActionListener(new MyActionListener(this));
JTextField jtfTitle = new JTextField("Welcome to CMU!");
JLabel jlbMain = new JLabel();
new Timer(2000, new MyActionListener(this)).start();
JPanel panel = new JPanel();
panel.setLayout(new BorderLayout());
panel.add("PAGE_START", jtfTitle);
panel.add("Center", jlbMain);
panel.add("LINE_START", btnLeft);
panel.add("LINE_END", btnRight);
add(panel);
setTitle("CPS240 SlideShow");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
pack();
setVisible(true);
}
public static void main(String[] args) {
JFrame frame = new SliderFrame();
btnRight.addActionListener(new MyActionListener(frame));
}
}
And then my ActionListener Class
import java.awt.event.*;
import javax.swing.*;
//does it need to extend SliderFrame? Originally I thought it would help with some of my errors
public class MyActionListener extends SliderFrame implements ActionListener {
JFrame frame;
public MyActionListener(JFrame frame) {
this.frame = frame;
}
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() instanceof Timer){
//here's where I need to be able to change the 'main' label in the frame
} else if(e.getSource() == btnRight){
//trying to figure out if the left or right button was pushed
} else{
}
}
}
I'm not sure if the source of my errors lie in how I set up the format to start with or if I'm just not getting something. Any help or opinions would be greatly appreciated.

bonus question, why would you use a separate class over an inner class, it seems like inner class is simpler and less error prone without losing any real abilities
Initially, you had no choice, as you couldn't have inner classes, but, there may be occasions where the functionality is common and can easily be repeated, for example, an "Open File" action, which is manged by a toolbar button, menu item and keyboard short cut...
First your ActionListener does not need to extend from SliderFrame, but instead, probably wants a reference to an instance of SliderFrame...
This
public class MyActionListener extends SliderFrame implements ActionListener {
Should probably be more like
public class MyActionListener implements ActionListener {
Instead of passing a reference of JFrame, you will want to pass a reference of SliderFrame. Having said that, I have no idea where btnRight is, but I'm pretty sure it shouldn't be maintained within the main method, but within the SliderFrame itself...
public class SliderFrame extends JFrame{
public SliderFrame(){
//...
btnRight.addActionListener(new MyActionListener(this));
You ActionListener should also expect an instance of SliderFrame
public class MyActionListener extends SliderFrame implements ActionListener {
private SliderFrame frame;
public MyActionListener(SliderFrame frame) {
This allows your ActionListener to make use of functionality that is defined by the SliderFrame, which won't be available from an instance of JFrame
Next, you want to provide functionality in your SliderFrame which can be used to update the state of the slide show...
public class SliderFrame extends JFrame{
//...
public void nextSlide() {
//...
}
public void previousSlide() {
//...
}
Then when your ActionListener is triggered, you just call the appropriate methods on SliderFrame
public class NextSlideActionListener extends SliderFrame implements ActionListener {
//...
#Override
public void actionPerformed(ActionEvent e) {
frame.nextSlide();
}
}
(ps- the above example can be used by the Timer and "next" button, because the functionality is the same for both)

Related

Inheriting classes in JButton

Hi guyz i have a prob in writing java code for my project as am unable to tackle that, i want my "Categories" button to perform action like it should show bread class in it but am unable to inherit it and put a pic on it so can anyone here just tell me whats the problem in it..
here is the code:
JButton b1 = new JButton("Categories");
b1.setSize(120,25);
b1.setLocation(130,650);
b1.setBackground(Color.LIGHT_GRAY) ;
b1.addActionListener(new AL());
f.add(b1);
public class AL implements ActionListener{
public void actionPerformed(ActionEvent ae){
JFrame f3 = new JFrame("Delicious Bakery");
f3.setVisible(true);
f3.setSize(400,200);
f3.add(Bread);
Now here is the Bread class:
public class Bread extends AL implements ActionListener
{
Bread()
{
ImageIcon BreadImage = new ImageIcon("C:\\Users\\Baba\\Downloads\\Documents\\Bread1.jpg");
JButton Bread = new JButton("Bread",BreadImage);
Bread.setSize(128,96);
}}
You appear to be making several basic mistakes including what looks to be trying to add a class that implements ActionListener to your GUI as if it were a button, but it's not. This suggests that you'd greatly benefit from first reading through the JButton Tutorial and the ActionListener Tutorial.
Note that if this were my project, I'd use AbstractActions a concept that is sort of like an "ActionListener on steroids". You would set your JButton with this Action and in doing so, gain the button its name text, its icon and its ActionListener behavior.
Some notes on your code:
JButton b1 = new JButton("Categories"); // (1)
b1.setSize(120,25); // (2)
b1.setLocation(130,650); // (3)
b1.setBackground(Color.LIGHT_GRAY) ;
b1.addActionListener(new AL()); // (4)
f.add(b1); // (5)
You here create a JButton with a String name text
Here you try to set absolute size, something that we don't recommend as doing this, and using null layouts leads to rigid hard to improve and debug programs
Same for setting locations
You appear to be adding an ActionListener OK
And then add your button to a container (the JFrame perhaps)?
public class AL implements ActionListener{ // (6)
public void actionPerformed(ActionEvent ae){
JFrame f3 = new JFrame("Delicious Bakery"); // (7)
f3.setVisible(true); // (8)
f3.setSize(400,200); // (9)
f3.add(Bread); // (10)
OK, so the AL class implements ActionListener and has an actionPerformed method, so far so good
OK, so inside it you create a new JFrame, a bit unusual since most GUI's have only one JFrame
You're calling setVisible(true) on the JFrame before adding components -- not code, since this may sometimes not allow the JFrame to fully render components added after this setVisible was called
Same notes as previous about setting sizes
Now you add Bread to the JFrame. But Bread looks to not be a component but rather only an ActionListener. This line should not compile.
public class Bread extends AL implements ActionListener { // (11)
Bread() {
// (12)
ImageIcon BreadImage = new ImageIcon("C:\\Users\\Baba\\Downloads\\Documents\\Bread1.jpg");
JButton Bread = new JButton("Bread",BreadImage); // (13)
Bread.setSize(128,96); // (14)
}
}
Your Bread class extends AL but also implements ActionListener which is redundant since AL already implements ActionListener.
You are creating an image icon using a file and an absolute path, a slight no-no. Better to use resources and absolute paths
OK you create a new JButton, but you give it the same name as the non-JButton class -- very confusing. Simply don't do this. Ever. Name your fields with unique names, and obey Java naming issues.
Same issue regarding setting sizes.
For example:
import java.awt.Dimension;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class TestBread extends JPanel {
// String to url for bread image
private static final String BREAD_IMG_PATH = "http://findicons.com/files/icons/339/"
+ "coffee_break/128/sliced_bread.png";
// preferred size of jpanel
private static final int PREF_W = 400;
private static final int PREF_H = 300;
private BreadAction breadAction; // our abstract action
private JButton breadButton; // our jbutton
public TestBread() {
Icon breadIcon = null;
try {
// get image and put into Icon
URL breadUrl = new URL(BREAD_IMG_PATH);
BufferedImage breadImg = ImageIO.read(breadUrl);
breadIcon = new ImageIcon(breadImg);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
breadAction = new BreadAction("Bread", KeyEvent.VK_B, breadIcon); // create Action
breadButton = new JButton(breadAction); // create button with Action
breadButton.setVerticalTextPosition(AbstractButton.BOTTOM); // position text
breadButton.setHorizontalTextPosition(SwingConstants.CENTER);
add(breadButton);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private static void createAndShowGui() {
TestBread mainPanel = new TestBread();
JFrame frame = new JFrame("TestBread");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
Our AbstractAction or "super" ActionListener
#SuppressWarnings("serial")
class BreadAction extends AbstractAction {
public BreadAction(String name, int mnemonic, Icon icon) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
putValue(LARGE_ICON_KEY, icon);
}
#Override
public void actionPerformed(ActionEvent e) {
System.out.println("bread pressed");
}
}

How to access an object within another class (Java)

Here's an abrevated version of what my code looks like:
public class ColorFactory extends JFrame {
public ColorFactory(){
buildTopPanel();
}
public void buildTopPanel(){
JPanel topPanel = new JPanel();
this.add(topPanel, BorderLayout.NORTH);
}
}
As you can see I have a method that makes a new JPanel object when called. How can I access that particular JPanel object from another class? I have a button listener class that I want to change the color of the JPanel from outside the ColorFactory class. This code is right after the ColorFactory class.
public class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
//Change JPanel color here.
}
}
Would it be better just to instantiate JPanel in the ColorFactory constructor and then just access it through there?
For starters, you need to make the JPanel a field in ColorFactory, so references to it don't disappear when you exit buildTopPanel(). Once you've saved a reference to it, then you have a couple of choices. From the design standpoint, the bad choice is to expose it, e.g.:
JPanel getTopPanel(){
return topPanel;
}
The better choice is to have your action listener send a message to ColorFactory that says "respondToButton(Color newColor)", and have ColorFactory decide to change topPanel's color... e.g.:
public void respondToButton(Color newColor){
topPanel.setBackground(newColor);
}
You are facing a design issue; in general, this type of situations require more investigation to understand how to end up with a clean and maintainable design.
However, For the specific problem you are reporting, I would:
Create a constructor of ButtonListener that receives a parameter (i.e. the ColorFactory) which could access the information you need, so that you can initialize a field in ButtonListener itself
Create a method changeColor in the ColorFactory. This method actually applies the color change
In the ButtonListener, invoke changeColor on the field, i.e. the reference to the ColorFactory
You should make the JPanel a field of the class like this:
public class ColorFactory extends JFrame {
JPanel topPanel;
public ColorFactory(){
buildTopPanel();
}
public void buildTopPanel(){
topPanel = new JPanel();
this.add(topPanel, BorderLayout.NORTH);
}
public void changeColor(Color color) {
//color changing code here
}
}
now You can get the JPanel from another class.
All you have to do now, is get the ColorFactory into your Button listener:
public class ButtonListener implements ActionListener{
ColorFactory colorFactory;
public ButtonListener(ColorFactory colorFactory) {
this.colorFactory = colorFactory;
}
public void actionPerformed(ActionEvent e) {
colorFactory.changeColor(/* color here */);
}
}

Implementing and calling JFrame/ActionListener class from main

I'm trying to get my program to launch a gui that gathers information before the actual programs starts. In main I try to call the JFrame which should then run until the start button is pressed and then the main program should launch. Everything seems to be correct except for the base class of the initializeLauncher. Thanks!
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
class InitializeLauncher implements ActionListener {
InitializeLauncher() {
JFrame frame = new JFrame("launcherClient");
Container c = frame.getContentPane();
Dimension d = new Dimension(700,400);
c.setPreferredSize(d);
JButton startButton = new JButton("Start");
JPanel pane = new JPanel();
startButton.addActionListener(this);
pane.add(startButton);
frame.add(pane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setVisible(true);
}
public void buttonClicked(ActionEvent e)
{
ApplicationDeploy displayExample = new ApplicationDeploy();
displayExample.initializeGameClient();
}
}
...and then in main I call this:
InitializeLauncher launcher = new InitializeLauncher();
launcher.InitializeLauncher();
By making your class abstract, you're fixing the wrong thing. Instead you should give your class the missing method, public void actionPerformed(ActionEvent e) {...}
The basic rule here is, if you state that your class is going to implement an interface, here the ActionListener interface, then the class must implement all of the methods of the interface.
#Override
public void actionPerformed(ActionEvent e) {
// ... your code that should occur when the button is pressed goes here
}
Note that your buttonClicked(...) method will do nothing useful for you. Likely you'll want to get rid of that method and put its code into the actionPerformed method.
As an aside, I often use a JOptionPane for the functionality that you're using a JFrame for.

JButton disappears when resize

anyone know or have an idea as to why my button disappears after i resize the applet?
this is my code:
import java.awt.event.*;
import javax.swing.*;
import acm.program.*;
public class button extends ConsoleProgram {
public void init(){
hiButton = new JButton("hi");
add(hiButton, SOUTH);
addActionListeners();
}
public void actionPerformed(ActionEvent e){
if(hiButton == e.getSource()){
println("hello") ;
}
}
private JButton hiButton;
}
I'm not sure if it is a good Idea to redefine the init-method. When I have a look at http://jtf.acm.org/javadoc/student/acm/program/ConsoleProgram.html I would expect that you have implement only the run-method. Overriding init without calling super.init() Looks strange to me.
Maybe I would be better to derive from JApplet directly for your first steps in Applet programming.
Assuming that
your ConsoleProgram extends (directly or indirectly) JApplet
You declared SOUTH as a static final variable that has the value BorderLayout.SOUTH (otherwise your code doesn't compile)
The code should work, no need to repaint (unless you would like to do some application-specific optimization). I just copied and pasted your code (by expliciting the two assumptions above), I see the applet and the button doesn't disappear on resize.
Anyway there are few "not good" things in the code:
First of all, a naming convention issue: the class name should be "Button" with the first letter capitalized (on top of that, it's a poor name for an Applet)
Second, action listeners should be attached before adding the component;
Third, as Oracle doc suggests here, the code that builds the GUI should be a job that runs on the event dispatcher thread. You can do that by wrapping the build gui code in a Runnable using a SwingUtilities.invokeAndWait(Runnable()
Have you tried calling super.init() at the start of your init() method?
Try explicitly using a layout for your Console and then use relative positioning.
To re-size a button in Applet:
public class Button extends JApplet implements ActionListener {
private JButton button;
public void init() {
Container container = getContentPane();
container.setLayout(null);
container.setBackground(Color.white);
button = new JButton("Press Me");
button.setSize(getWidth()/2,20);
button.setLocation(getWidth()/2-button.getSize().width/2, getHeight()/2-button.getSize().height/2);
container.add(button);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
int width = (button.getSize().width == getWidth()/2) ? getWidth()/4 : getWidth()/2;
int height = button.getSize().height;
button.setSize(width,height);
button.setLocation(getWidth()/2-width/2, getHeight()/2-height/2);
}
}
To re-size a button in JFrame:
public class Button extends JFrame implements ActionListener {
private JButton button;
public Button(String title) {
Container container = getContentPane();
container.setLayout(null);
container.setBackground(Color.white);
setTitle(title);
setSize(400,400);
button = new JButton("Press Me");
button.setSize(getWidth()/2,20);
button.setLocation(getWidth()/2-button.getSize().width/2,
getHeight()/2-button.getSize().height/2);
container.add(button);
button.addActionListener(this);
}
public void actionPerformed(ActionEvent e) {
int width = (button.getSize().width == getWidth()/2) ? getWidth()/4 : getWidth()/2;
int height = button.getSize().height;
button.setSize(width,height);
button.setLocation(getWidth()/2-width/2, getHeight()/2-height/2);
}
public static void main(String[] args) {
Button button = new Button("Test");
button.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
button.setVisible(true);
}
}
Have you declared the repaint method...???
You are using swing. It needs to have declared a repaint.
Please define a custom repaint mwthod

Swing - calling events from inside panel

I have simple Swing GUI with main window JFrame and its main panel derive from JPanel. The panel has some buttons that can be clicked and generate events.
I want these events affect data stored in JFrame because it is my main application - it has some queues for thread, open streams and so on.
So how do I make my button in panel invoke callbacks in its parent frame? What is best practice of this for Java/Swing?
To invoke methods in the parent frame you need a reference to the parent frame. So your JPanel's constructor can be declared like this:
public MyPanel(MyFrame frame){
super();
this.frame = frame;
//the rest of your code
}
And in the JFrame you invoke this constructor like this:
panel = new MyPanel(this);//this refers to your JFrame
In the event handlers attached to your buttons you now have access to the frame and can invoke the various methods as needed.
button1.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
//do some stuff
frame.someMethod();//invoke method on frame
//do more stuff
}
});
Have a look on this tutorial for using SwingWorker.
Use addActionListener method on desired buttons specifying the class implementing ActionListener.
ActionListenerClass actionListenerObject = new actionListenerClass();
JButton b = new JButton("Button");
b.addActionListener(actionListenerObject);
public class ActionListenerClass implements ActionListener(){
//or better : actionListenerClass extends AbstractAction
public void actionPerformed(ActionEvent e) {
}
}
EDIT:
Yes, I know this. But the action
listener I want to be in parent JFrame
class - this is the problem
then extends JFrame class making the new derived class implementing the desired interface.
You can implement the ActionListener in your class that has the JFrame (or extends it):
class MyPanelClass {
public MyPanelClass(ActionListener al)
{
//...
JButton myButton = new JButton("Button");
myButton.addActionListener(al);
//...
}
}
class MainClass extends JFrame implements ActionListener {
public void someMethod() {
MyPanelClass mpc = new MyPanelClass(this);
}
#Override
public void ActionPerformed(ActionEvent ev) {
// your implementation
}
}

Categories