Extending a java class whilst also altering it - java

I've been a lurker on stackoverflow for some time. I'm teaching myself Java, so bear with me if this is a rather elementary question (I couldn't find the answer on here though).
If I have a java class (like the one below), which I wish to use in future classes in a slightly different way (e.g. changing the button text/or output), is there a way to do this by extending the original class?
In the case below I have a JFrame with two buttons which print different text to the console. I simply want to extend this class whilst changing one of the button names.
ORIGINAL CLASS:
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class FrameIt extends JFrame implements ActionListener {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new FrameIt().setVisible(true);
}
public FrameIt() {
super("Make a choice");
setSize(600, 600);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new GridLayout());
JButton button = new JButton("Click Me.");
JButton button2 = new JButton("No, you should Click Me!");
button.addActionListener(this);
button2.addActionListener(this);
add(button);
add(button2);
}
#Override
public void actionPerformed(ActionEvent e) {
String name = e.getActionCommand();
if(name.equals("Click Me")){
System.out.println("That was actually the right choice.");
}else{
System.out.println("Poor choice.");
}
}
}
CLASS THAT EXTENDS:
import javax.swing.JButton;
public class Alterations extends FrameIt{
private static final long serialVersionUID = 1L;
public static void main(String args[]){
new Alterations().setVisible(true);
System.out.println("Doing it");
}
public Alterations(){
JButton button2 = new JButton("Slightly different button");
}
}
Thanks.

Creating the JButton instance in the sub-class Alterations will make no difference, since you don't connect it to anything.
One way to handle it is to change the constructor of the original class. You can make the name of the button originate from a method call :
Change :
JButton button2 = new JButton("No, you should Click Me!");
To :
JButton button2 = new JButton(getSecondButtonName ());
Then getSecondButtonName() can return "No, you should Click Me!" in the original class and you can override it in the sub-class to return "Slightly different button".
An alternative would be to remove the original button in the constructor of the sub-class and add the new button.
These solutions make sense only if there's a real reason to create a sub-class in the first place. If the only difference between the base class and the sub-class is the name of one button, there's no reason to create a sub-class.

Related

Why can´t I click on the JButtons?

Hey I am a beginner and I have wrote the following code in java, but I can´t click on the JButtons. The program includes three clases - Main, Frame and Actionhandler. My goal was to create a Frame with two buttons: Singleplayer and Mulitplayer. I wanted to test if they work, but I can´t click them. Can anyone help me please?
This is the Main class:
public class Main {
public static void main (String [] args) {
new Frame ();
}
}
This is the Frame class:
import java.awt.Color;
import java.awt.Font;
import java.awt.GridLayout;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
public class Frame extends JFrame {
public static Object multi;
public static Object single;
Frame() {
// Frame
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
this.setLocationRelativeTo(null);
//Layout in Frame
this.setLayout(new GridLayout(2,1));
this.setVisible(true);
// Buttons in Main Menu
JButton single = new JButton("Singleplayer");
JButton multi = new JButton("Multiplayer");
// specify single button
single.setBounds(200,100,250,80);
single.setForeground(Color.GREEN);
single.setBackground(Color.LIGHT_GRAY);
single.setOpaque(true);
single.setBorder(BorderFactory.createLineBorder(Color.BLACK));
single.setFont(new Font("Comic Sans",Font.BOLD,25));
single.addActionListener(new ActionHandler());
//specify multi button
multi.setBounds(800,100,250,80);
multi.setForeground(Color.GREEN);
multi.setBackground(Color.GRAY);
multi.setOpaque(true);
multi.setFont(new Font("Comic Sans",Font.BOLD,25));
multi.setBorder(BorderFactory.createLineBorder(Color.BLACK));
multi.addActionListener(new ActionHandler());
// add Buttons to Frame
this.add(single);
this.add(multi);
}
}
This is the ActionHandler class:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ActionHandler implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource() == Frame.multi) {
System.out.println("You have clicked on Singleplayer");
if(e.getSource() == Frame.single) {
System.out.println("You have clicked on Multiplayer");
}
}};
}
You can click on the buttons fine. They just won't do anything because of how you've wired the program:
public class Frame extends JFrame {
public static Object multi; // this is null
public static Object single; // and so is this
Frame() {
// Frame
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
this.setLocationRelativeTo(null);
//Layout in Frame
this.setLayout(new GridLayout(2,1));
this.setVisible(true);
// Buttons in Main Menu
JButton single = new JButton("Singleplayer"); // this is a new *local* variable
JButton multi = new JButton("Multiplayer"); // and so is this:
You are initializing local variables that have the same name as your static class fields, and you're leaving the same static class fields null, a situation known as "variable shadowing", and so in your listeners, you check if the source is the null static field. Which won't work.
So in your listener:
public void actionPerformed(ActionEvent e) {
if(e.getSource() == Frame.multi) {
You're testing if a null variable is the button that was pressed, and this will not work.
One simple solution is to not re-declare the multi and single variables, to assign your JButtons to these public static fields by changing this:
JButton single = new JButton("Singleplayer");
JButton multi = new JButton("Multiplayer");
to this:
single = new JButton("Singleplayer");
multi = new JButton("Multiplayer");
This would sort-of work. You'd have do do some casting to add these JButton objects to the container since the variables are Object, not JButton. But this would be a bad idea because you'd be throwing out the OOPs baby with the bathwater, discarding encapsulation completely.
Best not to throw out OOPs rules with public static (non-constant) fields and instead work with them. Better to use constant Strings to be passed into your JButtons and then test for them using the ActionEvent's actionCommand property:
public class Frame extends JFrame {
public static String SINGLE_PLAYER = "Single Player";
public static String MULTI_PLAYER = "Multi Player";
Frame() {
// Frame
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setExtendedState(JFrame.MAXIMIZED_BOTH);
this.setLocationRelativeTo(null);
//Layout in Frame
this.setLayout(new GridLayout(2,1));
this.setVisible(true);
// Buttons in Main Menu
JButton single = new JButton(SINGLE_PLAYER); // this is a new *local* variable
JButton multi = new JButton(MULTI_PLAYER); // and so is this:
in the listener:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class ActionHandler implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
if(e.getActionCommand().equals(Frame.MULTI_PLAYER)) {
System.out.println("You have clicked on Multi Player");
} else {
// ...
}
}};
}
Other problems with your code include:
Don't name your class Frame since this clashes with the name of class in the core Java library, java.awt.Frame. Name it something unique to avoid confusion
Avoid setting bounds, sizes and such. Let the GUI, its layout managers and component preferred sizes do the sizing by calling pack() on the top-level window (JFrame, JDialog,...) after adding components
Call .setVisible(true) on the top-level window after adding all components.
This looks like it will display as a sub-window or dialog window, and you might want to show this portion of the GUI in a modal JDialog, not in a JFrame.

Access difficulty with ArrayList of panels with buttons

my question is: how do I get the object of my CustomPanel, so that I am able to access its fields (because in my real programm I have some more fields in there) and also am able to delete it from my ArrayList?
I don't know how I have to implement an ActionListener in the Class Window, to somehow get the Object in my Arraylist, which containes the button that got pressed.
Also I am wondering if I am somehow able to implement an ActionListener in the Class CustomPanel which can influence the behaviour of the Object which is an instance of my Class Window.
I have kind of the following code:
public class Window extends JFrame{
ArrayList<CustomPanel> aLCustomPanel = new ArrayList();
JPanel jp = new JPanel();
public Window() {
for(int i=0;i<5;i++){
aLCustomPanel.add(new CustomPanel());
//here I could put the code from the 1 edit - see below
jp.add(aLCustomPanel.get(i));
}
this.add(jp);
}
public static void main(String args[]){
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new Window().setVisible(true);
}
});
}
}
class CustomPanel extends JPanel {
private JButton button;
public CustomPanel(){
button = new JButton("button");
this.add(button);
}
public JButton getButton(){
return this.button;
}
}
my Code is much longer and weirder, so I tried to extract the (for this question) importing things.
Thanks for any help in advance!
edit:
for example: I would like to delete the object from the ArrayList, of which the button got pressed.
//imagine this comment in above code
aLCustomPanel.get(aLCustomPanel.size()-1).getButton().addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
button_IwantToDeleteYou(e); //here I want to remove the panel, containing the button that got pressed from the above ArrayList, which is located in Class Window
}
});
edit2:
added a missing bracket and fixed some mistakes, code should be ok now.
Your code contained a few "gaps", i.e. missing code, which I filled in, as follows:
Added calls to [JFrame] methods setDefaultCloseOperation() and pack() and setLocationByPlatform(). I suggest you refer to the javadoc for those methods in order to understand what they do.
I set a layout manager for jp class member variable in your Window class.
Yes, you need to register an ActionListener with the JButton in class CustomPanel and that listener should reside in your Window class - the one that extends JFrame.
Here is my rewrite of your code. Note that I changed the name of class Window to CusPanel so as to distinguish between your class and java.awt.Window class. Not that it makes a difference, I just prefer not to use names of classes from the JDK.
import java.awt.Container;
import java.awt.EventQueue;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class CusPanel extends JFrame implements ActionListener {
private static final int COUNT = 5;
private ArrayList<CustomPanel> aLCustomPanel = new ArrayList<>();
private JPanel jp = new JPanel(new GridLayout(0, COUNT));
public CusPanel() {
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
for (int i = 0; i < COUNT; i++) {
aLCustomPanel.add(new CustomPanel(this));
// here I could put the code from the 1 edit - see below
jp.add(aLCustomPanel.get(i));
}
this.add(jp);
pack();
setLocationByPlatform(true);
}
public void actionPerformed(ActionEvent actionEvent) {
Object source = actionEvent.getSource();
if (source instanceof JButton) {
JButton button = (JButton) source;
Container parent = button.getParent();
jp.remove(parent);
jp.invalidate();
jp.repaint();
pack();
// aLCustomPanel.remove(parent); <- optional
}
}
public static void main(String args[]) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new CusPanel().setVisible(true);
}
});
}
}
class CustomPanel extends JPanel {
private JButton button;
public CustomPanel(ActionListener parent) {
button = new JButton("button");
button.addActionListener(parent);
this.add(button);
}
public JButton getButton() {
return this.button;
}
}
Note that after removing a CustomPanel, the GUI components need to be laid out again and the JFrame should also be resized accordingly. Hence in the actionPerformed() method, I call invalidate(), then repaint() and then pack(). I also think that if you remove a CustomPanel from the GUI, you should also remove it from the ArrayList, but hey, I still don't understand why you want to do this although I obviously don't know the whole story behind you wanting to do this in the first place.
Of-course, since each button (and each CustomPanel) looks exactly the same, you can't really know which button was removed. Again, I assume you see the big picture whereas I don't.

Java/Swing JButton does not display its text and does not perform its action

I want to write a simple street crossing traffic light system. I want to make a button which will start the whole program (open up the GUI of the traffic light system). But already my first button starts to make problems. It doesnt display its text and the action it should perform won't happen. I am really a beginner so its probably some dumb and obvious fault but please have a look I would be really happy ^^
package kreuzung;
import java.awt.BorderLayout;
import java.awt.Container;
import java.awt.FlowLayout;
import javax.swing.JFrame;
public class HomeFrame extends JFrame{
public HomeFrame(String title) {
super(title);
this.setLayout(new FlowLayout(FlowLayout.CENTER));
Button test = new Button("noAction");
Container cont = getContentPane();
cont.add(test, BorderLayout.CENTER);
}
}
And this would be the generated button which doesn't do the things its supposed to do
package kreuzung;
import javax.swing.Action;
import javax.swing.JButton;
public class Button extends JButton{
private String actionName;
public Button(String actionName) {
this.actionName = actionName; //set the Action name of this button
JButton button = new JButton(); //instantiate this Button
button.setText(actionName); //set the Action Name as Button Text
button.setSize(30, 30);
button.setBounds(5, 5, 25, 25);
button.addActionListener(new Evt(this.actionName)); //add an Action Listener to the button
//and gets the Action from the Evt Class
}
}
And last but not least here's the Evt class which should take care of the action performing
package kreuzung;
import java.awt.event.*;
import javax.swing.JFrame;
public class Evt implements ActionListener {
private String actionName;
public Evt(String actName) {
this.actionName = actName;
}
#Override
public void actionPerformed(ActionEvent e) {
switch(actionName) {
case "noAction":
JFrame frame = new HomeFrame("Home");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(300, 400);
frame.setVisible(true);
break;
}
}
}
There are several errors in your code:
You shouldn't extend JFrame, see Extends JFrame vs. creating it inside the program
Don't call setBounds(...) the Layout Managers will take care of positioning your components
Don't leave too much extra space between lines or after / before opening / closing braces {} it becomes hard to read
Don' call Button as a class name, it could be confused with java.awt.Button class.
It doesnt display its text and the action it should perform won't happen
In this class:
public class Button extends JButton {
private String actionName;
public Button(String actionName) {
this.actionName = actionName;
JButton button = new JButton();
button.setText(actionName);
button.setSize(30, 30);
button.setBounds(5, 5, 25, 25);
button.addActionListener(new Evt(this.actionName));
}
}
You extend from JButton and then you create a JButton inside of it! so, you have 2 JButtons, the one from the class (inherited) and the one you create inside it. But you're setting the text to the one created inside but you're adding the other one (without text) to your JFrame.
In a metaphor, it's like:
You write in a page something
You get a new white page and add it to your book, instead of adding the one you wrote in to your book.
There's no need to extend JButton in your current program, so just create a new JButton instance and that's it.
Otherwise if you really want to use a custom JButton class do this:
public class MyCustomButton extends JButton { // Change class name
private String actionName;
public MyCustomButton(String actionName) {
super(actionName); //Sets the text
this.actionName = actionName;
button.addActionListener(new Evt(this.actionName));
}
}
You do not really need to create a JButton's children class since you do not add any particular attribute to it.
Instead you should be able to make it works this way:
public class HomeFrame extends JFrame{
private static final String BUTTON_ACTION_NAME = "myActionName";
public HomeFrame(String title) {
super(title);
this.setLayout(new FlowLayout(FlowLayout.CENTER));
JButton test = new JButton();
test.setText(BUTTON_ACTION_NAME);
test.setSize(30, 30);
test.setBounds(5, 5, 25, 25);
test.addActionListener(new Evt(BUTTON_ACTION_NAME));
Container cont = getContentPane();
cont.add(test, BorderLayout.CENTER);
}
}

Add actionperformed to a button through another class

I'm trying to learn methods and classes
I created a small tool where class Main is to run it, class SWing to create the frame, class Verb to create a button to add to Swing and as well to do the action performed, my question here is that, how can I make the action performed really works via verb Class.
let's say I want to get text from textfield and to concatenate it with textfiled1 and to show the answer on textfield2.
here is my code
1- my main class
package Abo;
public class Main {
public static void main (String[]args){
Swing runFrame = new Swing(); // creating a variable for class Swing
runFrame.Run(); // to run the Swing
}
}
2- my Swing
package Abo;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JTextField;
public class Swing extends JFrame {
public JTextField textField;
public JTextField textField_1;
public JTextField textField_2;
public Swing() {
// creating the frame
getContentPane().setLayout(new GridLayout(2,2,5,5));
textField = new JTextField();
getContentPane().add(textField);
textField.setColumns(10);
textField_1 = new JTextField();
getContentPane().add(textField_1);
textField_1.setColumns(10);
textField_2 = new JTextField();
getContentPane().add(textField_2);
textField_2.setColumns(10);
textField_2.setEditable(false);
// adding the btn from another class
Verb addBTN = new Verb();
getContentPane().add(addBTN.BTN());
}
public void Run(){
// setting the frame
Swing frame = new Swing();
frame.setVisible(true);
frame.setSize(300,400);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
}
}
3- verb class
package Abo;
import javax.swing.JButton;
public class Verb extends JButton {
// creating the btn constructor
public JButton BTN(){
JButton btn1 = new JButton("First Button");
return btn1;
}
}
thank you in advance
Well in your verb class just add this to btn1
btn1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e)
{
//Execute when button is pressed
}
});
I noticed that Verb is extending JButton, but you are opting to write a method that returns a JButton inside of it.
If you want Verb to be a JButton that is always called "First Button", then I would instead modify Verb's constructor.
One way to do that would be like this:
package Abo;
import javax.swing.JButton;
public class Verb extends JButton {
public Verb() {
super("First Button"); //super calls the constructor of the parent of this class, in this case that is JButton
}
}
Now since you are extending JButton, whenever you make a Verb, it will also be a JButton with the text "First Button"
Now you can add your ActionListener to Verb directly.
By doing:
Verb addBTN = new Verb();
addBTN.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
textField_2.setText(textField.getText() + textField_1.getText());
}
});
However, in order to access your text fields inside of the actionlistener, you will need to make them final.
final textField = new JTextField(); // do this for all text fields
Also, just as good practice, unless you are planning to add more functionality than the default constructor to Verb, I would just use a JButton instead of making your own class.

Java - Use of non final variables in anonymous inner class?

In a school test I used non final variables inside an anonyme inner class.
On the the school Computer and on my private Computer(using x86 jre1.8.0_45) it is working.
However, on the teachers Laptop Eclipse is showing errors (The variables should use final). He is using jre1.8.0.x version (don't know the exact version).
Any ideas why it is working on my computer and not on his computer?
In this code example the no final object jLabel is used inside the actionPerformed function of the ActionListener:
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JLabel;
public class Main {
public Main(String[] args) {
JLabel jLabel = new JLabel();
JButton button = new JButton();
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
jLabel.setText("xyz");
}
});
}
}
Newer java versions are more tolerant in this concern: they only require that they should be "effectively final".
At the end, the difference is not soo big - you only can use variables which you COULD tag with final; you may not modify them.
If you are at that place, you as well can make them "really" final, and it works everywhere.
BTW, on 1.8 with its new lambda syntax you can write more elegantly
button.addActionListener((ActionEvent arg0) -> jLabel.setText("xyz"));
You could define the Listener not as an anonymous class but as a private inner class in order to avoid that problem:
public class Main {
public Main(String[] args) {
JLabel jLabel = new JLabel();
JButton button = new JButton();
button.addActionListener(new MyActionListener(jLabel));
}
private class MyActionListener implements ActionListener {
private JLabel jLabel;
MyActionListener(JLabel jLabel) {
this.jLabel = jLabel;
}
#Override
public void actionPerformed(ActionEvent arg0) {
jLabel.setText("xyz");
}
}
}
This way you can just pass the label (and button if you need) to the constructor of the listener and will be able to use it without any problems.

Categories

Resources