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 和不可能的错误。它使视图失效与其他平板电脑略有不同,并刷新了视图的错误部分。
我在网上搜索了一个困扰我好几天的问题的答案。我正在制作一个小部件,允许用户通过在查看区域内拖动(仅围绕 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 和不可能的错误。它使视图失效与其他平板电脑略有不同,并刷新了视图的错误部分。