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.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 Context context;
@ -51,6 +53,8 @@ public class BackendMap {
private final OSciMap4TileSource tileSource;
private final TileCache cache;
private final ItemizedLayer<MarkerItem> items;
private java.util.Map<String, Markup> markupMap = new HashMap<>();
private boolean redrawPosted = false;
public BackendMap(Context context) {
this.context = context;
@ -65,7 +69,8 @@ public class BackendMap {
layers.add(buildings = new BuildingLayer(mapView.map(), baseLayer));
layers.add(new LabelLayer(mapView.map(), baseLayer));
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);
}
@ -138,9 +143,10 @@ public class BackendMap {
mapView.map().animator().cancel();
}
public <T extends Markup> T add(T markup) {
public synchronized <T extends Markup> T add(T markup) {
switch (markup.getType()) {
case MARKER:
markupMap.put(markup.getId(), markup);
items.addItem(markup.getMarkerItem(context));
redraw();
break;
@ -157,14 +163,16 @@ public class BackendMap {
return markup;
}
public void clear() {
public synchronized void clear() {
markupMap.clear();
items.removeAllItems();
redraw();
}
public void remove(Markup markup) {
public synchronized void remove(Markup markup) {
switch (markup.getType()) {
case MARKER:
markupMap.remove(markup.getId());
items.removeItem(items.getByUid(markup.getId()));
redraw();
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()) {
case MARKER:
// TODO: keep order
@ -188,4 +200,20 @@ public class BackendMap {
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 circleCounter = 0;
private IOnMarkerClickListener onMarkerClickListener;
public GoogleMapImpl(LayoutInflater inflater, GoogleMapOptions options) {
context = inflater.getContext();
@ -212,6 +213,8 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub
@Override
public void clear() throws RemoteException {
backendMap.clear();
circleCounter = 0;
markerCounter = 0;
}
@Override
@ -223,6 +226,22 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub
public void remove(Markup 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
@ -321,7 +340,7 @@ public class GoogleMapImpl extends IGoogleMapDelegate.Stub
@Override
public void setOnMarkerClickListener(IOnMarkerClickListener listener) throws RemoteException {
this.onMarkerClickListener = listener;
}
@Override

View File

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

View File

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

View File

@ -19,36 +19,87 @@ package org.microg.gms.maps.bitmap;
import android.content.Context;
import android.graphics.Bitmap;
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.dynamic.ObjectWrapper;
import org.microg.gms.maps.ResourcesContainer;
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() {
this(0);
}
private final float hue;
public DefaultBitmapDescriptor(float hue) {
this.hue = hue;
this.hue = hue > 180 ? -DEGREES + hue : hue;
}
@Override
public Bitmap generateBitmap(Context context) {
Bitmap source = BitmapFactory
.decodeResource(ResourcesContainer.get(), R.drawable.maps_default_marker);
Bitmap bitmap = Bitmap
.createBitmap(source.getWidth(), source.getHeight(), source.getConfig());
float[] hsv = new float[3];
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));
}
Bitmap source;
if (this == DEFAULT_DESCRIPTOR) {
source = BitmapFactory.decodeResource(ResourcesContainer.get(), R.drawable.maps_default_marker);
} else {
source = DEFAULT_DESCRIPTOR.loadBitmap(context);
}
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;
}
/**
* 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 Point point;
private float drawRadius;
private boolean removed = false;
public CircleImpl(String id, CircleOptions options, MarkupListener listener) {
this.id = id;
@ -65,6 +66,9 @@ public class CircleImpl extends ICircleDelegate.Stub implements Markup {
@Override
public void remove() throws RemoteException {
listener.remove(this);
removed = true;
layer.setEnabled(false);
layer = null;
}
@Override
@ -158,6 +162,16 @@ public class CircleImpl extends ICircleDelegate.Stub implements Markup {
return id.hashCode();
}
@Override
public boolean onClick() {
return false;
}
@Override
public boolean isValid() {
return !removed;
}
@Override
public MarkerItem getMarkerItem(Context context) {
return null;

View File

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

View File

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