Add marker onclick support, optimize drawing

This commit is contained in:
mar-v-in 2015-02-21 20:16:13 +01:00
parent 3cdf785533
commit 5b9503275e
8 changed files with 205 additions and 41 deletions

View File

@ -41,7 +41,9 @@ import org.oscim.map.Viewport;
import org.oscim.theme.VtmThemes; import org.oscim.theme.VtmThemes;
import org.oscim.tiling.source.oscimap4.OSciMap4TileSource; import org.oscim.tiling.source.oscimap4.OSciMap4TileSource;
public class BackendMap { import java.util.HashMap;
public class BackendMap implements ItemizedLayer.OnItemGestureListener<MarkerItem> {
private final static String TAG = "GmsBackendMap"; private final static String TAG = "GmsBackendMap";
private final Context context; private final Context context;
@ -51,6 +53,8 @@ public class BackendMap {
private final OSciMap4TileSource tileSource; private final OSciMap4TileSource tileSource;
private final TileCache cache; private final TileCache cache;
private final ItemizedLayer<MarkerItem> items; private final ItemizedLayer<MarkerItem> items;
private java.util.Map<String, Markup> markupMap = new HashMap<>();
private boolean redrawPosted = false;
public BackendMap(Context context) { public BackendMap(Context context) {
this.context = context; this.context = context;
@ -65,7 +69,8 @@ public class BackendMap {
layers.add(buildings = new BuildingLayer(mapView.map(), baseLayer)); layers.add(buildings = new BuildingLayer(mapView.map(), baseLayer));
layers.add(new LabelLayer(mapView.map(), baseLayer)); layers.add(new LabelLayer(mapView.map(), baseLayer));
layers.add(items = new ItemizedLayer<>(mapView.map(), new MarkerSymbol(new AndroidBitmap(BitmapFactory layers.add(items = new ItemizedLayer<>(mapView.map(), new MarkerSymbol(new AndroidBitmap(BitmapFactory
.decodeResource(ResourcesContainer.get(), R.drawable.maps_default_marker)), 0.5F, 1))); .decodeResource(ResourcesContainer.get(), R.drawable.nop)), 0.5F, 1)));
items.setOnItemGestureListener(this);
mapView.map().setTheme(VtmThemes.DEFAULT); mapView.map().setTheme(VtmThemes.DEFAULT);
} }
@ -138,9 +143,10 @@ public class BackendMap {
mapView.map().animator().cancel(); mapView.map().animator().cancel();
} }
public <T extends Markup> T add(T markup) { public synchronized <T extends Markup> T add(T markup) {
switch (markup.getType()) { switch (markup.getType()) {
case MARKER: case MARKER:
markupMap.put(markup.getId(), markup);
items.addItem(markup.getMarkerItem(context)); items.addItem(markup.getMarkerItem(context));
redraw(); redraw();
break; break;
@ -157,14 +163,16 @@ public class BackendMap {
return markup; return markup;
} }
public void clear() { public synchronized void clear() {
markupMap.clear();
items.removeAllItems(); items.removeAllItems();
redraw(); redraw();
} }
public void remove(Markup markup) { public synchronized void remove(Markup markup) {
switch (markup.getType()) { switch (markup.getType()) {
case MARKER: case MARKER:
markupMap.remove(markup.getId());
items.removeItem(items.getByUid(markup.getId())); items.removeItem(items.getByUid(markup.getId()));
redraw(); redraw();
break; break;
@ -173,7 +181,11 @@ public class BackendMap {
} }
} }
public void update(Markup markup) { public synchronized void update(Markup markup) {
if (markup == null || !markup.isValid()) {
Log.d(TAG, "Tried to update() invalid markup!");
return;
}
switch (markup.getType()) { switch (markup.getType()) {
case MARKER: case MARKER:
// TODO: keep order // TODO: keep order
@ -188,4 +200,20 @@ public class BackendMap {
Log.d(TAG, "Unknown markup: " + markup); Log.d(TAG, "Unknown markup: " + markup);
} }
} }
@Override
public boolean onItemSingleTapUp(int index, MarkerItem item) {
Markup markup = markupMap.get(item.getUid());
if (markup != null) {
if (markup.onClick()) return true;
}
return false;
}
@Override
public boolean onItemLongPress(int index, MarkerItem item) {
// TODO: start drag+drop
Log.d(TAG, "onItemLongPress: " + markupMap.get(item.getUid()));
return false;
}
} }

View File

@ -80,6 +80,7 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub
private int markerCounter = 0; private int markerCounter = 0;
private int circleCounter = 0; private int circleCounter = 0;
private IOnMarkerClickListener onMarkerClickListener;
public GoogleMapImpl(LayoutInflater inflater, GoogleMapOptions options) { public GoogleMapImpl(LayoutInflater inflater, GoogleMapOptions options) {
context = inflater.getContext(); context = inflater.getContext();
@ -212,6 +213,8 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub
@Override @Override
public void clear() throws RemoteException { public void clear() throws RemoteException {
backendMap.clear(); backendMap.clear();
circleCounter = 0;
markerCounter = 0;
} }
@Override @Override
@ -224,6 +227,22 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub
backendMap.remove(markup); backendMap.remove(markup);
} }
@Override
public boolean onClick(Markup markup) {
if (markup instanceof IMarkerDelegate) {
if (onMarkerClickListener != null) {
try {
if (onMarkerClickListener.onMarkerClick((IMarkerDelegate) markup))
return true;
} catch (RemoteException e) {
Log.w(TAG, e);
}
}
// TODO: open InfoWindow
}
return false;
}
/* /*
Map options Map options
*/ */
@ -321,7 +340,7 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub
@Override @Override
public void setOnMarkerClickListener(IOnMarkerClickListener listener) throws RemoteException { public void setOnMarkerClickListener(IOnMarkerClickListener listener) throws RemoteException {
this.onMarkerClickListener = listener;
} }
@Override @Override

View File

@ -41,7 +41,7 @@ public class BitmapDescriptorFactoryImpl extends IBitmapDescriptorFactoryDelegat
@Override @Override
public IObjectWrapper defaultMarker() throws RemoteException { public IObjectWrapper defaultMarker() throws RemoteException {
return ObjectWrapper.wrap(new DefaultBitmapDescriptor()); return ObjectWrapper.wrap(DefaultBitmapDescriptor.DEFAULT_DESCRIPTOR);
} }
@Override @Override

View File

@ -19,13 +19,18 @@ package org.microg.gms.maps.bitmap;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.util.Log; import android.util.Log;
import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.dynamic.ObjectWrapper; import com.google.android.gms.dynamic.ObjectWrapper;
import com.google.android.gms.maps.model.BitmapDescriptor; import com.google.android.gms.maps.model.BitmapDescriptor;
import java.util.HashSet;
import java.util.Set;
public class BitmapDescriptorImpl { public class BitmapDescriptorImpl {
private BitmapDescriptor descriptor; private BitmapDescriptor descriptor;
private boolean loadStarted = false; private boolean loadStarted = false;
private Set<Runnable> waitingForLoad = new HashSet<>();
public BitmapDescriptorImpl(IObjectWrapper remoteObject) { public BitmapDescriptorImpl(IObjectWrapper remoteObject) {
this(new BitmapDescriptor(remoteObject)); this(new BitmapDescriptor(remoteObject));
@ -57,9 +62,13 @@ public class BitmapDescriptorImpl {
return null; return null;
} }
public synchronized void loadBitmapAsync(final Context context, final Runnable after) { public synchronized boolean loadBitmapAsync(final Context context, Runnable after) {
if (getBitmap() != null) {
return false;
}
waitingForLoad.add(after);
if (loadStarted) if (loadStarted)
return; return true;
loadStarted = true; loadStarted = true;
if (getDescriptor() != null) { if (getDescriptor() != null) {
new Thread(new Runnable() { new Thread(new Runnable() {
@ -67,11 +76,18 @@ public class BitmapDescriptorImpl {
public void run() { public void run() {
Log.d("BitmapDescriptor", "Start loading " + getDescriptor()); Log.d("BitmapDescriptor", "Start loading " + getDescriptor());
if (getDescriptor().loadBitmap(context) != null) { if (getDescriptor().loadBitmap(context) != null) {
Set<Runnable> waitingForLoad;
synchronized (BitmapDescriptorImpl.this) {
waitingForLoad = BitmapDescriptorImpl.this.waitingForLoad;
}
for (Runnable after : waitingForLoad) {
after.run(); after.run();
} }
}
Log.d("BitmapDescriptor", "Done loading " + getDescriptor()); Log.d("BitmapDescriptor", "Done loading " + getDescriptor());
} }
}).start(); }).start();
} }
return true;
} }
} }

View File

@ -19,36 +19,87 @@ package org.microg.gms.maps.bitmap;
import android.content.Context; import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.graphics.Color; import android.graphics.Canvas;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import com.google.android.gms.R; import com.google.android.gms.R;
import com.google.android.gms.dynamic.ObjectWrapper;
import org.microg.gms.maps.ResourcesContainer; import org.microg.gms.maps.ResourcesContainer;
public class DefaultBitmapDescriptor extends AbstractBitmapDescriptor { public class DefaultBitmapDescriptor extends AbstractBitmapDescriptor {
private float hue; public static final DefaultBitmapDescriptor DEFAULT_DESCRIPTOR = new DefaultBitmapDescriptor(0);
public static final BitmapDescriptorImpl DEFAULT_DESCRIPTOR_IMPL = new BitmapDescriptorImpl(ObjectWrapper.wrap(DEFAULT_DESCRIPTOR));
public static final int DEGREES = 360;
public DefaultBitmapDescriptor() { private final float hue;
this(0);
}
public DefaultBitmapDescriptor(float hue) { public DefaultBitmapDescriptor(float hue) {
this.hue = hue; this.hue = hue > 180 ? -DEGREES + hue : hue;
} }
@Override @Override
public Bitmap generateBitmap(Context context) { public Bitmap generateBitmap(Context context) {
Bitmap source = BitmapFactory Bitmap source;
.decodeResource(ResourcesContainer.get(), R.drawable.maps_default_marker); if (this == DEFAULT_DESCRIPTOR) {
Bitmap bitmap = Bitmap source = BitmapFactory.decodeResource(ResourcesContainer.get(), R.drawable.maps_default_marker);
.createBitmap(source.getWidth(), source.getHeight(), source.getConfig()); } else {
float[] hsv = new float[3]; source = DEFAULT_DESCRIPTOR.loadBitmap(context);
for (int x = 0; x < bitmap.getWidth(); x++) {
for (int y = 0; y < bitmap.getHeight(); y++) {
int pixel = source.getPixel(x, y);
Color.colorToHSV(pixel, hsv);
hsv[0] = (hsv[0] + hue) % 360;
bitmap.setPixel(x, y, Color.HSVToColor(Color.alpha(pixel), hsv));
}
} }
if (hue % DEGREES == 0) return source;
Paint paint = new Paint();
paint.setColorFilter(adjustHue(hue));
Bitmap bitmap = Bitmap.createBitmap(source.getWidth(), source.getHeight(), source.getConfig());
Canvas canvas = new Canvas(bitmap);
canvas.drawBitmap(source, 0, 0, paint);
return bitmap; return bitmap;
} }
/**
* Creates a HUE ajustment ColorFilter
* <p/>
* see http://groups.google.com/group/android-developers/browse_thread/thread/9e215c83c3819953
* see http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html
*
* @param value degrees to shift the hue.
*/
public static ColorFilter adjustHue(float value) {
ColorMatrix cm = new ColorMatrix();
adjustHue(cm, value);
return new ColorMatrixColorFilter(cm);
}
/**
* see http://groups.google.com/group/android-developers/browse_thread/thread/9e215c83c3819953
* see http://gskinner.com/blog/archives/2007/12/colormatrix_cla.html
*/
public static void adjustHue(ColorMatrix cm, float value) {
value = cleanValue(value, 180f) / 180f * (float) Math.PI;
if (value == 0) {
return;
}
float cosVal = (float) Math.cos(value);
float sinVal = (float) Math.sin(value);
float lumR = 0.213f;
float lumG = 0.715f;
float lumB = 0.072f;
float[] mat = new float[]{lumR + cosVal * (1 - lumR) + sinVal * (-lumR),
lumG + cosVal * (-lumG) + sinVal * (-lumG),
lumB + cosVal * (-lumB) + sinVal * (1 - lumB), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (0.143f),
lumG + cosVal * (1 - lumG) + sinVal * (0.140f),
lumB + cosVal * (-lumB) + sinVal * (-0.283f), 0, 0,
lumR + cosVal * (-lumR) + sinVal * (-(1 - lumR)),
lumG + cosVal * (-lumG) + sinVal * (lumG),
lumB + cosVal * (1 - lumB) + sinVal * (lumB),
0, 0, 0f, 0f, 0f, 1f, 0f, 0f, 0f, 0f, 0f, 1f};
cm.postConcat(new ColorMatrix(mat));
}
protected static float cleanValue(float p_val, float p_limit) {
return Math.min(p_limit, Math.max(-p_limit, p_val));
}
} }

View File

@ -50,6 +50,7 @@ public class CircleImpl extends ICircleDelegate.Stub implements Markup {
private CircleLayer layer; private CircleLayer layer;
private Point point; private Point point;
private float drawRadius; private float drawRadius;
private boolean removed = false;
public CircleImpl(String id, CircleOptions options, MarkupListener listener) { public CircleImpl(String id, CircleOptions options, MarkupListener listener) {
this.id = id; this.id = id;
@ -65,6 +66,9 @@ public class CircleImpl extends ICircleDelegate.Stub implements Markup {
@Override @Override
public void remove() throws RemoteException { public void remove() throws RemoteException {
listener.remove(this); listener.remove(this);
removed = true;
layer.setEnabled(false);
layer = null;
} }
@Override @Override
@ -158,6 +162,16 @@ public class CircleImpl extends ICircleDelegate.Stub implements Markup {
return id.hashCode(); return id.hashCode();
} }
@Override
public boolean onClick() {
return false;
}
@Override
public boolean isValid() {
return !removed;
}
@Override @Override
public MarkerItem getMarkerItem(Context context) { public MarkerItem getMarkerItem(Context context) {
return null; return null;

View File

@ -20,12 +20,15 @@ import android.content.Context;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.os.RemoteException; import android.os.RemoteException;
import android.util.Log; import android.util.Log;
import com.google.android.gms.dynamic.IObjectWrapper; import com.google.android.gms.dynamic.IObjectWrapper;
import com.google.android.gms.maps.model.LatLng; import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions; import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.internal.IMarkerDelegate; import com.google.android.gms.maps.model.internal.IMarkerDelegate;
import org.microg.gms.maps.GmsMapsTypeHelper; import org.microg.gms.maps.GmsMapsTypeHelper;
import org.microg.gms.maps.bitmap.BitmapDescriptorImpl; import org.microg.gms.maps.bitmap.BitmapDescriptorImpl;
import org.microg.gms.maps.bitmap.DefaultBitmapDescriptor;
import org.oscim.android.canvas.AndroidBitmap; import org.oscim.android.canvas.AndroidBitmap;
import org.oscim.layers.Layer; import org.oscim.layers.Layer;
import org.oscim.layers.marker.MarkerItem; import org.oscim.layers.marker.MarkerItem;
@ -39,6 +42,8 @@ public class MarkerImpl extends IMarkerDelegate.Stub implements Markup {
private final MarkerOptions options; private final MarkerOptions options;
private final MarkupListener listener; private final MarkupListener listener;
private BitmapDescriptorImpl icon; private BitmapDescriptorImpl icon;
private AndroidBitmap oldBitmap;
private boolean removed = false;
public MarkerImpl(String id, MarkerOptions options, MarkupListener listener) { public MarkerImpl(String id, MarkerOptions options, MarkupListener listener) {
this.id = id; this.id = id;
@ -55,6 +60,9 @@ public class MarkerImpl extends IMarkerDelegate.Stub implements Markup {
@Override @Override
public void remove() { public void remove() {
listener.remove(this); listener.remove(this);
removed = true;
icon = null;
oldBitmap = null;
} }
@Override @Override
@ -200,27 +208,49 @@ public class MarkerImpl extends IMarkerDelegate.Stub implements Markup {
return bitmap.getHeight(); return bitmap.getHeight();
} }
@Override
public boolean onClick() {
return listener.onClick(this);
}
@Override
public boolean isValid() {
return !removed;
}
@Override @Override
public MarkerItem getMarkerItem(Context context) { public MarkerItem getMarkerItem(Context context) {
MarkerItem item = new MarkerItem(getId(), getTitle(), getSnippet(), MarkerItem item = new MarkerItem(getId(), getTitle(), getSnippet(),
GmsMapsTypeHelper.fromLatLng(getPosition())); GmsMapsTypeHelper.fromLatLng(getPosition()));
if (icon != null) { BitmapDescriptorImpl icon = this.icon;
if (icon == null)
icon = DefaultBitmapDescriptor.DEFAULT_DESCRIPTOR_IMPL;
if (icon.getBitmap() != null) { if (icon.getBitmap() != null) {
item.setMarker( oldBitmap = new AndroidBitmap(icon.getBitmap());
new MarkerSymbol(new AndroidBitmap(icon.getBitmap()), options.getAnchorU(), prepareMarkerIcon(item);
options.getAnchorV(), !options.isFlat()));
} else { } else {
icon.loadBitmapAsync(context, new Runnable() { if (!icon.loadBitmapAsync(context, new Runnable() {
@Override @Override
public void run() { public void run() {
listener.update(MarkerImpl.this); listener.update(MarkerImpl.this);
} }
}); })) {
// Was loaded since last check...
oldBitmap = new AndroidBitmap(icon.getBitmap());
prepareMarkerIcon(item);
}
// Keep old icon while loading new
if (oldBitmap != null) {
prepareMarkerIcon(item);
} }
} }
return item; return item;
} }
private void prepareMarkerIcon(MarkerItem item) {
item.setMarker(new MarkerSymbol(oldBitmap, options.getAnchorU(), options.getAnchorV(), !options.isFlat()));
}
@Override @Override
public Layer getLayer(Context context, Map map) { public Layer getLayer(Context context, Map map) {
return null; return null;

View File

@ -31,6 +31,10 @@ public interface Markup {
public String getId(); public String getId();
public boolean onClick();
public boolean isValid();
public static enum Type { public static enum Type {
MARKER, LAYER MARKER, LAYER
} }
@ -39,5 +43,7 @@ public interface Markup {
void update(Markup markup); void update(Markup markup);
void remove(Markup markup); void remove(Markup markup);
boolean onClick(Markup markup);
} }
} }