【问题标题】:Awkward touch event propagation between views视图之间的尴尬触摸事件传播
【发布时间】:2011-08-02 15:32:44
【问题描述】:

我有一个充满ImageViews 的Gallery,而ImageViews 是可缩放和可翻译的。我的目标是一旦ImageView 不能再向左/向右转换,Gallery 就会滚动。所以有时ImageView 需要处理触摸事件,有时Gallery 需要处理触摸事件。我的ImageViewonTouchEvent 方法中有逻辑,当我希望切换发生时,但我得到了意想不到的结果。我会在展示我的代码后解释问题:

// PinchZoomImageView.java

@Override
public boolean onTouchEvent( MotionEvent event ) {

    Log.i( "PinchZoomImageView", "IM GETTING TOUCHED!" );

    if ( isPassThroughTouchEvent() ) {
        Log.i( "PinchZoomImageView", "IM RETURNING FALSE!" );
        return false;
    }

    getScaleDetector().onTouchEvent( event );

    switch (event.getAction() & MotionEvent.ACTION_MASK) {
        case MotionEvent.ACTION_DOWN: {
            final float x = event.getX();
            final float y = event.getY();

            setLastTouchX( x );
            setLastTouchY( y );
            setActivePointerId( event.getPointerId( 0 ) );

            break;
        }

        case MotionEvent.ACTION_MOVE: {
            final int pointerIndex = event.findPointerIndex( getActivePointerId() );
            final float x = event.getX( pointerIndex );
            final float y = event.getY( pointerIndex );

            // Only move if the ScaleGestureDetector isn't processing a gesture.
            if ( !getScaleDetector().isInProgress() ) {
                if ( isDetectMovementX() ) {
                    final float dx = x - getLastTouchX();
                    setPosX( getPosX() + dx );
                }

                if ( isDetectMovementY() ) {
                    final float dy = y - getLastTouchY();
                    setPosY( getPosY() + dy );
                }

                invalidate();
            }

            setLastTouchX( x );
            setLastTouchY( y );

            if ( isAtXBound() && !isPassThroughTouchEvent() ) {

                setPassThroughTouchEvent( true );
            }

            break;
        }

        case MotionEvent.ACTION_UP: {
            setActivePointerId( INVALID_POINTER_ID );
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            setActivePointerId( INVALID_POINTER_ID );
            break;
        }

        case MotionEvent.ACTION_POINTER_UP: {
            final int pointerIndex = ( event.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK ) >> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
            final int pointerId = event.getPointerId( pointerIndex );
            if ( pointerId == getActivePointerId() ) {
                // This was our active pointer going up. Choose a new
                // active pointer and adjust accordingly.
                final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
                setLastTouchX( event.getX( newPointerIndex ) );
                setLastTouchY( event.getY( newPointerIndex ) );
                setActivePointerId( event.getPointerId( newPointerIndex ) );
            }
            break;
        }
    }

    return true;
}

这是我的Gallery。我覆盖了onTouchEvent 只是为了显示它何时接收到触摸事件。

// SwipeGallery.java

@Override
public boolean onTouchEvent( MotionEvent event ) {

    Log.i( "SwipeGallery", "IM GETTING TOUCHED!" );
    return super.onTouchEvent( event );
}

所以当我加载活动时,我尝试从右向左滑动。传递运动事件的逻辑会立即触发,但这是我的日志输出。

08-02 10:04:47.097: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.179: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.179: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.179: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.230: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.230: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.230: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.230: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.245: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.245: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.261: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.261: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.277: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.277: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.296: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.296: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.312: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.312: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.327: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.327: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.343: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.343: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:04:47.360: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:04:47.360: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
....etc.

我第二次从右向左滑动,我得到了这个:

08-02 10:27:31.573: INFO/PinchZoomImageView(17189): IM GETTING TOUCHED!
08-02 10:27:31.573: INFO/PinchZoomImageView(17189): IM RETURNING FALSE!
08-02 10:27:31.573: INFO/SwipeGallery(17189): IM GETTING TOUCHED!
08-02 10:27:31.636: INFO/SwipeGallery(17189): IM GETTING TOUCHED!
08-02 10:27:31.636: INFO/SwipeGallery(17189): IM GETTING TOUCHED!
08-02 10:27:31.683: INFO/SwipeGallery(17189): IM GETTING TOUCHED!
08-02 10:27:31.933: INFO/SwipeGallery(17189): IM GETTING TOUCHED!
08-02 10:27:31.964: INFO/SwipeGallery(17189): IM GETTING TOUCHED!
08-02 10:27:31.999: INFO/SwipeGallery(17189): IM GETTING TOUCHED!
08-02 10:27:32.034: INFO/SwipeGallery(17189): IM GETTING TOUCHED!

这种“图像视图始终处理的第一个运动事件,画廊始终处理的第二个运动事件”的模式永远持续下去(为画廊中的每个位置制作一个新的图像视图,这就是为什么isPassThroughTouchEvent() 在第三、第五个返回 false等时间)。那么我到底错过了什么?我认为返回 false 会传播触摸事件,直到它被处理,但Gallery 第一次不会接受它,但第二次呢?这对我来说毫无意义。谁有想法?谢谢。

【问题讨论】:

  • 你在画廊上试过 clearFocus() 吗?
  • 我想保持一种自下而上的方法,ImageViewGallery 不知道它们存在也不需要。此外,在Gallery 上执行的任何操作都不会影响我的第一次滑动结果,其中Gallery 从未收到任何触摸事件。
  • 我很好奇如何解决这个问题,isPassThroughTouchEvent() 是什么样的?
  • 它只是一个布尔设置器/获取器。我确实得到了我想要实现的工作,但这远非理想......我在我的 SwipeGallery 中覆盖 onInterceptTouchEvent() 并将任何运动事件传递给自己,然后将事件传递给我的 ImageView 和 SwipeGallery 接收和对相同的MotionEvent 做出反应。这不是我正在寻找的解决方案。

标签: android android-gallery


【解决方案1】:

当视图在向下 (ACTION_DOWN) 运动事件中返回 true 时,该视图被“锁定”为触摸运动目标。这意味着它将接收到最终向上事件的后续运动事件,无论它发生在屏幕上的哪个位置(参见此thread),除非其父级wantsallowed 拦截该事件。

解释你的情况:

  1. 在第一次滑动时,您的 ImageView 处理了向下运动,使其成为运动目标(参见日志)。这意味着所有后续的运动事件都将传递给它,并且由于您的 Gallery 不会拦截事件,因此不会调用其 onTouchEvent 处理程序。

  2. 在第二次滑动时,您的 ImageView 不处理向下运动(在日志中显示为“IM GETTING TOUCH!”+“IM RETURNING FALSE!”)并将事件传递给下一个处理程序,在这种情况下,Gallery 将运行其 onTouchEvent 处理程序。默认情况下,Gallery 始终处理向下事件,将其锁定为运动目标。

【讨论】:

  • 所以我尝试通过MotionEvent.obtain(MotionEvent)dispatchTouchEvent(MotionEvent) 重新发送MotionEvent 将操作更改为ACTION_DOWN,结果相同。该事件被发送到我的 ImageView 的onTouchEvent(MotionEvent),我已经验证它是一个向下操作,它返回 false,结果与上图相同。
  • 重新发送MotionEvent不会改变任何事情。一旦视图成为运动目标(通过处理向下事件),它将被锁定为运动目标直到最终向上事件,除非父级拦截它。您需要检查的是为什么您的 isPassThroughTouchEvent() 在第二次滑动时返回 true。
  • 因为我将它设置为 true。查看机箱底部的ACTION_MOVE。布尔值在我需要的时候准确设置,所以这不是问题。当布尔值设置为 true 时,我基本上需要一种方法来释放我的图像视图对触摸事件的“锁定”。
  • 如果你想释放锁,你可以发送一个ACTION_CANCELImageView
  • 我会试试这个。感谢您迄今为止的意见。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-08-26
  • 2014-01-18
  • 2012-10-12
  • 2021-03-07
  • 1970-01-01
  • 1970-01-01
  • 2014-05-21
相关资源
最近更新 更多