开发者

Android image with clickable areas

I 开发者_StackOverflowneed an advice how to achieve the following functionality under Android:

  • I need an image that represents something like a graph (from discrete math), with vertices and edges, where I can click every vertice or edge and fire a different action.

Please advise me how to achieve this (maybe with imagebuttons) or another approach to represent this functionality.


I was bored, so I coded up this crude example... It assumes straight edges between points.

public class App extends Activity
{
    PlotView plot;
    @Override
    public void onCreate(Bundle sis)
    {
        super.onCreate(sis);
        plot = new PlotView(this);
        setContentView(plot);
    }

    public class PlotView extends View
    {
        Paint paint1 = new Paint();
        Paint paint2 = new Paint();
        Point[] points = new Point[10];

        public PlotView(Context context)
        {
            super(context);
            paint1.setColor(Color.RED);
            paint2.setColor(Color.BLUE);
            for (int i = 0; i < points.length; i++)
            {
                points[i] = new Point();
                points[i].x = (float) (Math.random() * 320);
                points[i].y = (float) (Math.random() * 480);
            }
            Arrays.sort(points);
        }

        @Override
        protected void onDraw(Canvas canvas)
        {
            canvas.drawColor(Color.WHITE);
            for (int i = 0; i < points.length; i++)
            {
                if (i < points.length - 1)
                {
                    canvas.drawLine(points[i].x, points[i].y, points[i + 1].x, points[i + 1].y, paint2);
                }
                canvas.drawCircle(points[i].x, points[i].y, 5, paint1);             
            }
            super.onDraw(canvas);
        }

        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            switch(event.getAction())
            {
                case MotionEvent.ACTION_DOWN:
                {
                    float x = event.getX();
                    float y = event.getY();

                    int hitPoint = -1;
                    int closestLeft = -1;
                    int closestRight = -1;

                    for (int i = 0; i < points.length; i++)
                    {
                        float dx = x - points[i].x;
                        float dy = y - points[i].y;

                        if(i < points.length - 1)
                        {
                            if(points[i].x < x && x < points[i + 1].x)
                            {
                                closestLeft = i;
                                closestRight = i + 1;
                            }
                        }

                        if (Math.abs(dx) <= 16.0f && Math.abs(dy) <= 16.0f)
                        {
                            hitPoint = i;
                            break;
                        }
                    }
                    if (hitPoint != -1)
                    {
                        Toast.makeText(getContext(), "Hit Point: " + hitPoint, Toast.LENGTH_SHORT).show();                      
                    }
                    else                        
                    if(closestLeft != -1 && closestRight != -1)
                    {
                        float dx = points[closestLeft].x - points[closestRight].x;
                        float dy = points[closestLeft].y - points[closestRight].y;

                        final float u = ((x - points[closestLeft].x) * dx + (y - points[closestLeft].y) * dy) / (dx * dx + dy * dy);

                        float px = points[closestLeft].x + u * dx;
                        float py = points[closestLeft].y + u * dy;

                        if (Math.abs(x - px) <= 16.0f && Math.abs(y - py) <= 16.0f)
                        {
                            Toast.makeText(getContext(), "Hit Line Between: " + closestLeft + " & " + closestRight, Toast.LENGTH_SHORT).show();
                        }
                    }                   
                }
            }
            return super.onTouchEvent(event);
        }

        public class Point implements Comparable<Point>
        {
            float x;
            float y;
            @Override
            public int compareTo(Point other)
            {
                if (x < other.x) return -1;
                if (x > other.x) return 1;
                return 0;
            }
        }       
    }
}


I can imagine how to do this with SurfaceView:

  • create a Vertex class, which among other things, has an x,y coordinate representing where to draw the vertex. If your vertex was a png image of a circle, then the top-left x,y coordinates of the image are stored in the Vertex class.
  • Have all your verticies in a List, and iterate through and draw each vertex.
  • the edges are more complicated since they might criss-cross or curve around.
    • assuming they are straight lines, then you can have a Edge class that contains the starting x,y and ending x,y coordinates.
    • you can iterate through a List of Edges and draw the lines accordingly

In order to detect when a user clicks on them, you should override the onTouch method and check the event.rawX() and event.rawY() values to see if they match up to a Vertex or Edge class.

  • for a Vertex class, you can check if x <= event.rawX <= x + image_width and y <= event.rawY <= y + image_height

  • for an Edge, you can check if the event.rawX, event.rawY coordinates are found in the line formed by the two sets of coordinates you stored in the Edge class.

I've used a similar method to draw a set of nodes in a game. I'm not so sure how to do the edges though - the method I outline would only work if they were straight and do not criss-cross.

I am sure there is a better way to do this using openGL, but I have not used openGL before.

Hopefully you can get some ideas out of this.


I think you might be best off with a SurfaceView:

http://developer.android.com/reference/android/view/SurfaceView.html

And handling the onTouchEvent() as a whole for the surface, and mapping that to underlying entities in the image. If you're calculating the drawing the graph as you go should be easy to also create a map of tapable areas and grabbing the X and Y of the touch event to figure out if it corresponds to an element in the image.

If you literally have an image, as an already processed PNG for example, you would need some way to also carry in the touch event areas. Depends where that image comes in from.


According to android help, "drawing to a View, is your best choice when you want to draw simple graphics that do not need to change dynamically and are not part of a performance-intensive game." This is the right way to go when making a snake or a chess game, for instance. So I don't see a point in suggesting using a SurfaceView for this, it will just overcomplicate things.

For clickable areas you override public boolean onTouchEvent(MotionEvent event) where you manage x and y coordinates of the click for identifying the clicked area.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜