翻译画布后的Android No Touch事件

时间:2015-06-30 18:20:28

标签: android xml user-interface canvas translate

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

因此,例如,如果我在Y位置将整个画布移动50,那么我想移动较小的视图,但必须触摸旧位置而不是偏移的位置(50)。所以看起来我没有点击现在的小视图,而是过去的位置(我希望这是有意义的)。

下一期,我移动较小的视图后。我无法再触发触摸事件。我假设这是因为它被移动到其父级之外(因为我将clipChildren设置为false,因此仍然可以查看)和它的" z-order"低于布局中的其他视图。有没有办法始终把它放在最上面?我正在使用Android API 17.即使我将这个较小的视图(我触摸时移动并触发ACTION_MOVE事件)设置为主布局中的最后一个子节点,我需要能够将它拖出这里(我can)并继续将它拖到新的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

3 个答案:

答案 0 :(得分:0)

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

答案 1 :(得分:0)

我不确定我是否理解你问题的所有内容但也许这可以帮到你 View with horizontal and vertical pan/drag and pinch-zoom您应该尝试Alex答案中的代码。代码正在做一些额外的事情,但你可以只关闭scalelistener(用于缩放)和当前的X轴平移。该代码也在移动子视图&#34;内部&#34;因此,在onTouch调用后,你不应该翻译点击位置。

答案 2 :(得分:0)

更好的解决方案是保持您的观点。然后将矩阵变换应用于视图,将反转矩阵应用于MotionEvents。通过这种方式,您可以始终正确地获取视图并无条件地委派您的事件,并且所有平移都在场景的绘制中进行。移动视图也会遇到一个基本上无法解决的问题,即你无法获得超出真实视图范围的MotionEvents。因此,如果你缩小,并按屏幕 之外的区域,它会忽略它并且不会在那里发送触摸事件。

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