use EventDispatcher for Input- and Map-Events

This commit is contained in:
Hannes Janetzek 2014-02-14 14:50:19 +01:00
parent 940a753b41
commit e15163bb08
13 changed files with 153 additions and 168 deletions

View File

@ -17,12 +17,13 @@ package org.oscim.android;
*/
import java.util.HashMap;
import java.util.Map;
import org.oscim.android.canvas.AndroidBitmap;
import org.oscim.core.MapPosition;
import org.oscim.core.MercatorProjection;
import org.oscim.event.Event;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
import org.oscim.map.Map.UpdateListener;
import org.oscim.renderer.BitmapRenderer;
@ -71,7 +72,7 @@ public class MapScaleBar extends Layer implements UpdateListener {
private boolean mRedrawNeeded;
private double mPrevLatitude = -1;
private final double mPrevScale = -1;
private final Map<TextField, String> mTextFields;
private final HashMap<TextField, String> mTextFields;
private final Bitmap mBitmap;
// passed to BitmapRenderer - need to sync on this object.
@ -102,7 +103,9 @@ public class MapScaleBar extends Layer implements UpdateListener {
}
@Override
public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
public void onMapEvent(Event e, MapPosition mapPosition) {
if (e == Map.UPDATE_EVENT)
return;
double latitude = MercatorProjection.toLatitude(mapPosition.y);

View File

@ -152,8 +152,7 @@ public class MapView extends RelativeLayout {
if (mGestureDetector.onTouchEvent(motionEvent))
return true;
mMap.handleMotionEvent(mMotionEvent.wrap(motionEvent));
mMap.input.fire(null, mMotionEvent.wrap(motionEvent));
return true;
}

View File

@ -0,0 +1,9 @@
package org.oscim.event;
/**
* The Class Event to be sub-classed by event-producers. Should be used
* to distinguish the type of event when more than one is produced.
*/
public class Event {
}

View File

@ -5,70 +5,32 @@ import org.oscim.utils.pool.LList;
/**
* The Class EventDispatcher.
*
* Events MUST be dispached from main-loop! To add events from other
* Events MUST be dispatched from main-loop! To add events from other
* threads use:
* Map.post(new Runnable(){ public void run(tell(event,data);)};);
*
* @param <T> the event source type
* @param <E> the event 'data' type
*/
public class EventDispatcher<T, E> {
/**
* Generic event-listener interface.
*
* @param <E> the type of event 'data'
*/
public interface Listener<E> {
/**
* The onEvent handler to be implemented by Listeners.
*
* @param source the event source
* @param event the event object/type
* @param data some object involved in this event
*/
public void onEvent(Object source, Event event, E data);
}
/**
* The Class Event to be subclassed by event-producers. Should be used
* to distinguish the type of event when more than one is produced.
*/
public static class Event {
// nothing here
}
/** The event source object. */
protected final T mSource;
public abstract class EventDispatcher<E extends EventListener, T> {
/** The list of listeners. */
protected LList<Listener<E>> mListeners;
/**
* Instantiates a new event dispatcher.
*
* @param source the event-source
*/
public EventDispatcher(T source) {
mSource = source;
}
protected LList<E> mListeners;
/**
* Bind listener for event notifications.
*/
public void bind(Listener<E> listener) {
public void bind(E listener) {
if (LList.find(mListeners, listener) != null) {
// throw some poo?
return;
}
mListeners = LList.push(mListeners, new LList<Listener<E>>(listener));
mListeners = LList.push(mListeners, new LList<E>(listener));
}
/**
* Unbind listener.
* Remove listener.
*/
public void unbind(Listener<E> listener) {
public void unbind(E listener) {
mListeners = LList.remove(mListeners, listener);
}
@ -78,9 +40,11 @@ public class EventDispatcher<T, E> {
* @param event the event
* @param data the data
*/
public void tell(Event event, E data) {
for (LList<Listener<E>> l = mListeners; l != null; l = l.next) {
l.data.onEvent(mSource, event, data);
public abstract void tell(E listener, Event event, T data);
public void fire(Event event, T data) {
for (LList<E> l = mListeners; l != null; l = l.next) {
tell(l.data, event, data);
}
}
}

View File

@ -0,0 +1,8 @@
package org.oscim.event;
/**
* Event-listener interface.
*/
public interface EventListener {
}

View File

@ -17,6 +17,7 @@
package org.oscim.layers;
import org.oscim.core.MapPosition;
import org.oscim.event.Event;
import org.oscim.map.Map;
import org.oscim.map.Map.UpdateListener;
import org.oscim.renderer.GLViewport;
@ -61,7 +62,7 @@ public class CustomRenderLayer extends Layer implements UpdateListener {
private int someConccurentVariable;
@Override
public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
public void onMapEvent(Event e, MapPosition mapPosition) {
synchronized (mRenderer) {
someConccurentVariable++;

View File

@ -18,10 +18,12 @@ package org.oscim.layers;
import org.oscim.backend.CanvasAdapter;
import org.oscim.core.Tile;
import org.oscim.event.Event;
import org.oscim.event.Gesture;
import org.oscim.event.GestureListener;
import org.oscim.event.MotionEvent;
import org.oscim.map.Map;
import org.oscim.map.Map.InputListener;
import org.oscim.map.ViewController;
import org.oscim.utils.FastMath;
import org.slf4j.Logger;
@ -30,7 +32,7 @@ import org.slf4j.LoggerFactory;
/**
* Changes Viewport by handling move, fling, scale, rotation and tilt gestures.
*/
public class MapEventLayer extends Layer implements Map.InputListener, GestureListener {
public class MapEventLayer extends Layer implements InputListener, GestureListener {
static final Logger log = LoggerFactory.getLogger(MapEventLayer.class);
@ -84,8 +86,8 @@ public class MapEventLayer extends Layer implements Map.InputListener, GestureLi
}
@Override
public void onMotionEvent(MotionEvent event) {
onTouchEvent(event);
public void onInputEvent(Event e, MotionEvent motionEvent) {
onTouchEvent(motionEvent);
}
public void enableRotation(boolean enable) {
@ -496,4 +498,5 @@ public class MapEventLayer extends Layer implements Map.InputListener, GestureLi
// return sum;
//}
}
}

View File

@ -24,6 +24,7 @@ import org.oscim.backend.canvas.Bitmap;
import org.oscim.core.MapElement;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.event.Event;
import org.oscim.map.Map;
import org.oscim.renderer.elements.BitmapLayer;
import org.oscim.renderer.elements.ElementLayers;
@ -71,8 +72,11 @@ public class BitmapTileLayer extends TileLayer {
}
@Override
public void onMapUpdate(MapPosition pos, boolean changed, boolean clear) {
super.onMapUpdate(pos, changed, clear);
public void onMapEvent(Event event, MapPosition pos) {
super.onMapEvent(event, pos);
if (event != Map.POSITION_EVENT)
return;
FadeStep[] fade = mTileSource.getFadeSteps();

View File

@ -17,6 +17,7 @@
package org.oscim.layers.tile;
import org.oscim.core.MapPosition;
import org.oscim.event.Event;
import org.oscim.layers.Layer;
import org.oscim.map.Map;
import org.oscim.map.Map.UpdateListener;
@ -69,19 +70,22 @@ public abstract class TileLayer extends Layer implements UpdateListener {
}
@Override
public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (clear) {
public void onMapEvent(Event event, MapPosition mapPosition) {
if (event == Map.CLEAR_EVENT) {
// sync with TileRenderer
synchronized (mRenderer) {
tileRenderer().clearTiles();
mTileManager.init();
}
changed = true;
}
if (mTileManager.update(mapPosition))
notifyLoaders();
if (changed && mTileManager.update(mapPosition))
notifyLoaders();
} else if (event == Map.POSITION_EVENT) {
if (mTileManager.update(mapPosition))
notifyLoaders();
}
}
@Override

View File

@ -17,9 +17,7 @@
package org.oscim.layers.tile.vector.labeling;
import org.oscim.core.MapPosition;
import org.oscim.event.EventDispatcher.Event;
import org.oscim.event.EventDispatcher.Listener;
import org.oscim.event.MotionEvent;
import org.oscim.event.Event;
import org.oscim.layers.Layer;
import org.oscim.layers.tile.vector.VectorTileLayer;
import org.oscim.map.Map;
@ -29,8 +27,7 @@ import org.oscim.utils.async.SimpleWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LabelLayer extends Layer implements Map.InputListener, Map.UpdateListener,
Listener<MapTile> {
public class LabelLayer extends Layer implements Map.UpdateListener, TileManager.Listener {
static final Logger log = LoggerFactory.getLogger(LabelLayer.class);
@ -94,33 +91,34 @@ public class LabelLayer extends Layer implements Map.InputListener, Map.UpdateLi
}
@Override
public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) {
if (clear)
public void onMapEvent(Event event, MapPosition mapPosition) {
if (event == Map.CLEAR_EVENT)
mWorker.cancel(true);
if (changed || clear)
if (event == Map.POSITION_EVENT)
mWorker.submit(MAX_RELABEL_DELAY);
}
@Override
public void onMotionEvent(MotionEvent e) {
// int action = e.getAction() & MotionEvent.ACTION_MASK;
// if (action == MotionEvent.ACTION_POINTER_DOWN) {
// multi++;
// mTextRenderer.hold(true);
// } else if (action == MotionEvent.ACTION_POINTER_UP) {
// multi--;
// if (multi == 0)
// mTextRenderer.hold(false);
// } else if (action == MotionEvent.ACTION_CANCEL) {
// multi = 0;
// log.debug("cancel " + multi);
// mTextRenderer.hold(false);
// }
}
// @Override
// public void onMotionEvent(MotionEvent e) {
// // int action = e.getAction() & MotionEvent.ACTION_MASK;
// // if (action == MotionEvent.ACTION_POINTER_DOWN) {
// // multi++;
// // mTextRenderer.hold(true);
// // } else if (action == MotionEvent.ACTION_POINTER_UP) {
// // multi--;
// // if (multi == 0)
// // mTextRenderer.hold(false);
// // } else if (action == MotionEvent.ACTION_CANCEL) {
// // multi = 0;
// // log.debug("cancel " + multi);
// // mTextRenderer.hold(false);
// // }
// }
@Override
public void onEvent(Object source, Event e, MapTile tile) {
public void onTileManagerEvent(Event e, MapTile tile) {
if (e == TileManager.TILE_LOADED) {
if (tile.isVisible)
mWorker.submit(MAX_RELABEL_DELAY / 4);

View File

@ -57,9 +57,10 @@ public final class Layers extends AbstractList<Layer> {
throw new IllegalArgumentException("layer added twice");
if (layer instanceof UpdateListener)
mMap.bind((UpdateListener) layer);
mMap.events.bind((UpdateListener) layer);
if (layer instanceof InputListener)
mMap.bind((InputListener) layer);
mMap.input.bind((InputListener) layer);
mLayerList.add(index, layer);
mDirtyLayers = true;
@ -72,9 +73,9 @@ public final class Layers extends AbstractList<Layer> {
Layer remove = mLayerList.remove(index);
if (remove instanceof UpdateListener)
mMap.unbind((UpdateListener) remove);
mMap.events.unbind((UpdateListener) remove);
if (remove instanceof InputListener)
mMap.unbind((InputListener) remove);
mMap.input.unbind((InputListener) remove);
return remove;
}
@ -89,9 +90,9 @@ public final class Layers extends AbstractList<Layer> {
// unbind replaced layer
if (remove instanceof UpdateListener)
mMap.unbind((UpdateListener) remove);
mMap.events.unbind((UpdateListener) remove);
if (remove instanceof InputListener)
mMap.unbind((InputListener) remove);
mMap.input.unbind((InputListener) remove);
return remove;
}

View File

@ -16,10 +16,10 @@
*/
package org.oscim.map;
import java.util.LinkedHashSet;
import java.util.Set;
import org.oscim.core.MapPosition;
import org.oscim.event.Event;
import org.oscim.event.EventDispatcher;
import org.oscim.event.EventListener;
import org.oscim.event.Gesture;
import org.oscim.event.GestureDetector;
import org.oscim.event.MotionEvent;
@ -45,8 +45,8 @@ public abstract class Map {
* register when the layer is added to the map and unregistered when
* the layer is removed.
*/
public interface UpdateListener {
void onMapUpdate(MapPosition mapPosition, boolean positionChanged, boolean clear);
public interface UpdateListener extends EventListener {
void onMapEvent(Event e, MapPosition mapPosition);
}
/**
@ -55,41 +55,67 @@ public abstract class Map {
* register when the layer is added to the map and unregistered when
* the layer is removed.
*/
public interface InputListener {
void onMotionEvent(MotionEvent e);
public interface InputListener extends EventListener {
void onInputEvent(Event e, MotionEvent motionEvent);
}
/***/
public static Event UPDATE_EVENT = new Event();
/**
* Map state has changed in a way that all layers should clear their state
* e.g. the theme or the TilesSource has changed
*/
public static Event CLEAR_EVENT = new Event();
public static Event POSITION_EVENT = new Event();
public final EventDispatcher<InputListener, MotionEvent> input;
public final EventDispatcher<UpdateListener, MapPosition> events;
private final Layers mLayers;
private final ViewController mViewport;
private final Animator mAnimator;
private final MapPosition mMapPosition;
private final AsyncExecutor mAsyncExecutor;
protected final MapEventLayer mEventLayer;
protected GestureDetector mGestureDetector;
/**
* Listener interface for map update notifications.
* Layers implementing this interface they will be automatically
* register when the layer is added to the map and unregistered when
* the layer is removed.
*/
private VectorTileLayer mBaseLayer;
private Set<InputListener> mInputListenerSet = new LinkedHashSet<InputListener>();
private InputListener[] mInputListeners;
private Set<UpdateListener> mUpdateListenerSet = new LinkedHashSet<UpdateListener>();
private UpdateListener[] mUpdateListeners;
protected boolean mClearMap = true;
public Map() {
mViewport = new ViewController();
mAnimator = new Animator(this);
mLayers = new Layers(this);
input = new EventDispatcher<InputListener, MotionEvent>() {
@Override
public void tell(InputListener l, Event e, MotionEvent d) {
l.onInputEvent(e, d);
}
};
events = new EventDispatcher<UpdateListener, MapPosition>() {
@Override
public void tell(UpdateListener l, Event e, MapPosition d) {
l.onMapEvent(e, d);
}
};
mAsyncExecutor = new AsyncExecutor(2);
mMapPosition = new MapPosition();
mEventLayer = new MapEventLayer(this);
mLayers.add(0, mEventLayer);
}
public MapEventLayer getEventLayer() {
@ -248,22 +274,6 @@ public abstract class Map {
return mAnimator;
}
/**
* Register UpdateListener
*/
public void bind(UpdateListener l) {
if (mUpdateListenerSet.add(l))
mUpdateListeners = null;
}
/**
* Unregister UpdateListener
*/
public void unbind(UpdateListener l) {
if (mUpdateListenerSet.remove(l))
mUpdateListeners = null;
}
/**
* This function is run on main-loop before rendering a frame.
* Caution: Do not call directly!
@ -274,44 +284,16 @@ public abstract class Map {
changed |= mViewport.getMapPosition(pos);
if (mUpdateListeners == null) {
mUpdateListeners = new UpdateListener[mUpdateListenerSet.size()];
mUpdateListenerSet.toArray(mUpdateListeners);
}
for (UpdateListener l : mUpdateListeners)
l.onMapUpdate(pos, changed, mClearMap);
if (mClearMap)
events.fire(CLEAR_EVENT, pos);
else if (changed)
events.fire(POSITION_EVENT, pos);
else
events.fire(UPDATE_EVENT, pos);
mClearMap = false;
}
/**
* Register InputListener
*/
public void bind(InputListener listener) {
if (mInputListenerSet.add(listener))
mInputListeners = null;
}
/**
* Unregister InputListener
*/
public void unbind(InputListener listener) {
if (mInputListenerSet.remove(listener))
mInputListeners = null;
}
public void handleMotionEvent(MotionEvent e) {
if (mInputListeners == null) {
mInputListeners = new InputListener[mInputListenerSet.size()];
mInputListenerSet.toArray(mInputListeners);
}
for (InputListener l : mInputListeners)
l.onMotionEvent(e);
}
public boolean handleGesture(Gesture g, MotionEvent e) {
return mLayers.handleGesture(g, e);
}

View File

@ -28,8 +28,9 @@ import java.util.Arrays;
import org.oscim.core.MapPosition;
import org.oscim.core.Tile;
import org.oscim.event.Event;
import org.oscim.event.EventDispatcher;
import org.oscim.event.EventDispatcher.Event;
import org.oscim.event.EventListener;
import org.oscim.map.Map;
import org.oscim.map.Viewport;
import org.oscim.renderer.BufferObject;
@ -46,7 +47,17 @@ public class TileManager {
public final static Event TILE_LOADED = new Event();
public final static Event TILE_REMOVED = new Event();
public final EventDispatcher<TileManager, MapTile> events;
public final EventDispatcher<Listener, MapTile> events = new EventDispatcher<Listener, MapTile>() {
@Override
public void tell(Listener l, Event event, MapTile tile) {
l.onTileManagerEvent(event, tile);
}
};
public interface Listener extends EventListener {
void onTileManagerEvent(Event event, MapTile tile);
};
private int mCacheLimit;
private int mCacheReduce;
@ -139,8 +150,6 @@ public class TileManager {
mTilesSize = 0;
mTilesForUpload = 0;
mUpdateSerial = 0;
events = new EventDispatcher<TileManager, MapTile>(this);
}
private int[] mZoomTable;
@ -392,7 +401,7 @@ public class TileManager {
private void removeFromCache(MapTile t) {
if (t.state == NEW_DATA || t.state == READY)
events.tell(TILE_REMOVED, t);
events.fire(TILE_REMOVED, t);
synchronized (t) {
/* still belongs to TileLoader thread, defer clearing to
@ -518,7 +527,7 @@ public class TileManager {
}
tile.state = NEW_DATA;
events.tell(TILE_LOADED, tile);
events.fire(TILE_LOADED, tile);
mTilesForUpload += 1;