Android 翻译后没有触摸事件 Canvas

Android No Touch Events after Translating Canvas

我在网上搜索了一个困扰我好几天的问题的答案。我正在制作一个小部件,允许用户通过在查看区域内拖动(仅围绕 Y 轴)来平移整个小部件。然后我在此处有一个较小的视图(如按钮,监听 onTouchEvent),用户也可以拖动它。这个想法是用户将向上或向下拖动此视图,如果他们想要更高或更低,他们可以平移整个小部件,然后继续向上或向下拖动视图。现在,我有两个问题。首先,在拖动主视图(整个小部件)之后,我想移动的视图仍然位于它在翻译整个 canvas 之前所在的区域。

因此,例如,如果我将整个 canvas 在 Y 位置移动 50,然后我想移动较小的视图但必须触摸旧位置而不是偏移的位置(移动 50 ).所以看起来我没有点击它现在所在的较小视图,而是点击它曾经所在的位置(我希望这是有道理的)。

下一期,在我移动较小的视图之后。我无法再触发触摸事件。我假设这是因为它已移出其父级(自从我将 clipChildren 设置为 false 以来它仍然可见)并且它的 "z-order" 低于布局中的其他视图。有没有办法始终将其置于顶部?我正在使用 Android API 17。即使我将这个较小的视图(触摸时我正在移动并且 ACTION_MOVE 事件被触发)设置为主布局中的最后一个子视图,我也需要能够将它拖出这里(我可以)并继续将它拖到新的 onTouchEvent 上。任何帮助是极大的赞赏。我在下面添加了一些布局 xml 和代码片段以提供帮助。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:clipChildren="false"
            android:clipToPadding="false">

<RelativeLayout
        android:id="@+id/main_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipChildren="false"
        android:clipToPadding="false">

    <RelativeLayout
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            android:layout_centerHorizontal="true"
            android:clipChildren="false"
            android:clipToPadding="false">

        <ImageView
                android:id="@+id/ref_image"
                android:layout_width="wrap_content"
                android:layout_height="216dp"
                android:src="@drawable/my_ref_image"
                android:layout_centerInParent="true"
                android:clipChildren="false"/>
        <RelativeLayout
                android:id="@+id/selection_view"
                style="@style/MyStyle"
                android:layout_alignTop="@id/ref_image"
                android:layout_marginTop="116dp"
                android:clipChildren="false"
                android:clipToPadding="false">
            <RelativeLayout
                    android:id="@+id/selection_area"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_centerInParent="true"
                    android:clipChildren="false"
                    android:clipToPadding="false">
                <ImageView
                        android:id="@+id/underlay_image"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:src="@drawable/translucent_image"
                        android:layout_centerInParent="true"/>
                <ImageView
                        android:id="@+id/point_area"
                        android:layout_width="wrap_content"
                        android:layout_height="wrap_content"
                        android:src="@drawable/point_image"
                        android:layout_centerInParent="true"/>
            </RelativeLayout>
        </RelativeLayout>
    </RelativeLayout>        
</RelativeLayout>

所以感兴趣的代码片段:

@Override
protected void onDraw(Canvas canvas) {
  super.onDraw(canvas);
  if (DEBUG) {
     Log.d(TAG, TAG + "::onDraw: ");
  }

  canvas.translate(0, backgroundPositionY);
}

@Override
public boolean onTouchEvent(MotionEvent ev) {
  final int action = ev.getAction();
  Log.d(TAG, TAG + "::onTouchEvent: parent touch event");
  switch (action) {
  case MotionEvent.ACTION_DOWN: {
     final float y = ev.getY();

     // Remember where we started
     lastTouchY = y;

     if (DEBUG) {
        Log.d(TAG, TAG + "::onTouchEvent: parent ACTION_DOWN");
     }
     return true;
  }

  case MotionEvent.ACTION_MOVE: {
     final float y = ev.getY();

     // Calculate the distance moved
     final float dy = y - lastTouchY;

     backgroundPositionY += dy

     // Remember this touch position for the next move event
     lastTouchY = y;

     // Invalidate to request a redraw
     invalidate();
     break;
  }
  case MotionEvent.ACTION_UP: {         
     lastTouchY = ev.getY();
  }
  }
  return false;
}
// called upon initialization (selectionArea refs view id = selection_area)
private void initPointListener() {
  this.setClipChildren(false);
  this.setClipToPadding(false);

  if (selectionArea != null) {
     selectionArea .setOnTouchListener(new OnTouchListener() {
        @Override
        public boolean onTouch(View view, MotionEvent motionEvent) {

           RelativeLayout.LayoutParams pointParams = (RelativeLayout.LayoutParams) selectionArea.getLayoutParams();
           final int action = motionEvent.getAction();
           switch (action) {
           case MotionEvent.ACTION_DOWN: {
              pointLastTouchY = motionEvent.getY();
              }
              break;
           }

           case MotionEvent.ACTION_MOVE: {
              float moveY = motionEvent.getY();
              final float dy = moveY - pointLastTouchY;
              pointPosY += dy;

              selectionArea.setY(pointPosY);
              break;
           }
           }

           return true;
        }
     });
  }
}

请注意,我已经尝试设置填充,并且我在 SO 上引用了以下内容但没有很好的结果:

How to translate the canvas and still get the touch events on the correct places

Android : click / touch event not working after canvas translate

好吧,好吧...我找到了解决方法。基本上,我的 parent 视图(小部件本身)总能看到触摸事件。但是children不能给出我上面提到的情况。因此,我所做的是跟踪我拖动的 child 视图相对于查看区域的 x 和 y 位置。然后,每当我收到触摸事件时,我都会检查我是否触摸了 child 区域。如果我碰巧在 child 位于其旧位置时触发了它的 onTouchEvent,如果它没有通过此检查,我将忽略它。它很草率,但这是我让它工作的唯一方法。现在我有一个可平移的视图和一个可拖动的视图 object

我不确定我是否理解了关于您问题的所有内容,但也许这可以帮助您 View with horizontal and vertical pan/drag and pinch-zoom 您应该尝试使用 Alex 的回答中的代码。该代码正在做一些额外的事情,但您可以关闭 scalelistener(用于缩放),当然还有 X 轴的平移。该代码还将子视图 "inside" 移动到 canvas,因此您不必在调用 onTouch 后转换点击位置。

更好的解决方案是保留您的观点。然后将矩阵变换应用于您的视图,并将倒置矩阵应用于 MotionEvents。通过这种方式,您始终可以正确地获得您的视图,并且毫不费力地委派您的事件,并且所有的平移都发生在场景的绘制中。四处移动视图也会遇到一个基本上无法解决的问题,即您无法获得在真实视图边界之外开始的 MotionEvents。因此,如果您缩小,并在屏幕 所在区域之外的区域按下某些东西,它会忽略它并且不会在那里调度触摸事件。

此外,它在某些实现上变得疯狂,我认为 galaxy tab 10,它简直是可怕的 fubar 和不可能的错误。它使视图失效与其他平板电脑略有不同,并刷新了视图的错误部分。