【问题标题】:Android No Touch Events after Translating Canvas翻译 Canvas 后的 Android 无触摸事件
【发布时间】:2016-07-17 20:30:50
【问题描述】:

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

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

下一个问题,在我移动较小的视图之后。我无法再触发触摸事件。我假设那是因为它被移到了它的父级之外(由于我将 clipChildren 设置为 false,它仍然可见)并且它的“z-order”低于布局中的其他视图。有没有办法总是把它放在首位?我正在使用 Android API 17。即使我将这个较小的视图(触摸时移动并触发 ACTION_MOVE 事件)设置为主布局中的最后一个子视图,我也需要能够将其拖出此处(我可以)并继续将它拖到一个新的onTouchEvent上。任何帮助是极大的赞赏。我在下面添加了一些布局 xml 和代码 sn-ps 以提供帮助。

<?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>

所以感兴趣的代码的sn-ps:

@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

【问题讨论】:

    标签: android xml user-interface canvas translate


    【解决方案1】:

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

    【讨论】:

      【解决方案2】:

      我不确定我是否正确理解了您的问题,但也许这可以帮助您 View with horizontal and vertical pan/drag and pinch-zoom 你应该试试亚历克斯回答中的代码。该代码正在做一些额外的事情,但您可以关闭 scalelistener(用于缩放),当然还有平移 X 轴。该代码还在画布“内部”移动子视图,因此您不必在调用 onTouch 后翻译点击位置。

      【讨论】:

        【解决方案3】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-03
          • 2011-08-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-11-06
          相关资源
          最近更新 更多