Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (579)
games submitted by our members
Games in WIP (500)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  How to Organize Event Listeners  (Read 1620 times)
0 Members and 1 Guest are viewing this topic.
Offline Best Username Ever

Junior Member





« Posted 2013-02-01 01:11:16 »

I think a lot of people probably have trouble writing event listener code. It is awkward and error prone for new Java users. Here's how I deal with event listeners and other single purpose anonymous classes. Notice that the object/anonymous class looks a lot like a nested class. The code to attach the event listener is in the object's constructor and the code that defines the listener is in its own function. (This code is based on a file from a non-game project using Swing.)

I can think of four alternatives:
1) Make your class implement a listener interface and attach it by passing this as a parameter. -- This has the downside that you can only use one instance of a given listener type and that your event listener interface is exposed as public.
2) Use an inner class and create a new one the first time you use it. -- This code is essentially the same, but it is a little easier to use. You could attach or remove listeners if you had to. The addListener code is more concise and readable. It is the same number of lines or one fewer.
3) Declare your class in the same place as you first use it. -- It's harder to find the relevant code and is more difficult to read once you find it.
4) Use lambdas, anonymous functions, or other magic imported from scripting languages. -- Nope. It involves fewer characters, which is no practical benefit, but it is as bad as #3.

This method is easy to use. Takes only a few characters with the use of autocompletetion and could easily be made a code template. It is extremely readable and is consistent with Java's type system and its practical features. It is very easy to find the relevant code if you're used to the practice and can be found easier with IDE tools. The nested usage and scoping makes it easy to define methods and variables consistent with good object oriented programming practices. Using adapter classes instead of interfaces is helpful while developing since you can add functionality later without adding empty methods. (Use IDE code completion again to make that task even faster.)

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
public GridThingy extends JWhatever
{
  public GridThingy()
  {
    addMouseListener(ml);
    addMouseMotionListener(ml);
  }
 
  public int yToRow()
  {
    // Convert y position to row number
 }
 
  public Object setValue(int row, Object value)
  {
    // Set row data
 }
 
  public void clearValue(int row)
  {
    // Clear row data
 }
 
  /*
   * Other methods ...
   */

 
  private final MouseAdapter ml = new MouseAdapter()
  {
    private final long doubleClickTime = 600;
    private long clickTime = System.currentTimeMillis() - doubleClickTime;

    @Override public void mouseClicked(MouseEvent e)
    {
      long time = System.currentTimeMillis();
      int x = e.getX(), y = e.getY();
      boolean dbl = time - clickTime < doubleClickTime;
      clickTime = time;

      if(dbl && e.getButton() == MouseEvent.BUTTON1 && /* Omitted */ )
      {
        int row = yToRow(y);
        Object o = promptValue(row);
        if(o != null) setValue(row, o);
      }
     
      if(e.getButton() == MouseEvent.BUTTON3)
      {
        clearValue(yToRow(e.getX()));
      }
    }

    @Override public void mouseMoved(MouseEvent e)
    {
      repaint();
    }
  };
}


It looks a lot better than the alternatives, doesn't it?
Offline ReBirth
« Reply #1 - Posted 2013-02-01 02:59:33 »

Back in my Java2D time I use no 2.

Online Agro
« Reply #2 - Posted 2013-02-01 03:01:17 »

Uh, isn't the most organized way to create something like an Input class which implements whatever you want it to?

Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Best Username Ever

Junior Member





« Reply #3 - Posted 2013-02-01 06:26:44 »

... Yes? Java 2D requires an object implementing a listener interface. The fact that addMouseListener expects a custom implementation of a MouseListener interface means you will indeed have a class designed to handle input that implements stuff to do whatever you want.

The question is where you create that class. Do you want it in the same file or a separate file? If it is an inner class, do you want it to be static or non-static? Do you want an anonymous or general purpose class?

If you mean the mega input classes that noobs inevitably create to centralize input processing or emulate polling, then it depends on the situation. If your requirements are so simple that you can use polling exclusively, then you can use a centralized class. If you're creating a game with multiple types (uses) of input, then you will want a modular set up. Your input class or its clients would turn into a giant mess if you had to manage the state of every aspect of your UI in one object. It's the Java equivalent to a C programmer overusing global variables. Things would easily break if they were changed and it would be hard to read if you could not isolate the code relevant to what you were looking to read.

I presume the reader requires the use of a callback based interface in a library such as, but not necessarily, Java2D. And if not, it is still helpful for people that would not consider different styles of programming because they only know how to handle things like listeners using hacks. It is also good advice to Java programmers in general. (I write a lot of applets and desktop applications. I also use them to prototype physics simulations, algorithms, and control systems for games. Adapting between different styles of user interface libraries does not cost me any time.) I don't advocate or care if someone uses Java2D and this may be irrelevant to LWJGL users, but it would be better if people could use this as a reference or template.

It is embarrassing to be a Java programmer whenever someone releases source code with hacks to get listeners working.
Or, makes a terrible UI because the programmer spent too much time writing disorganized Swing code.
Or, reinvents the wheel be making their own input handling classes.
Or, complains about the problem of using ugly anonymous classes.
Or, says that Java needs duck typing or first class functions.
Or, says that Java projects have more source code files than other projects. (A myth true for C++ but not Java, contrary to popular misconception.)

For some reason, I do not see many other people store anonymous classes directly in fields or using the same layout as normal classes. They always stick them in local variables or directly in function parameters. It's one of the tricks I learned that probably is not standard Java practices but really improves your productivity. It also addresses one of the persistent criticisms/myths of Java that JDK programmers try to solve with language modifications based on truly bad programming languages.
Offline 65K
« Reply #4 - Posted 2013-02-01 08:40:27 »

5) Use a little helper to map events directly to handler methods: no clutter from anonymous classes, meaningful names like onSaveButtonClicked(...) instead of actionPerformed(...), one place for common actions like logging ui exceptions, multiple event handlers possible

EDIT:
Without using any magic helper, just keep the anonymous listeners classes as small as possible and call the real handlers from there (again, meaningful methods names). Easier to test, easier to trigger the same functions from various event sources, one code block to group the listener clutter which usually do not need be looked at later.

Offline Best Username Ever

Junior Member





« Reply #5 - Posted 2013-02-01 23:35:02 »

Good point. Action listeners are strange... Stare

I still like encapsulating UI input state in its own class/object. I like to think of the listener class as a plugin, so that even if I only write one, the user interface code can be written independently from the rendering and functional methods of the main class. This might be useful, for example, if you started with a applet supporting two button computers but later decide you want to expand the project to support touch screens. If you mixed the functionality of the two classes, you would have to rewrite most of your class and then you may need multiple versions of the same methods or similar variables. Noobs to that type of UI programming, including myself at one time, end up writing terrifying spaghetti code. When I don't need two implementations, it is still helpful in case I ever make a mistake that requires reworking part of the class and because I can write a crude interface using the easiest methods for quick testing and replace the listener/state-handler later with a more polished one.

It's absolutely a good idea to split up a basic listener interface with individual "events". A lot of people make the mistake of doing things like creating a public save() method of their component class instead of a protected saveButtonPressed() and a public saveProject() method in another class. You can use both methods, so I would not call it a fifth method. Smiley
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

xsi3rr4x (35 views)
2014-04-15 18:08:23

BurntPizza (31 views)
2014-04-15 03:46:01

UprightPath (46 views)
2014-04-14 17:39:50

UprightPath (29 views)
2014-04-14 17:35:47

Porlus (46 views)
2014-04-14 15:48:38

tom_mai78101 (67 views)
2014-04-10 04:04:31

BurntPizza (127 views)
2014-04-08 23:06:04

tom_mai78101 (227 views)
2014-04-05 13:34:39

trollwarrior1 (192 views)
2014-04-04 12:06:45

CJLetsGame (199 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!