【问题标题】:Detecting touches on Views drawn on SurfaceView检测对 SurfaceView 上绘制的视图的触摸
【发布时间】:2013-06-10 23:10:40
【问题描述】:

我正在开发一款游戏并遇到了一些麻烦,由于我是 Android 图形方面的新手,所以我可能做得不对。

我有一个SurfaceView 和一个我自己的Card 对象的ArrayList,它扩展了View。我覆盖Card 对象的onDraw 方法,然后在SurfaceView's onDraw 中绘制所有内容。绘图部分正常工作。

我现在尝试使用onTouchListener 检测单独的卡片何时被触摸,我为每张卡片设置了侦听器,但它检测到触摸就好像被触摸的视图是SurfaceView。可能我的整个想法都是错误的,所以我征求你的意见。

一些代码:

public GameSurfaceView(Context context) {
    super(context);
    GAME_STATE = GameState.LOADING;
    this.context = context;
    holder = getHolder();
    holder.addCallback(new SurfaceHolder.Callback() {

        @Override
        public void surfaceDestroyed(SurfaceHolder holder) {
        }

        @Override
        public void surfaceCreated(SurfaceHolder holder) {
            Canvas c = holder.lockCanvas(null);
            onDraw(c);
            holder.unlockCanvasAndPost(c);
        }

        @Override
        public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        }
    });
}

SurfaceView onDraw():

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawRGB(30, 180, 30);
    for (Card card : user.getTableCards()) {
        card.draw(canvas);
    }
}

卡片的 onDraw():

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    canvas.drawBitmap(getBitmap(), x, y, null);
}

和onTouch

@Override
public boolean onTouch(View v, MotionEvent event) {
    Log.d("TOUCH", "Class: " + v.getClass().toString());
    if (v.getClass() == Card.class) {
        Log.d("CARD", "Touched: " + ((Card) v).getValue());
    }
    return true;
}

我总是在记录 SurfaceView 类。 这甚至是做类似事情的正确方法吗?我需要检测对同一类的单个对象的触摸..

我目前通过向每张卡片添加Rect 类型的成员然后循环并检查触摸的 x 和 y 是否包含在该矩形中来做到这一点,但这似乎是一种昂贵的方式,迭代每个卡(即使最多 52 个)并检查它..

谢谢

【问题讨论】:

    标签: java android graphics surfaceview


    【解决方案1】:

    您混淆了两个不同的概念,我将在 A) 和 B) 中进行解释。

    你有两个选择:

    A) 如果您想继续使用 SurfaceView(这可能是游戏的正确选择,因为它可以让您更好地控制事物的绘制方式),您的 Card 对象应该扩展 Drawable,而不是 View。 Drawable 是一个愚蠢的类,您只需将其绘制到画布或传递给小部件作为它们的背景等。Android 不需要了解更多关于它的信息。在这种情况下,您需要检查自己被击中的卡片,就像您描述的那样。 Android 无法为您处理这个问题。您使用 View 而不是 Drawable 来执行此操作,这是错误的!或者至少是巨大的浪费。请参阅下文,了解如何正确使用视图。

    B) 使用视图,它将为您处理触摸事件和许多其他事情。

    View 是一个复杂的类,应该存在于 android 的视图层次结构中。你使用它的方式是丢掉它的大部分功能——当你将视图直接绘制到画布上时,android 根本不知道视图,所以它无法处理触摸事件。

    正确的基于视图的卡片实现可能如下所示:使用视图组作为基础,这就是您在 setContentView() 中设置的内容。这个 ViewGroup 可能只是一个 LinearLayout,或者如果你想在你的卡片上设置任意位置和大小,你可以使用 AbsoluteView,尽管这已被弃用。

    然后使用 addView() 将您的卡片添加到 ViewGroup。 Card 视图在其中的位置由传递给 addView() 的 LayoutParams 对象控制。这样 android 就会知道它的存在,并且会像这样处理触摸事件:

    • 触摸事件首先进入您的 ViewGroup
    • ViewGroup onTouchEvent 会将事件向下发送到其子级 onTouchEvent(已通过 addView() 添加)。因此,您只需在 Card 类中覆盖此方法。您天生就知道调用了哪个 Card 的 onTouchEvent,因为它是在对象本身上调用的。
    • 在 Card 的 onTouchEvent 中,您还可以通过返回 true 让父级知道您已经处理了触摸事件。如果您不这样做,则父级可以改为处理该事件。

    非常清楚:您没有将视图添加到 SurfaceView,事实上,您不能,因为它不是 ViewGroup。它只是一个复杂的 View 对象,允许您在其中进行任意绘图操作。但它始终是 View 层次结构的一个叶子,并且只能处理它自己的 onTouchEvents,它不会将它发送给孩子,因为它不能有任何。

    【讨论】:

      【解决方案2】:

      您在 SurfaceView 上绘制卡片,就像在墙上绘制图像一样,当您触摸图像时,您基本上是在触摸墙,图像现在是墙的一部分,您无法将它们分开。

      这是一个可能有帮助的想法:

      您有一个扩展 View 的 Card,为什么不直接使用 RelativeLayout 而不是 SurfaceView 并将您的卡片添加到它(可能并排)。这样,您就可以为每张卡片注册一个具体的视图,并为每个卡片注册一个 OnClickListener 并处理您的触摸事件...

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-07-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-09-30
        • 2013-05-08
        相关资源
        最近更新 更多