Android image map. - displaying an .svg and using it as an image map (touch zones)
Asked Answered
W

3

19

I am looking for a way to display an image and have the user tap on different parts of the image to navigate and perform actions.

I was considering using an invisible color map to check which parts have been touched.

But since i also want to highlight the selected areas, i was thinking of using vectors. There is a nice library to render svg files into an image view here, but it doesn't handle touches.
Is there a library out there that does? Or any smarter way of doing this?

(I also checked out this project but it won't swallow .svg files and my vector image is far too complex to insert all the data by hand)

Windward answered 9/7, 2013 at 8:23 Comment(2)
how about creating a overlay on top of the iamge view which can handle touch and then highlight the image using coordinates?Thornberry
If basically you only need svgs when webviews are around, you're home and dry with danbrough's answer?Counterword
C
12

Interesting problem! I'm not convinced you can't use a combination of the libraries you mentioned.

What I would do is first use SVG-Android to programmatically read in your SVG file. Looking at SVG-Android, it looks like it returns the final product as a PictureDrawable, which is a subclass of Drawable.

Right after SVG-Android finishes processing the SVG graphics, I'd use ImageView.setImageDrawable to load the main ImageView with the PictureDrawable we just generated. Then I'd just use that ImageView as the base for the implementation of "Images with Clickable Areas" you linked in the original question.

Honestly, I think the hardest part will be getting SVG-Android to work correctly (I've played with it a bit, and it's a bit finicky). However, I don't think you'll have much difficulty combining the SVG-generated drawable with the clickable areas. It's a simple change in the source of the base image.

Good luck!

Coulometer answered 16/7, 2013 at 19:11 Comment(5)
You could break the SVG up to create separate images to show as overlays, and use them to catch the touches.Thyroiditis
Possibly. The only caveat is that the clickable areas would have to be rectangular or spaced apart far enough to "wrap" in an ImageView without overlapping another clickable area. It would be cool if only the non-transparent parts of the image were clickable; then you could definitely take the overlay approach. And that's probably doable if you write a little library to process the pixel you're touching. That would actually be a cool little challenge, although maybe not immediately practical.Coulometer
Shouldn't calling getPixel(event.getX(),event.getY()) and checking the alpha value of sampled color do the trick? (in the OnTochListener) My only concern with that is, that with about 20 touch zones i would create some serious overdraw... Cutting each image up into "bonding-box-sized" squares to reduce overdraw might work, but im positioning all of those pieces with all the differet screen densities... headache :)Windward
For sure. :) I still hold to my original answer. I just thought the alternative would be an interesting little side project. ;)Coulometer
@Coulometer nice explanation. I have a question related to creating SVG format image from the view in Android. Can you please let me know if you have any idea... I want to generate SVG format image from the data i have in Android. So what could be the work around i need to do ?Unbelt
D
11

Not sure if this is the sort of thing you are after but here is an example of click enabled svg in a Android WebView:

WebView webView = (WebView) findViewById(R.id.webView1);
webView.getSettings().setJavaScriptEnabled(true);
webView.loadUrl("http://www.w3.org/TR/SVG/images/script/script01.svg");

It should show a red circle that you can click on and the circle changes size.

Here is the same example reworked with the svg loaded from the assets folder and an android javascript interface exposed so you do callbacks into android code from your svg file.

  WebView webView;

  public class WebAppInterface {
    /** Show a toast from svg */
    @JavascriptInterface
    public void showToast(String toast) {
      Toast.makeText(MainActivity.this, toast, Toast.LENGTH_SHORT).show();
    }
  }

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    webView = (WebView) findViewById(R.id.webView1);
    webView.getSettings().setJavaScriptEnabled(true);
    webView.addJavascriptInterface(new WebAppInterface(), "Android");

    String svg = loadSvg();
    webView.loadData(svg, "image/svg+xml", "utf-8");
  }

  String loadSvg() {
    try {
      BufferedReader input = new BufferedReader(new InputStreamReader(
          getAssets().open("test.svg")));
      StringBuffer buf = new StringBuffer();
      String s = null;
      while ((s = input.readLine()) != null) {
        buf.append(s);
        buf.append('\n');
      }
      input.close();
      return buf.toString();
    } catch (IOException ex) {
      ex.printStackTrace();
    }
    return null;
  }

and the test.svg file:

<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" 
  "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="6cm" height="5cm" viewBox="0 0 600 500"
     xmlns="http://www.w3.org/2000/svg" version="1.1">
  <desc>Example script01 - invoke an ECMAScript function from an onclick event
  </desc>
  <!-- ECMAScript to change the radius with each click -->
  <script type="application/ecmascript"> <![CDATA[
    function circle_click(evt) {
        Android.showToast("Hello from SVG");
      var circle = evt.target;
      var currentRadius = circle.getAttribute("r");
      if (currentRadius == 100)
        circle.setAttribute("r", currentRadius*2);
      else
        circle.setAttribute("r", currentRadius*0.5);
    }
  ]]> </script>

  <!-- Outline the drawing area with a blue line -->
  <rect x="1" y="1" width="598" height="498" fill="none" stroke="blue"/>

  <!-- Act on each click event -->
  <circle onclick="circle_click(evt)" cx="300" cy="225" r="100"
          fill="red"/>

  <text x="300" y="480" 
        font-family="Verdana" font-size="35" text-anchor="middle">
    Click on circle to change its size
  </text>
</svg>
Director answered 16/7, 2013 at 13:36 Comment(3)
Nice creative workaround! I guess the con is having to pay for the expensive overhead of using a webview.Counterword
yeah, it has potential. You can pass java primitives back to the javascript too. Shame you can't call directly into the javascript.Director
Here is a tutorial on a svg based map with zoom and pan controls: petercollingridge.co.uk/interactive-svg-components/…Director
R
1

It's very very simple ;-)

You must create an javascript interface to handle click from web view and add this file to your web view. Just flow the step:

1) Create JavaScript Interface

public class WebViewInterface {
    @JavascriptInterface
    public void showDetail(String content) {
        Toast.makeText(getActivity(), content, Toast.LENGTH_SHORT).show();
    }
}

2) Add interface to web view

WebView webView = findViewById(R.id.web_view);
webView.getSettings().setJavaScriptEnabled(true);
webView.addJavascriptInterface(new WebViewInterface(), "Android");

3) Edit svg file same as this one

<?xml version="1.0" standalone="no"?>
<svg width="10cm" height="10cm" viewBox="0 0 600 500"
    xmlns="http://www.w3.org/2000/svg" version="1.1">
    <desc>Sample SVG</desc>
    <script type="application/ecmascript"> <![CDATA[
    function section_click(evt) {
        Android.showDetail("OnClick: " + evt.id);
    }
  ]]> </script>

    <rect id="rect" onclick="section_click(this)" x="1" y="1" width="598" height="498" fill="none" stroke="blue"/>

    <circle id="circle" onclick="section_click(this)" cx="300" cy="225" r="100"
        fill="red"/>

    <text id="text" onclick="section_click(this)" x="300" y="480"
        font-family="Verdana" font-size="35" text-anchor="middle">
        Click on each element
    </text>
</svg>
Riflery answered 28/4, 2018 at 22:35 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.