Java Architecture - Question about ActionListener Conventions - java

I am making a user interface which shows graphs and manipulates graphs. The class extends JFrame implements ActionListener. The ActionListener then calls different classes to manipulate graphs depending on the action. This worked while the class had few ActionListeners; however, now the class is becoming unmanageable.
I know that in the interest of encapsulation, it would be best to have the ActionListener within the user interface class because it needs to access non-static components of the interface. However, it seems like there is a conflict between encapsulation and readability.
What I am proposing is breaking the class into one class for the interface and a second for the ActionListener and accessing the interface components statically. What I want to know is does this follow basic design conventions? And, if this is an acceptable approach would you place the main class in the user-interface class or the ActionListener class?

Not a duplicate question... but my answer should help with your question.
Short summery, my preference would be to have the JFrame class not implement ActionListener and then have a number of named inner classes withing the JFrame that do implement the ActionListener.
I would place the main in a class unto itself... and call it Main.
Here is some sample code for the way I like to do it:
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class Main
{
private Main()
{
}
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
final FooFrame frame;
frame = new FooFrame();
frame.setupGUI();
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
and then the GUI:
import java.awt.FlowLayout;
import java.awt.LayoutManager;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
public class FooFrame
extends JFrame
{
private final JButton incrementBtn;
private final JButton decrementBtn;
private int value;
{
incrementBtn = new JButton("++");
decrementBtn = new JButton("--");
}
private class IncrementListener
implements ActionListener
{
public void actionPerformed(final ActionEvent evt)
{
increment();
}
}
private class DecrementListener
implements ActionListener
{
public void actionPerformed(final ActionEvent evt)
{
decrement();
}
}
public void setupGUI()
{
final LayoutManager layout;
layout = new FlowLayout();
setLayout(layout);
setupListeners();
addComponents();
}
private void setupListeners()
{
incrementBtn.addActionListener(new IncrementListener());
decrementBtn.addActionListener(new DecrementListener());
}
private void addComponents()
{
add(incrementBtn);
add(decrementBtn);
}
private void increment()
{
value++;
System.out.println("value = " + value);
}
private void decrement()
{
value--;
System.out.println("value = " + value);
}
}

Related

Java Swing Component("c") Cannot be Resolved

I'm currently stuck on a project at the moment component("c") cannot be resolved, I know I'm getting this issue as I'm trying to grab "c" from outside the class from the repaint method but have no idea how to get around this. As far as I'm concerned if I am able to grab "c" then my whole program would. Any help will be massively appreciated. thanks!
package adp.cwr2122;
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JButton;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JProgressBar;
import javax.swing.SwingUtilities;
public class SomApplication extends JFrame {
private static final long serialVersionUID = 1L;
private final JProgressBar bar = new JProgressBar();
private final SoM som = new SoM(800,600);
public SomApplication() {
final JPanel mainPanel = new JPanel(new BorderLayout());
final JPanel southPanel = new JPanel(new BorderLayout());
final JButton cancelButton = new JButton("Cancel");
cancelButton.addActionListener(ev -> doCancel());
this.bar.setMaximum(5000);
southPanel.add(this.bar);
southPanel.add(cancelButton, BorderLayout.EAST);
final SomComponent c = new SomComponent(this.som);
mainPanel.add(c);
mainPanel.add(southPanel, BorderLayout.SOUTH);
add(mainPanel);
pack();
setVisible(true);
this.som.initialise();
c.repaint();
final long start = System.currentTimeMillis();
while(SomApplication.this.som.range() > 0) {
SomApplication.this.som.doOne();
SomApplication.this.bar.setValue((int)((5000 * (SomApplication.this.som.maxRange() - SomApplication.this.som.range())) / SomApplication.this.som.maxRange()));
c.repaint();
}
final long elapsed = System.currentTimeMillis() - start;
System.out.println("Done " + elapsed);
}
private void doCancel() {
System.out.println( "Cancel button pressed");
}
private static class SomComponent extends JComponent {
private static final long serialVersionUID = 1L;
private final SoM som;
public SomComponent( final SoM som) {
this.som = som;
new Thread(new Runnable() {
public void run(){
queuePaint();
System.out.println("Thread is working");
}
}).start();
}
public void repaint() {
if(SwingUtilities.isEventDispatchThread()) {
c.repaint();
}
else {
SwingUtilities.invokeLater(c.repaint);
}
}
public void queuePaint() {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
System.out.println("Queue Paint is being called");
setPaint(); }
});
}
public void setPaint() {
System.out.println("Paint is being called");
paintComponent(this.getGraphics());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(this.som.image().getWidth(), this.som.image().getHeight());
}
#Override
public void paintComponent(final Graphics g) {
super.paintComponent(g);
g.drawImage(this.som.image(), 0, 0, this);
repaint();
}
}
public static void launch() {
new SomApplication();
}
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new SomApplication();
}
});
}
}
As you have recognized yourself your problem is access to c in the repaint() method. You only declare c in the scope of the constructor of SomApplication, so everything outside that scope (read: outside the curly braces of the SomApplicaton constructor) has no idea what c is supposed to be, because it is not declared.
One way to resolve this is declaring c as a member of SomApplication. However you will run into an additional problem there. Your nested class is static, so if you declare c as a member of SomApplication, then SomComponent cannot be static in order to "see" c.
Having said all that there is something fishy with your code there. Because c is an instance of SomComponent and presumably you intend to update itself I then don't see why c is in repaint() to begin with. Your repaint() does not override the JComponent#repaint and your c is presumably supposed to be this.
It's possible I misunderstand something about your architecture, but are you sure you need the c at all in there? Why don't you call repaint() rather than c.repaint() in SomComponent?
If you took the code piece in your SomComponent#repaint method from some online example, take a look at whether you need that code at all. JComponent already comes with repaint(), so maybe this is just the result of a bad copy+paste. Not sure.

basic java Main method and update

Im new to pure java after been coding in javabased processing (processing.org) a lot. In processing you have a setup method (the constructor) and a draw method in the main class.
The draw method loops over and over again untill you close the program. In draw i run my methods from other classes.
How do I do this in java? For example in this code below for getting the counter to count?
public class Testing3 {
public static void main(String[] args) {
MyClass c = new MyClass();
System.out.println("c = " + c.getCount());
}
}
And the class:
public class MyClass {
private int value;
public MyClass() {
}
public void setCount(int startV) {
value = startV;
}
public int getCount() {
value++;
int counter = value % 10;
return counter;
}
}
I know I of course can put the printmessage in a while loop and loop it that way but I guess there are other ways?
I have also been using swing a bit through the GUI builder in netbeans. There I cannot reach my objects within the main method, getting the "non static variable cannot be referenced from a static content". only through functions like the one below. But what if I want to get my counter without pushing on a button i created in the builder etc?
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {}
I know its a newby question but Im a bit confused.
I didn't exactly understand your question (assuming there's only one question) , but I think this is what you're looking for
import java.awt.FlowLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
class Listener implements ActionListener{
int n;
JLabel label;
public Listener(int n,JLabel label){
this.n=n;
this.label=label;
}
#Override
public void actionPerformed(ActionEvent arg0) {
n++;
label.setText(""+n);
}
}
public class SwingTest {
public static void main(String[] args) {
int n=0;
JFrame frame= new JFrame("My First JAVA SWING App");
frame.setLayout(new FlowLayout());
JLabel label=new JLabel("value here");
JButton button=new JButton("click!!!");
button.addActionListener(new Listener(n,label));
frame.add(button);
frame.add(label);
frame.setSize(200, 100);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
this should be on a file called SwingTest.java

How to add a listener to a button from a ActionListener inner class?

I'm trying to do a simple calculator program by building my swing interface on netbeans.
I want to have 3 Classes:
GUI Class - which holds the codes for building the interface
Listener Class - holds all the listener in the GUI interface
Boot Class - this will start the application
For simplicity, I will post my code for a single button. My goal here is to change the Buttons visible text from "1" to "11" to test my design. After verifying that my design works I will continue on working on other buttons.
calculatorGUI.class
import javax.swing.JButton;
public class calculatorGUI extends javax.swing.JFrame {
public calculatorGUI() {
initComponents();
}
private void initComponents() {
oneBtn = new javax.swing.JButton();
oneBtn.setText("1");
}
private javax.swing.JButton oneBtn;
public JButton getOneBtn() {
return oneBtn;
}
public void setOneBtn(String name) {
oneBtn.setText(name);
}
}
Listener.class
import java.awt.event.ActionListener;
import java.awt.event.ActionEvent;
public class Listener {
class oneBtnListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ev) {
calculatorGUI g = new calculatorGUI();
g.setOneBtn("11");
}
}
}
Boot.class
public class Boot {
public static void main(String[] args) {
calculatorGUI gui = new calculatorGUI();
Listener listen = new Listener();
Listener.oneBtnListener oneListen = listen.new oneBtnListener();
gui.getOneBtn().addActionListener(oneListen);
gui.setVisible(true);
}
}
The problem is, nothing happens when I click the button. It seems that the actionListener is not being registered to the button. Can I ask for your help guys on which angle I missed?
The issue I am seeing is how you are initializing calculatorGUI twice, once with the default value and another with the changed value. Take out the initialization of calculatorGUI within your Listener class and pass it from your Boot class and it should work fine.
Although if I were you, I would add the GUI implementations within the GUI class, having it within the listener class that is using within the main function is not something I have seen before and would probably not advise.
Modify your code accordingly,
class Listener {
class oneBtnListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent ev) {
if(ev.getActionCommand() == "1")
{
JButton btn = (JButton)ev.getSource();
btn.setText("11");
}
}
}
}
class calculatorGUI extends javax.swing.JFrame {
public calculatorGUI() {
initComponents();
}
private void initComponents() {
oneBtn = new javax.swing.JButton();
oneBtnListener btnListener = new Listener().new oneBtnListener();
oneBtn.setText("1");
oneBtn.setBounds(100,100,100,25);
oneBtn.addActionListener(btnListener);
add(oneBtn);
setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400,400);
}
private javax.swing.JButton oneBtn;
public JButton getOneBtn() {
return oneBtn;
}
public void setOneBtn(String name) {
oneBtn.setText(name);
}
}
You can change now other part according to your requirement, I just
gave you "1" -> "11", but you can do more.
Best of Luck.

Java - Reusable button action handler concepts question

First off - sorry for the wall of code but it's not too horrendous, just a framework for what I'm trying to explain. It runs without errors.
The goal
I'm making a reuseable button class for my GUI and each button object needs to have a different handler when it's clicked. I want to to assign a ClickHandler object to each new button. Then, the button would call init() on the handler, and be on its way. Unfortunately, there's a typing problem, since each handler class would have a different name.
Current progress
Right now, the handler is typed as HandlerA, but I'd like to have it handle any name, like "SettingsHandler" or "GoToTheWahWah" etc.
I've tried messing about with generic types, but since I'm new to this, and from a webdev background, there seems to be a conceptual hurdle I keep knocking over. Is this the right way to approach the problem?
The code
ReuseableButton.java is a reuseable class, the only thing that changes is the action when it's clicked:
package gui;
import java.awt.event.*;
import java.awt.*;
import javax.swing.*;
public class ReuseableButton extends JButton implements ActionListener {
private static final long serialVersionUID = 1L;
// I want a generic type here, not just HandlerA!
private HandlerA ClickHandler;
// Assemble generic button
public ReuseableButton(Container c, String s) {
super(s);
addActionListener(this);
c.add(this);
}
// Once again, generic type, not just HandlerA!
public void SetClickHandler(HandlerA ch) {
ClickHandler = ch;
}
// Call init() from whatever class has been defined as click handler.
public void actionPerformed(ActionEvent e) {
ClickHandler.init();
}
}
Controller.java fires the frame and assembles buttons as needed (right now, only one button).
package gui;
import javax.swing.*;
import java.awt.*;
public class Controller extends JFrame {
private static final long serialVersionUID = 1L;
public Controller() {
JFrame frame = new JFrame("Handler Test GUI");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container pane = frame.getContentPane();
pane.setLayout(new FlowLayout());
ReuseableButton b = new ReuseableButton(pane,"Reuseable Button A");
// THE QUESTION IS HERE: Pass a generic object?
b.SetClickHandler(new HandlerA());
frame.pack();
frame.setSize(200,200);
frame.setVisible(true);
}
public static void main(String args[]) {
new Controller();
}
}
HandlerA.java is a sample of a random handler for the button click. Later, there could be HandlerB, HandlerC, etc.
package gui;
// A random handler
public class HandlerA {
public void init() {
System.out.println("Button clicked.");
}
}
Thanks very much in advance!
All of you handlers should implement an interface like Clickable or something. That way the interface specifies the existence of the init function:
public interface Clickable
{
public void init();
}
Making the HandlerA definition:
public class HandlerA implements Clickable {
public void init() {
System.out.println("Button clicked.");
}
}
I recommend to work with inheritence in this case:
public abstract class AbstractHandler {
public abstract void init();
}
Then:
public class ConcreteHandlerA extends AbstractHandler {
#Override
public void init() {
// do stuff...
}
}
Controller
public class ReuseableButton extends JButton implements ActionListener {
// I want a generic type here, not just HandlerA!
private AbstractHandler ClickHandler;
public Controller() {
//...
ReuseableButton b = new ReuseableButton(pane,"Reuseable Button A");
AbstractHandler handlerA = new ConcreteHandlerA();
b.SetClickHandler(handlerA);
// ...
}
}
Not sure if this is what you're looking for...
BTW: You can define the AbstractHandler as an interface as well, but you may want to implement some common logic here as well - shared across handlers.
You should use an interface for the handler.
public interface ClickHandler() {
void init();
}
ReuseableButton b = new ReuseableButton(pane,"Reuseable Button A");
b.SetClickHandler(object which implements the ClickHandler interface);
This is the same concept as the normal JButton. There you have the ActionListener interface and the actionPerformed method in it.
P.S. If I don't understand your question, please correct me.

Nested class vs implements ActionListener

Are there any benefits or drawbacks to creating a nested class that implements ActionListener:
public class Foo{
Foo(){
something.addActionListener(new ButtonListener());
}
//...
private class ButtonListener implements ActionListener{
public void actionPerformed(ActionEvent e){
//...
}
}
}
versus implementing ActionListener in the main class itself:
public class Foo implements ActionListener{
Foo(){
something.addActionListener(this);
}
//...
public void actionPerformed(ActionEvent e){
//...
}
}
I've seen both examples quite often, and just want to know if there's a 'best practice.'
#Ankur, you can still use anonymous inner classes as your listeners and have a separate free-standing control class and thus have code that's quite maintainable, a technique I like to use a bit. For example:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class AnonymousInnerEg {
private static void createAndShowUI() {
GuiPanel guiPanel = new GuiPanel();
GuiControl guiControl = new GuiControl();
guiPanel.setGuiControl(guiControl);
JFrame frame = new JFrame("AnonymousInnerEg");
frame.getContentPane().add(guiPanel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
class GuiPanel extends JPanel {
private GuiControl control;
public GuiPanel() {
JButton startButton = new JButton("Start");
startButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (control != null) {
control.startButtonActionPerformed(e);
}
}
});
JButton endButton = new JButton("End");
endButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
if (control != null) {
control.endButtonActionPerformed(e);
}
}
});
add(startButton);
add(endButton);
}
public void setGuiControl(GuiControl control) {
this.control = control;
}
}
class GuiControl {
public void startButtonActionPerformed(ActionEvent ae) {
System.out.println("start button pushed");
}
public void endButtonActionPerformed(ActionEvent ae) {
System.out.println("end button pushed");
}
}
I think first approach is better, as your class will have a separate code for handling action. And usually also composition is better than inheritance so a class should extend a class or implement a interface only if it truly is-a super type.
Also for maintainability, let us say Foo class has a new requirement to listen for another different type of events and then perform action, in that case also first class can be easily modified.
If I am not worried about maintainability I would rather go for a anonymous class.
If the class Foo has no other responsibility than encapsulating this button, then the first solution is sort of ok.
However, as soon as Foo gets more "somethings" that it has to listen to then it gets messy. I prefer the second solution since it is more explicit and has a better scalability.
An even better solution may be to create an anomymous inner class.
public class Foo{
Foo(){
something.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e){
//...
}
});
}
}
Usually you want to use a nested or even anonymous class rather than exposing ActionListener to the API of the enclosing class. (public class Foo implements ActionListener -> Javadoc will state that Foo is an ActionListener, though this is usually just an implementation detail -> bad)

Categories