-
Notifications
You must be signed in to change notification settings - Fork 415
Events
Most events in Codename One are routed via the high level events (e.g. action listener) but sometimes we need access to low level events (e.g. when drawing via Graphics) that provide more fine grained access. Typically working with the higher level events is far more potable since it might map to different functionality on different devices.
The most common high level event is the ActionListener which allows binding a generic action event to pretty much anything. This is so ubiquitous in Codename One that it is even used for networking (as a base class) and for some of the low level event options.
E.g. we can bind an event callback for a Button by using:
Button b = new Button("Click Me");
b.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
// button was clicked, you can do anything you want here...
}
});
Notice that the click will work whether the button was touched using a mouse, finger or keypad shortcut seamlessly with an action listener. Many components work with action events e.g. buttons, text components, slider etc.
There are quite a few types of high level event types that are more specific to requirements.
The DataChangedListener is used in several places to indicate that the underlying model data has changed:
-
TextField - the text field provides an action listener but that only "fires" when the data input is complete.
DataChangeListener
files with every key entered and thus allows functionality such as "auto complete" and is indeed used internally in the Codename One AutoCompleteTextField. -
TableModel & ListModel - the model for the Table class notifies the view that its content has changed via this event, thus allowing the UI to refresh properly.
The focus listener allows us to track the currently "selected" or focused component. Its not as useful as it used to be in feature phones. You can bind a focus listener to the component itself and receive an event when it gained focus, or you can bind the listener to the form and receive events for every focus change event within the hierarchy.
ScrollListener allows tracking scroll events so UI elements can be adapted if necessary. Normally scrolling is seamless and this event isn’t necessary, however if developers wish to "shrink" or "fade" an element on scrolling this interface can be used to achieve that. Notice that you should bind the scroll listener to the actual scrollable component and not to an arbitrary component.
E.g. in this code from the Flickr
demo the Toolbar is faded based on scroll position:
public class CustomToolbar extends Toolbar implements ScrollListener {
private int alpha;
public CustomToolbar() {
}
public void paintComponentBackground(Graphics g) {
int a = g.getAlpha();
g.setAlpha(alpha);
super.paintComponentBackground(g);
g.setAlpha(a);
}
public void scrollChanged(int scrollX, int scrollY, int oldscrollX, int oldscrollY) {
alpha = scrollY;
alpha = Math.max(alpha, 0);
alpha = Math.min(alpha, 255);
}
}
The SelectionListener event is mostly used to broadcast list model selection changes to the list view. Since list supports the ActionListener event callback its usually the better option since its more coarse grained. SelectionListener gets fired too often for events and that might result in a performance penalty. When running on non-touch devices list selection could be changed with the keypad and only a specific fire button click would fire the action event, for those cases SelectionListener made a lot of sense. However, in touch devices this API isn’t as useful.
StyleListener allows components to track changes to the style objects. E.g. if the developer does something like:
cmp.getUnselectedStyle().setFgColor(0xffffff);
This will trigger a style event that will eventually lead to the component being repainted. This is quite important for the component class but not a very important event for general user code.
NetworkEvent is a subclass of ActionEvent that is passed to actionPerformed
callbacks made in relation
to generic network code. E.g.
NetworkManager.getInstance().addErrorListener(new ActionListener() {
public void actionPerformed(ActionEvent ev) {
NetworkEvent ne = (NetworkEvent)ev;
// now we have access to the methods on NetworkEvent that provide more information about the network specific flags
}
});
When creating your own components and objects you sometimes want to broadcast your own events, for that purpose Codename One has the EventDispatcher class which saves a lot of coding effort in this regard. E.g. if you wish to provide an ActionListener event for components you can just add this to your class:
private final EventDispatcher listeners = new EventDispatcher();
public void addActionListener(ActionListener a) {
listeners.addListener(a);
}
public void removeActionListener(ActionListener a) {
listeners.removeListener(a);
}
Low level events can be implemented in one of 3 ways:
Each of those has advantages and disadvantages, specifically:
-
'Form' based events and callbacks deliver pointer events in the 'Form' coordinate space.
-
'Component' based events require focus even if it isn’t drawn
-
'Form' based events can block existing functionality from going on e.g. you can avoid calling super in a form event and thus block other events from happening (e.g. block a listener or component event from even triggering).
Listener | Override Form | Override Container | |
---|---|---|---|
Coordinate System |
Form |
Form |
Component |
Block current functionality |
Yes, just avoid super |
Partially (event consume) |
No |
There are two basic types of low level events: Key and Pointer.
Key events are only relevant to physical keys and will not trigger on virtual keyboard keys, to track those use
a TextField with a DataChangeListener
as mentioned above.
The pointer events (touch events) can be intercepted by overriding one or more of these methods in Component or
Form. Notice that unless you want to block functionality you should probably invoke super
when overriding:
public void pointerDragged(int[] x, int[] y)
public void pointerDragged(final int x, final int y)
public void pointerPressed(int[] x, int[] y)
public void pointerPressed(int x, int y)
public void pointerReleased(int[] x, int[] y)
public void pointerReleased(int x, int y)
public void longPointerPress(int x, int y)
public void keyPressed(int keyCode)
public void keyReleased(int keyCode)
public void keyRepeated(int keyCode)
Notice that most pointer events have a version that accepts an array as an argument, this allows for multi-touch event handling by sending all the currently touched coordinates.
Drag events are quite difficult to handle properly across devices. Some devices send a ridiculous number of events for even the lightest touch while others send too little. It seems like too many drag events wouldn’t be a problem, however if we drag over a button then it might disable the buttons action event (since this might be the user trying to scroll).
Drag sensitivity should really be about the component being dragged which is why we have the method
getDragRegionStatus
that allows us to "hint" to the drag API whether we are interested in drag events or not
and if so in which directional bias.
The BrowserNavigationCallback isn’t quite an "event" but there is no real "proper" classification for it.
Important
|
The callback method of this interface is invoked off the EDT! You must NEVER block this method and must not access UI or Codename One sensitive elements in this method! |
The browser navigation callback is invoked directly from the native web component as it navigates to a new page. Because of that it is invoked on the native OS thread and gives us a unique opportunity to handle the navigation ourselves as we see fit. That is why it MUST be invoked on the native thread, since the native browser is pending on our response to that method, spanning an invokeAndBlock/callSerially would be to slow and would bog down the browser.
You can use the browser navigation callback to change the UI or even to invoke Java code from JavaScript code e.g.:
browser.setBrowserNavigationCallback(new BrowserNavigationCallback() {
public boolean shouldNavigate(String url) {
if(url.startsWith(myUrlPrefix)) {
// warning!!! This is not on the EDT and this method MUST return immediately!
Display.getInstance().callSerially(new Runnable() {
public void run() {
// this code will happen later on the EDT, we can do anything here...
}
});
// don't navigate in the browser
return false;
}
return true;
}
});
About This Guide
Introduction
Basics: Themes, Styles, Components & Layouts
Theme Basics
Advanced Theming
Working With The GUI Builder
The Components Of Codename One
Using ComponentSelector
Animations & Transitions
The EDT - Event Dispatch Thread
Monetization
Graphics, Drawing, Images & Fonts
Events
File-System,-Storage,-Network-&-Parsing
Miscellaneous Features
Performance, Size & Debugging
Advanced Topics/Under The Hood
Signing, Certificates & Provisioning
Appendix: Working With iOS
Appendix: Working with Mac OS X
Appendix: Working With Javascript
Appendix: Working With UWP
Security
cn1libs
Appendix: Casual Game Programming