diff --git a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java index e8d35b4f..3593e4ed 100644 --- a/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java +++ b/vtm-gdx-html/src/org/oscim/gdx/emu/org/oscim/layers/tile/bitmap/BitmapTileLayer.java @@ -19,8 +19,6 @@ import java.net.URL; import org.oscim.backend.canvas.Bitmap; import org.oscim.core.MapPosition; import org.oscim.core.Tile; -import org.oscim.event.MapEvent; -import org.oscim.event.UpdateEvent; import org.oscim.gdx.client.GwtBitmap; import org.oscim.layers.tile.TileLayer; import org.oscim.layers.tile.bitmap.TileSource.FadeStep; @@ -50,66 +48,33 @@ public class BitmapTileLayer extends TileLayer<TileLoader> { mFade = mTileSource.getFadeSteps(); } - // @Override - // public void onUpdate(MapPosition pos, boolean changed, boolean clear) { - // super.onUpdate(pos, changed, clear); - // - // if (mFade == null) { - // mRenderLayer.setBitmapAlpha(1); - // return; - // } - // - // float alpha = 0; - // for (FadeStep f : mFade) { - // if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd) - // continue; - // - // if (f.alphaStart == f.alphaEnd) { - // alpha = f.alphaStart; - // break; - // } - // double range = f.scaleEnd / f.scaleStart; - // float a = (float)((range - (pos.scale / f.scaleStart)) / range); - // a = FastMath.clamp(a, 0, 1); - // // interpolate alpha between start and end - // alpha = a * f.alphaStart + (1 - a) * f.alphaEnd; - // break; - // } - // - // mRenderLayer.setBitmapAlpha(alpha); - // } - @Override - public void handleEvent(MapEvent event) { - super.handleEvent(event); + public void onMapUpdate(MapPosition pos, boolean changed, boolean clear) { + super.onMapUpdate(pos, changed, clear); - if (event instanceof UpdateEvent) { + if (mFade == null) { + mRenderLayer.setBitmapAlpha(1); + return; + } - if (mFade == null) { - mRenderLayer.setBitmapAlpha(1); - return; - } - MapPosition pos = mMap.getMapPosition(); + float alpha = 0; + for (FadeStep f : mFade) { + if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd) + continue; - float alpha = 0; - for (FadeStep f : mFade) { - if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd) - continue; - - if (f.alphaStart == f.alphaEnd) { - alpha = f.alphaStart; - break; - } - double range = f.scaleEnd / f.scaleStart; - float a = (float) ((range - (pos.scale / f.scaleStart)) / range); - a = FastMath.clamp(a, 0, 1); - // interpolate alpha between start and end - alpha = a * f.alphaStart + (1 - a) * f.alphaEnd; + if (f.alphaStart == f.alphaEnd) { + alpha = f.alphaStart; break; } - - mRenderLayer.setBitmapAlpha(alpha); + double range = f.scaleEnd / f.scaleStart; + float a = (float) ((range - (pos.scale / f.scaleStart)) / range); + a = FastMath.clamp(a, 0, 1); + // interpolate alpha between start and end + alpha = a * f.alphaStart + (1 - a) * f.alphaEnd; + break; } + + mRenderLayer.setBitmapAlpha(alpha); } @Override diff --git a/vtm-gdx/src/org/oscim/gdx/GdxMap.java b/vtm-gdx/src/org/oscim/gdx/GdxMap.java index a3db1e53..683f771a 100644 --- a/vtm-gdx/src/org/oscim/gdx/GdxMap.java +++ b/vtm-gdx/src/org/oscim/gdx/GdxMap.java @@ -82,6 +82,33 @@ public abstract class GdxMap implements ApplicationListener { }, delay / 1000f); return true; } + + /** + * Update all Layers on Main thread. + * + * @param forceRedraw + * also render frame FIXME (does nothing atm) + */ + private void redrawMapInternal(boolean forceRedraw) { + // FIXME needed? + GLState.blend(false); + GLState.test(false, false); + + updateLayers(); + + mRenderRequest = true; + Gdx.graphics.requestRendering(); + } + + /* private */boolean mWaitRedraw; + private final Runnable mRedrawRequest = new Runnable() { + @Override + public void run() { + mWaitRedraw = false; + redrawMapInternal(false); + } + }; + }; mMapRenderer = new MapRenderer(mMap); @@ -200,30 +227,6 @@ public abstract class GdxMap implements ApplicationListener { public void resume() { } - /** - * Update all Layers on Main thread. - * - * @param forceRedraw - * also render frame FIXME (does nothing atm) - */ - void redrawMapInternal(boolean forceRedraw) { - GLState.blend(false); - GLState.test(false, false); - - mMap.updateLayers(); - - mRenderRequest = true; - Gdx.graphics.requestRendering(); - } - - /* private */boolean mWaitRedraw; - private final Runnable mRedrawRequest = new Runnable() { - @Override - public void run() { - mWaitRedraw = false; - redrawMapInternal(false); - } - }; class TouchHandler implements InputProcessor { diff --git a/vtm/src/org/oscim/event/Dispatcher.java b/vtm/src/org/oscim/event/Dispatcher.java new file mode 100644 index 00000000..7fc5012f --- /dev/null +++ b/vtm/src/org/oscim/event/Dispatcher.java @@ -0,0 +1,19 @@ +package org.oscim.event; + +import java.util.ArrayList; +import java.util.List; + +public abstract class Dispatcher<T> { + protected List<T> listeners = new ArrayList<T>(); + + public void addListener(T l) { + if (!listeners.contains(l)) + listeners.add(l); + } + + public void removeListener(T l) { + listeners.remove(l); + } + + public abstract void dispatch(); +} diff --git a/vtm/src/org/oscim/event/IListener.java b/vtm/src/org/oscim/event/IListener.java new file mode 100644 index 00000000..55b6ffd7 --- /dev/null +++ b/vtm/src/org/oscim/event/IListener.java @@ -0,0 +1,5 @@ +package org.oscim.event; + +public interface IListener { + +} diff --git a/vtm/src/org/oscim/event/UpdateEvent.java b/vtm/src/org/oscim/event/UpdateEvent.java deleted file mode 100644 index 1a650b11..00000000 --- a/vtm/src/org/oscim/event/UpdateEvent.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.oscim.event; - - -public class UpdateEvent extends MapEvent { - - private static final long serialVersionUID = 1L; - public static final String TYPE = "UpdateEvent"; - - public UpdateEvent(Object source) { - super(source); - } - - public boolean positionChanged; - public boolean clearMap; - -} diff --git a/vtm/src/org/oscim/layers/tile/TileLayer.java b/vtm/src/org/oscim/layers/tile/TileLayer.java index 23a72a2a..ebbedac5 100644 --- a/vtm/src/org/oscim/layers/tile/TileLayer.java +++ b/vtm/src/org/oscim/layers/tile/TileLayer.java @@ -16,16 +16,14 @@ package org.oscim.layers.tile; import java.util.ArrayList; -import org.oscim.event.EventListener; -import org.oscim.event.MapEvent; -import org.oscim.event.UpdateEvent; +import org.oscim.core.MapPosition; import org.oscim.layers.Layer; import org.oscim.map.Map; import org.oscim.tiling.TileLoader; import org.oscim.tiling.TileManager; import org.oscim.tiling.TileRenderer; -public abstract class TileLayer<T extends TileLoader> extends Layer implements EventListener { +public abstract class TileLayer<T extends TileLoader> extends Layer implements Map.UpdateListener { //private final static String TAG = TileLayer.class.getName(); private final static int MAX_ZOOMLEVEL = 17; private final static int MIN_ZOOMLEVEL = 2; @@ -61,8 +59,6 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements E // RenderLayer is working in GL Thread and actually // drawing loaded tiles to screen. mRenderer = mRenderLayer = new TileRenderer(mTileManager); - - map.addListener(UpdateEvent.TYPE, this); } abstract protected T createLoader(TileManager tm); @@ -71,55 +67,32 @@ public abstract class TileLayer<T extends TileLoader> extends Layer implements E return (TileRenderer) mRenderer; } - // @Override - // public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) { - // - // if (clear || mInitial) { - // mRenderLayer.clearTiles(); - // mTileManager.init(mInitial); - // mInitial = false; - // changed = true; - // } - // - // if (changed && mTileManager.update(mapPosition)) - // notifyLoaders(); - // } - @Override - public void handleEvent(MapEvent event) { - if (event instanceof UpdateEvent) { - - UpdateEvent e = (UpdateEvent) event; - - boolean changed = e.positionChanged; - - if (e.clearMap || mInitial) { - mRenderLayer.clearTiles(); - mTileManager.init(mInitial); - mInitial = false; - changed = true; - } - - if (changed && mTileManager.update(mMap.getMapPosition())) - notifyLoaders(); + public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) { + if (clear || mInitial) { + mRenderLayer.clearTiles(); + mTileManager.init(mInitial); + mInitial = false; + changed = true; } + + if (changed && mTileManager.update(mapPosition)) + notifyLoaders(); } @Override public void onDetach() { - mMap.removeListener(UpdateEvent.TYPE, this); - for (T loader : mTileLoader) { loader.pause(); loader.interrupt(); loader.cleanup(); - // try { - // tileWorker.join(10000); - // } catch (InterruptedException e) { - // // restore the interrupted status - // Thread.currentThread().interrupt(); - // } + //try { + // tileWorker.join(10000); + //} catch (InterruptedException e) { + // // restore the interrupted status + // Thread.currentThread().interrupt(); + //} } mTileManager.destroy(); } diff --git a/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java b/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java index cf843773..58214650 100644 --- a/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java +++ b/vtm/src/org/oscim/layers/tile/bitmap/BitmapTileLayer.java @@ -24,8 +24,6 @@ import org.oscim.backend.CanvasAdapter; import org.oscim.backend.canvas.Bitmap; import org.oscim.core.MapPosition; import org.oscim.core.Tile; -import org.oscim.event.MapEvent; -import org.oscim.event.UpdateEvent; import org.oscim.layers.tile.TileLayer; import org.oscim.layers.tile.bitmap.TileSource.FadeStep; import org.oscim.map.Map; @@ -53,66 +51,33 @@ public class BitmapTileLayer extends TileLayer<TileLoader> { } -// @Override -// public void onUpdate(MapPosition pos, boolean changed, boolean clear) { -// super.onUpdate(pos, changed, clear); -// -// if (mFade == null) { -// mRenderLayer.setBitmapAlpha(1); -// return; -// } -// -// float alpha = 0; -// for (FadeStep f : mFade) { -// if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd) -// continue; -// -// if (f.alphaStart == f.alphaEnd) { -// alpha = f.alphaStart; -// break; -// } -// double range = f.scaleEnd / f.scaleStart; -// float a = (float)((range - (pos.scale / f.scaleStart)) / range); -// a = FastMath.clamp(a, 0, 1); -// // interpolate alpha between start and end -// alpha = a * f.alphaStart + (1 - a) * f.alphaEnd; -// break; -// } -// -// mRenderLayer.setBitmapAlpha(alpha); -// } - @Override - public void handleEvent(MapEvent event) { - super.handleEvent(event); + public void onMapUpdate(MapPosition pos, boolean changed, boolean clear) { + super.onMapUpdate(pos, changed, clear); - if (event instanceof UpdateEvent){ + if (mFade == null) { + mRenderLayer.setBitmapAlpha(1); + return; + } - if (mFade == null) { - mRenderLayer.setBitmapAlpha(1); - return; - } - MapPosition pos = mMap.getMapPosition(); + float alpha = 0; + for (FadeStep f : mFade) { + if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd) + continue; - float alpha = 0; - for (FadeStep f : mFade) { - if (pos.scale < f.scaleStart || pos.scale > f.scaleEnd) - continue; - - if (f.alphaStart == f.alphaEnd) { - alpha = f.alphaStart; - break; - } - double range = f.scaleEnd / f.scaleStart; - float a = (float)((range - (pos.scale / f.scaleStart)) / range); - a = FastMath.clamp(a, 0, 1); - // interpolate alpha between start and end - alpha = a * f.alphaStart + (1 - a) * f.alphaEnd; + if (f.alphaStart == f.alphaEnd) { + alpha = f.alphaStart; break; } - - mRenderLayer.setBitmapAlpha(alpha); + double range = f.scaleEnd / f.scaleStart; + float a = (float)((range - (pos.scale / f.scaleStart)) / range); + a = FastMath.clamp(a, 0, 1); + // interpolate alpha between start and end + alpha = a * f.alphaStart + (1 - a) * f.alphaEnd; + break; } + + mRenderLayer.setBitmapAlpha(alpha); } @Override diff --git a/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java b/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java index 5a492c67..37741a30 100644 --- a/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java +++ b/vtm/src/org/oscim/layers/tile/vector/labeling/LabelLayer.java @@ -15,15 +15,15 @@ package org.oscim.layers.tile.vector.labeling; import org.oscim.backend.Log; +import org.oscim.core.MapPosition; import org.oscim.event.EventListener; import org.oscim.event.MapEvent; import org.oscim.event.MotionEvent; -import org.oscim.event.UpdateEvent; import org.oscim.layers.Layer; import org.oscim.map.Map; import org.oscim.tiling.TileRenderer; -public class LabelLayer extends Layer implements EventListener { +public class LabelLayer extends Layer implements EventListener, Map.UpdateListener { private final static String TAG = LabelLayer.class.getName(); private final TextRenderer mTextRenderer; @@ -31,7 +31,7 @@ public class LabelLayer extends Layer implements EventListener { public LabelLayer(Map map, TileRenderer tileRenderLayer) { super(map); - map.addListener(UpdateEvent.TYPE, this); + map.addListener(MotionEvent.TYPE, this); //mTextLayer = new org.oscim.renderer.layers.TextRenderLayer(map, tileRenderLayer); @@ -41,7 +41,6 @@ public class LabelLayer extends Layer implements EventListener { @Override public void onDetach() { - mMap.removeListener(UpdateEvent.TYPE, this); mMap.removeListener(MotionEvent.TYPE, this); // TODO stop and clear labeling thread @@ -50,13 +49,7 @@ public class LabelLayer extends Layer implements EventListener { @Override public void handleEvent(MapEvent event) { - if (event instanceof UpdateEvent) { - - UpdateEvent e = (UpdateEvent) event; - if (e.clearMap) - mTextRenderer.clearLabels(); - - } else if (event instanceof MotionEvent) { + if (event instanceof MotionEvent) { MotionEvent e = (MotionEvent) event; int action = e.getAction() & MotionEvent.ACTION_MASK; @@ -75,11 +68,11 @@ public class LabelLayer extends Layer implements EventListener { } } - // @Override - // public void onUpdate(MapPosition mapPosition, boolean changed, boolean clear) { - // if (clear) - // mTextRenderer.clearLabels(); - // } + @Override + public void onMapUpdate(MapPosition mapPosition, boolean changed, boolean clear) { + if (clear) + mTextRenderer.clearLabels(); + } // @Override // public boolean onTouchEvent(MotionEvent e) { diff --git a/vtm/src/org/oscim/map/Layers.java b/vtm/src/org/oscim/map/Layers.java deleted file mode 100644 index 00bee1d5..00000000 --- a/vtm/src/org/oscim/map/Layers.java +++ /dev/null @@ -1,109 +0,0 @@ -/* - * Copyright 2012 osmdroid authors - * Copyright 2013 Hannes Janetzek - * - * This program is free software: you can redistribute it and/or modify it under the - * terms of the GNU Lesser General Public License as published by the Free Software - * Foundation, either version 3 of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, but WITHOUT ANY - * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A - * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License along with - * this program. If not, see <http://www.gnu.org/licenses/>. - */ - -package org.oscim.map; - -import java.util.AbstractList; -import java.util.concurrent.CopyOnWriteArrayList; - -import org.oscim.layers.Layer; -import org.oscim.renderer.LayerRenderer; - -public class Layers extends AbstractList<Layer> { - //private final static String TAG = Layers.class.getName(); - - private final CopyOnWriteArrayList<Layer> mLayerList; - - Layers() { - mLayerList = new CopyOnWriteArrayList<Layer>(); - } - - @Override - public synchronized Layer get(int index) { - return mLayerList.get(index); - } - - @Override - public synchronized int size() { - return mLayerList.size(); - } - - @Override - public synchronized void add(int index, Layer element) { - mLayerList.add(index, element); - mDirtyLayers = true; - } - - @Override - public synchronized Layer remove(int index) { - mDirtyLayers = true; - return mLayerList.remove(index); - } - - @Override - public synchronized Layer set(int index, Layer element) { - mDirtyLayers = true; - return mLayerList.set(index, element); - } - - private boolean mDirtyLayers; - private LayerRenderer[] mLayerRenderer; - - public LayerRenderer[] getLayerRenderer() { - if (mDirtyLayers) - updateLayers(); - - return mLayerRenderer; - } - - public void destroy() { - if (mDirtyLayers) - updateLayers(); - - for (Layer o : mLayers) - o.onDetach(); - } - - Layer[] mLayers; - - private synchronized void updateLayers() { - if (!mDirtyLayers) - return; - - mLayers = new Layer[mLayerList.size()]; - - int numRenderLayers = 0; - - for (int i = 0, n = mLayerList.size(); i < n; i++) { - Layer o = mLayerList.get(i); - - if (o.getRenderer() != null) - numRenderLayers++; - mLayers[i] = o; - } - - mLayerRenderer = new LayerRenderer[numRenderLayers]; - - for (int i = 0, cntR = 0, n = mLayerList.size(); i < n; i++) { - Layer o = mLayerList.get(i); - LayerRenderer l = o.getRenderer(); - if (l != null) - mLayerRenderer[cntR++] = l; - } - - mDirtyLayers = false; - } -} diff --git a/vtm/src/org/oscim/map/Map.java b/vtm/src/org/oscim/map/Map.java index ad28d35e..b67c55b3 100644 --- a/vtm/src/org/oscim/map/Map.java +++ b/vtm/src/org/oscim/map/Map.java @@ -14,20 +14,24 @@ */ package org.oscim.map; +import java.util.AbstractList; import java.util.ArrayList; +import java.util.concurrent.CopyOnWriteArrayList; import org.oscim.backend.Log; import org.oscim.core.BoundingBox; -import org.oscim.core.GeoPoint; import org.oscim.core.MapPosition; +import org.oscim.core.MercatorProjection; +import org.oscim.event.Dispatcher; import org.oscim.event.EventDispatcher; import org.oscim.event.EventListener; +import org.oscim.event.IListener; import org.oscim.event.MotionEvent; -import org.oscim.event.UpdateEvent; +import org.oscim.layers.Layer; import org.oscim.layers.MapEventLayer; import org.oscim.layers.tile.bitmap.BitmapTileLayer; import org.oscim.layers.tile.vector.VectorTileLayer; -import org.oscim.layers.tile.vector.VectorTileLoader; +import org.oscim.renderer.LayerRenderer; import org.oscim.renderer.MapRenderer; import org.oscim.theme.IRenderTheme; import org.oscim.theme.InternalRenderTheme; @@ -94,16 +98,21 @@ public abstract class Map implements EventDispatcher { private InternalRenderTheme mCurrentTheme; + /** + * Utility function to set theme of base vector-layer and + * use map background color from theme. + */ public void setTheme(InternalRenderTheme theme) { if (mBaseLayer == null) { Log.e(TAG, "No base layer set"); throw new IllegalStateException(); } - if (mCurrentTheme == theme){ + if (mCurrentTheme == theme) { Log.d(TAG, "same theme: " + theme); return; } + IRenderTheme t = ThemeLoader.load(theme); if (t == null) { Log.e(TAG, "Invalid theme"); @@ -126,7 +135,7 @@ public abstract class Map implements EventDispatcher { * Request call to onUpdate for all layers. This function can * be called from any thread. Request will be handled on main * thread. - * + * * @param forceRedraw pass true to render next frame */ public abstract void updateMap(boolean forceRedraw); @@ -151,16 +160,21 @@ public abstract class Map implements EventDispatcher { /** * Post a task to run on a shared worker-thread. Only use for * tasks running less than a second! - * */ - public void addTask(Runnable task){ + */ + public void addTask(Runnable task) { mAsyncExecutor.post(task); } + /** + * Return screen width in pixel. + */ public abstract int getWidth(); + /** + * Return screen height in pixel. + */ public abstract int getHeight(); - /** * Request to clear all layers before rendering next frame */ @@ -169,27 +183,9 @@ public abstract class Map implements EventDispatcher { } /** - * Do not call directly! This function is run on main-loop - * before rendering a frame. + * This function is run on main-loop before rendering a frame. + * Caution: Do not call directly! */ - public void updateLayers() { - boolean changed = false; - - // get the current MapPosition - changed |= mViewport.getMapPosition(mMapPosition); - - //mLayers.onUpdate(mMapPosition, changed, mClearMap); - - UpdateEvent e = new UpdateEvent(this); - e.clearMap = mClearMap; - e.positionChanged = changed; - - for (EventListener l : mUpdateListeners) - l.handleEvent(e); - - mClearMap = false; - } - public void setDebugSettings(DebugSettings debugSettings) { mDebugSettings = debugSettings; //MapTileLoader.setDebugSettings(debugSettings); @@ -197,6 +193,9 @@ public abstract class Map implements EventDispatcher { public DebugSettings getDebugSettings() { return mDebugSettings; + + protected void updateLayers() { + mUpdateDispatcher.dispatch(); } public void setMapPosition(MapPosition mapPosition) { @@ -242,32 +241,169 @@ public abstract class Map implements EventDispatcher { return mAnimator; } - - ArrayList<EventListener> mUpdateListeners = new ArrayList<EventListener>(); ArrayList<EventListener> mMotionListeners = new ArrayList<EventListener>(); @Override public void addListener(String type, EventListener listener) { - if (type == UpdateEvent.TYPE) - mUpdateListeners.add(listener); - else if (type == MotionEvent.TYPE) + if (type == MotionEvent.TYPE) mMotionListeners.add(listener); - } + @Override public void removeListener(String type, EventListener listener) { - if (type == UpdateEvent.TYPE) - mUpdateListeners.remove(listener); - else if (type == MotionEvent.TYPE) + if (type == MotionEvent.TYPE) mMotionListeners.remove(listener); } - public MapPosition getMapPosition() { - return mMapPosition; - } - public void handleMotionEvent(MotionEvent e) { for (EventListener l : mMotionListeners) l.handleEvent(e); } + + /** + * Listener interface for map update notifications. + * + * NOTE: Layers implementing this interface they will be automatically + * registered when the layer is added to the map and unresitered when + * the layer is removed. + */ + public interface UpdateListener extends IListener { + void onMapUpdate(MapPosition mapPosition, boolean positionChanged, boolean clear); + } + + private class UpdateDispatcher extends Dispatcher<UpdateListener> { + @Override + public void dispatch() { + boolean changed = false; + + // get the current MapPosition + changed |= mViewport.getMapPosition(mMapPosition); + + for (UpdateListener l : listeners) + l.onMapUpdate(mMapPosition, changed, mClearMap); + + mClearMap = false; + } + } + + private final UpdateDispatcher mUpdateDispatcher = new UpdateDispatcher(); + + /** + * Register UpdateListener + */ + public void addUpdateListener(UpdateListener l) { + mUpdateDispatcher.addListener(l); + } + + /** + * Unregister UpdateListener + */ + public void removeUpdateListener(UpdateListener l) { + mUpdateDispatcher.removeListener(l); + } + + public final class Layers extends AbstractList<Layer> { + + private final CopyOnWriteArrayList<Layer> mLayerList; + + Layers() { + mLayerList = new CopyOnWriteArrayList<Layer>(); + } + + @Override + public synchronized Layer get(int index) { + return mLayerList.get(index); + } + + @Override + public synchronized int size() { + return mLayerList.size(); + } + + @Override + public synchronized void add(int index, Layer layer) { + if (mLayerList.contains(layer)) + throw new IllegalArgumentException("layer added twice"); + + if (layer instanceof UpdateListener) + addUpdateListener((UpdateListener) layer); + + mLayerList.add(index, layer); + mDirtyLayers = true; + } + + @Override + public synchronized Layer remove(int index) { + mDirtyLayers = true; + + Layer remove = mLayerList.remove(index); + + if (remove instanceof UpdateListener) + removeUpdateListener((UpdateListener) remove); + + return remove; + } + + @Override + public synchronized Layer set(int index, Layer layer) { + if (mLayerList.contains(layer)) + throw new IllegalArgumentException("layer added twice"); + + mDirtyLayers = true; + Layer remove = mLayerList.set(index, layer); + + if (remove instanceof UpdateListener) + removeUpdateListener((UpdateListener) remove); + + return remove; + } + + private boolean mDirtyLayers; + private LayerRenderer[] mLayerRenderer; + + public LayerRenderer[] getLayerRenderer() { + if (mDirtyLayers) + updateLayers(); + + return mLayerRenderer; + } + + public void destroy() { + if (mDirtyLayers) + updateLayers(); + + for (Layer o : mLayers) + o.onDetach(); + } + + Layer[] mLayers; + + private synchronized void updateLayers() { + if (!mDirtyLayers) + return; + + mLayers = new Layer[mLayerList.size()]; + + int numRenderLayers = 0; + + for (int i = 0, n = mLayerList.size(); i < n; i++) { + Layer o = mLayerList.get(i); + + if (o.getRenderer() != null) + numRenderLayers++; + mLayers[i] = o; + } + + mLayerRenderer = new LayerRenderer[numRenderLayers]; + + for (int i = 0, cntR = 0, n = mLayerList.size(); i < n; i++) { + Layer o = mLayerList.get(i); + LayerRenderer l = o.getRenderer(); + if (l != null) + mLayerRenderer[cntR++] = l; + } + + mDirtyLayers = false; + } + } }