diff --git a/androidsvg/src/main/java/com/caverock/androidsvg/SVG.java b/androidsvg/src/main/java/com/caverock/androidsvg/SVG.java index 244c4e4..0bb5f16 100644 --- a/androidsvg/src/main/java/com/caverock/androidsvg/SVG.java +++ b/androidsvg/src/main/java/com/caverock/androidsvg/SVG.java @@ -27,6 +27,7 @@ import java.io.IOException; import java.io.InputStream; +import java.util.Collection; import java.util.Set; /** @@ -739,4 +740,8 @@ SVGBase.Svg getRootElement() { return base.getRootElement(); } + + Collection getElementsAt(float x, float y) { + return base.getElementsAt(x, y, true); + } } diff --git a/androidsvg/src/main/java/com/caverock/androidsvg/SVGImageView.java b/androidsvg/src/main/java/com/caverock/androidsvg/SVGImageView.java index 6af3851..c11ec15 100644 --- a/androidsvg/src/main/java/com/caverock/androidsvg/SVGImageView.java +++ b/androidsvg/src/main/java/com/caverock/androidsvg/SVGImageView.java @@ -20,6 +20,7 @@ import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Method; +import java.util.Collection; import android.annotation.SuppressLint; import android.content.Context; @@ -31,9 +32,12 @@ import android.os.AsyncTask; import android.util.AttributeSet; import android.util.Log; +import android.view.MotionEvent; import android.view.View; import android.widget.ImageView; +import com.caverock.androidsvg.utils.SVGBase; + /** * SVGImageView is a View widget that allows users to include SVG images in their layouts. * @@ -54,6 +58,7 @@ public class SVGImageView extends ImageView private static Method setLayerTypeMethod = null; + private SVGElementClickListener svgElementClickListener; static { try @@ -328,6 +333,102 @@ protected void onPostExecute(SVG svg) //=============================================================================================== + @Override + public boolean onTouchEvent(MotionEvent event) { + boolean handled = super.onTouchEvent(event); + + if(svgElementClickListener != null && !handled && event.getAction() == MotionEvent.ACTION_DOWN) { + ScaleType scaleType = getScaleType(); + float relativePointerX = event.getX(); + float relativePointerY = event.getY(); + + float svgWidth = this.svg.getDocumentWidth(); + float svgHeight = this.svg.getDocumentHeight(); + + float viewWidth = this.getWidth(); + float viewHeight = this.getHeight(); + + switch (scaleType) { + //TODO add support for CENTER_INSIDE, FIT_XY, MATRIX + case CENTER: + float spacingWidth = viewWidth - svgWidth; + float spacingHeight = viewHeight - svgHeight; + + relativePointerX = (event.getX() - spacingWidth/2) / (viewWidth - spacingWidth); + relativePointerY = (event.getY() - spacingHeight/2) / (viewHeight - spacingHeight); + break; + + case CENTER_CROP: + relativePointerX = event.getX() / viewWidth; + relativePointerY = event.getY() / viewHeight; + + if (svgWidth / svgHeight < 1) { + float factor = svgWidth / viewWidth; + float spacing = ((factor * viewHeight) - svgHeight) / factor; + + relativePointerY = (event.getY() - spacing / 2) / (viewHeight - spacing); + } else { + float factor = svgHeight / viewHeight; + float spacing = ((factor * viewWidth) - svgWidth) / factor; + + relativePointerX = (event.getX() - spacing / 2) / (viewWidth - spacing); + } + break; + + case FIT_START: + case FIT_END: + case FIT_CENTER: + relativePointerX = event.getX() / viewWidth; + relativePointerY = event.getY() / viewHeight; + + float spacing; + if (svgWidth / svgHeight < 1) { + float factor = svgHeight / viewHeight; + spacing = ((factor * viewWidth) - svgWidth) / factor; + } else { + float factor = svgWidth / viewWidth; + spacing = ((factor * viewHeight) - svgHeight) / factor; + } + + switch (scaleType) { + case FIT_START: + if (svgWidth / svgHeight < 1) { + relativePointerX = event.getX() / (viewWidth - spacing); + } else { + relativePointerY = event.getY() / (viewHeight - spacing); + } + break; + case FIT_END: + if (svgWidth / svgHeight < 1) { + relativePointerX = (event.getX() - spacing) / (viewWidth - spacing); + } else { + relativePointerY = (event.getY() - spacing) / (viewHeight - spacing); + } + break; + case FIT_CENTER: + if (svgWidth / svgHeight < 1) { + relativePointerX = (event.getX() - spacing / 2) / (viewWidth - spacing); + } else { + relativePointerY = (event.getY() - spacing / 2) / (viewHeight - spacing); + } + break; + } + + } + + if (relativePointerX >= 0 && relativePointerX <= 1 && + relativePointerY >= 0 && relativePointerY <= 1) { + Collection elements = this.svg.getElementsAt(relativePointerX * svgWidth, relativePointerY * svgHeight); + + for (SVGBase.SvgObject element : elements) { + svgElementClickListener.onClick(element); + handled = true; + } + } + } + + return handled; + } /* * Use reflection to call an API 11 method from this library (which is configured with a minSdkVersion of 8) @@ -358,4 +459,15 @@ private void doRender() setImageDrawable(new PictureDrawable(picture)); } + public SVGElementClickListener getSVGElementClickListener(){ + return this.svgElementClickListener; + } + + public void setSVGElementClickListener(SVGElementClickListener listener){ + this.svgElementClickListener = listener; + } + + public interface SVGElementClickListener { + void onClick(SVGBase.SvgObject elementClicked); + } } diff --git a/androidsvg/src/main/java/com/caverock/androidsvg/utils/SVGAndroidRenderer.java b/androidsvg/src/main/java/com/caverock/androidsvg/utils/SVGAndroidRenderer.java index b22f679..a3a3336 100644 --- a/androidsvg/src/main/java/com/caverock/androidsvg/utils/SVGAndroidRenderer.java +++ b/androidsvg/src/main/java/com/caverock/androidsvg/utils/SVGAndroidRenderer.java @@ -422,6 +422,10 @@ private void render(SvgObject obj) render((Text) obj); } + if(obj instanceof SvgElementBase && ((SvgElementBase) obj).id != null){ + document.putElementById(((SvgElementBase) obj).id, (SvgElementBase) obj); + } + // Restore state statePop(); } diff --git a/androidsvg/src/main/java/com/caverock/androidsvg/utils/SVGBase.java b/androidsvg/src/main/java/com/caverock/androidsvg/utils/SVGBase.java index 0869e25..079dc27 100644 --- a/androidsvg/src/main/java/com/caverock/androidsvg/utils/SVGBase.java +++ b/androidsvg/src/main/java/com/caverock/androidsvg/utils/SVGBase.java @@ -35,6 +35,7 @@ import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; +import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; @@ -2127,6 +2128,11 @@ void enumeratePath(PathInterface handler) } + SvgElementBase putElementById(String id, SvgElementBase obj) { + return idToElementMap.put(id, obj); + } + + SvgElementBase getElementById(String id) { if (id == null || id.length() == 0) @@ -2190,5 +2196,40 @@ private void getElementsByTagName(List result, SvgObject obj, String } } + public Collection getElementsAt(float x, float y, boolean withIdsOnly) + { + List result = new ArrayList<>(); + + // Search the object tree for nodes at the given coordinates + if(!withIdsOnly){ + getElementsAt(result, rootElement, x, y); + } else { + for (SvgElementBase obj: idToElementMap.values()){ + if(obj instanceof SvgElement && containsCoords((SvgElement) obj, x, y)){ + result.add((SvgElement) obj); + } + } + } + return result; + } + + private void getElementsAt(List result, SvgObject obj, float x, float y) + { + if (obj instanceof SvgContainer) + { + for (SvgObject child: ((SvgContainer) obj).getChildren()) + getElementsAt(result, child, x, y); + + } else if (obj instanceof SvgElement && containsCoords((SvgElement) obj, x, y)) { + result.add((SvgElement) obj); + } + } + + private boolean containsCoords(SvgElement elm, float x, float y){ + Box box = elm.boundingBox; + if (box != null) + return (x >= box.minX && x <= box.maxX() && y >= box.minY && y <= box.maxY()); + else return false; + } }