【问题标题】:Android image with clickable areas带有可点击区域的 Android 图像
【发布时间】:2011-02-15 20:12:11
【问题描述】:

我需要一个建议,如何在 Android 下实现以下功能:

  • 我需要一个图像来表示类似图形(来自离散数学),带有顶点和边,我可以在其中单击每个顶点或边并触发不同的操作。

请告诉我如何实现此功能(可能使用imagebuttons)或其他表示此功能的方法。

【问题讨论】:

  • 我实际上并没有按照你的建议使用它,但看看 JFreeChart 是否满足你的需要。
  • 我所说的“图”是我所说的离散数学。我需要一张可以点击的线条和结的图像。
  • clickable area of image的可能重复
  • @lukle,2011 年提出的问题怎么会与 2013 年提出的问题重复?

标签: android


【解决方案1】:

我很无聊,所以我编写了这个粗略的例子...... 它假定点之间有直边。

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;
            }
        }       
    }
}

【讨论】:

  • 非常感谢示例代码。我将在本周末之前尝试一下,并将其设为“已接受的答案”。
【解决方案2】:

我可以想象如何使用SurfaceView 做到这一点:

  • 创建一个Vertex 类,其中包括一个x,y 坐标,表示在何处绘制顶点。如果您的顶点是圆形的 png 图像,则图像的左上角 x,y 坐标存储在 Vertex 类中。
  • 将所有顶点放在List 中,然后遍历并绘制每个顶点。
  • 边缘更复杂,因为它们可能交叉或弯曲。
    • 假设它们是直线,那么您可以拥有一个包含起始 x,y 和结束 x,y 坐标的 Edge 类。
    • 您可以遍历 ListEdges 并相应地绘制线条

为了检测用户何时点击它们,您应该重写 onTouch 方法并检查 event.rawX()event.rawY() 值以查看它们是否与 Vertex 或 Edge 类匹配。

  • 对于Vertex 类,您可以检查x &lt;= event.rawX &lt;= x + image_widthy &lt;= event.rawY &lt;= y + image_height 是否存在

  • 对于Edge,您可以检查event.rawX, event.rawY 坐标是否在您存储在Edge 类中的两组坐标形成的直线中。

我使用类似的方法在游戏中绘制一组节点。不过,我不太确定如何处理边缘 - 我概述的方法只有在它们是直的并且不纵横交错的情况下才有效。

我确信使用 openGL 有更好的方法来做到这一点,但我之前没有使用过 openGL。

希望你能从中得到一些想法。

【讨论】:

    【解决方案3】:

    我认为你最好使用 SurfaceView:

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

    将 onTouchEvent() 作为一个整体处理表面,并将其映射到图像中的底层实体。如果您正在计算绘图,则应该很容易创建可点击区域的地图并抓取触摸事件的 X 和 Y 以确定它是否对应于图像中的元素。

    如果您确实有一个图像,例如已处理的 PNG,您将需要一些方法来携带触摸事件区域。取决于该图像的来源。

    【讨论】:

      【解决方案4】:

      根据 android 帮助,“绘制到 View 是您想​​要绘制不需要动态更改且不属于性能密集型游戏一部分的简单图形时的最佳选择。”例如,在制作蛇或国际象棋游戏时,这是正确的方法。所以我认为建议为此使用 SurfaceView 没有任何意义,它只会使事情变得过于复杂。

      对于可点击区域,您可以覆盖 public boolean onTouchEvent(MotionEvent event),您可以在其中管理点击的 x 和 y 坐标以识别点击区域。

      【讨论】:

        猜你喜欢
        • 2019-04-16
        • 1970-01-01
        • 2013-09-20
        • 2015-12-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多