简体   繁体   中英

View parent's onTouch method is only called when the child that's being touched is not clickable

I needed to intercept the touch events for a DialogFragment , so I overrode it's view's parent's onInterceptTouchEvent like so:

@Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        ViewGroup viewGroup = new FrameLayout(GlobalState.getContext()){
            @Override
            public boolean onInterceptTouchEvent(MotionEvent ev) {
                mDecorView.onTouchEvent(ev);
                return super.onInterceptTouchEvent(ev);
            }
        };
        View view = inflater.inflate(R.layout.enter_number_dialog_layout,container);
        viewGroup.addView(view);
        return viewGroup;
    }

As you can see, I call mDecorView.onTouchEvent whenever the fragment is touched. However, the problem is, even though I know interceptOnTouchEvent is always called when the fragment's layout is touched -- placed a breakpoint on the mDecorView.onTouchEvent line -- mDecorView 's OnTouchListener.onTouch is only executed when the TextView is touched, but not when anything inside the GridLayout is; which are the only two direct children. I know this because I placed a breakpoint on the first line of code in my implementation of onTouch , and it's always called when the TextView is touched, but not when the GridLayout is.

Here is my fragment's layout:

    <TextView
        android:id="@+id/number_text_view"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:gravity="center"
        android:hint="$"
        android:singleLine="true"
        android:textSize="40sp"/>

    <GridLayout
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:columnCount="3"
        android:orientation="horizontal">

        <Button
            android:id="@+id/the_1_button"
            android:background="@android:color/white"
            android:text="1"/>

        <Button
            android:id="@+id/the_2_button"
            android:background="@android:color/white"
            android:text="2"/>

        <Button
            android:id="@+id/the_3_button"
            android:background="@android:color/white"
            android:text="3"/>

        <Button
            android:id="@+id/the_4_button"
            android:background="@android:color/white"
            android:text="4"/>

        <Button
            android:id="@+id/the_5_button"
            android:background="@android:color/white"
            android:text="5"/>

        <Button
            android:id="@+id/the_6_button"
            android:background="@android:color/white"
            android:text="6"/>

        <Button
            android:id="@+id/the_7_button"
            android:background="@android:color/white"
            android:text="7"/>

        <Button
            android:id="@+id/the_8_button"
            android:background="@android:color/white"
            android:text="8"/>

        <Button
            android:id="@+id/the_9_button"
            android:background="@android:color/white"
            android:text="9"/>

        <ImageButton
            android:id="@+id/the_backspace_button"
            style="@style/Widget.AppCompat.Button"
            android:background="@android:color/white"
            android:src="@drawable/ic_backspace"/>

        <Button
            android:id="@+id/the_0_button"
            android:background="@android:color/white"
            android:text="0"/>

        <Button
            android:id="@+id/the_done_button"
            android:background="@android:color/white"
            android:text="done"/>
    </GridLayout>

</LinearLayout>

Any idea why mDecorView 's OnTouchListener.onTouch is not being called when the GridLayout is touched?

Update:

Here is the body of mDecorView 's OnTouchListener 's onTouch() :

@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
    // offset because the view is translated during swipe
    motionEvent.offsetLocation(mTranslationX, 0);

    if (mViewWidth < 2) {
        mViewWidth = mView.getWidth();
    }

    switch (motionEvent.getActionMasked()) {
        case MotionEvent.ACTION_DOWN: {
            // TODO: ensure this is a finger, and set a flag
            mDownX = motionEvent.getRawX();
            mDownY = motionEvent.getRawY();
            if (mCallbacks.canDismiss(mToken)) {
                mVelocityTracker = VelocityTracker.obtain();
                mVelocityTracker.addMovement(motionEvent);
            }
            break;
        }

        case MotionEvent.ACTION_UP: {
            if (mVelocityTracker == null) {
                break;
            }

            float deltaX = motionEvent.getRawX() - mDownX;
            mVelocityTracker.addMovement(motionEvent);
            mVelocityTracker.computeCurrentVelocity(1000);
            float velocityX = mVelocityTracker.getXVelocity();
            float absVelocityX = Math.abs(velocityX);
            float absVelocityY = Math.abs(mVelocityTracker.getYVelocity());
            boolean dismiss = false;
            boolean dismissRight = false;
            if (Math.abs(deltaX) > mViewWidth / 2 && mSwiping) {
                dismiss = true;
                dismissRight = deltaX > 0;
            } else if (mMinFlingVelocity <= absVelocityX && absVelocityX <= mMaxFlingVelocity
                    && absVelocityY < absVelocityX
                    && absVelocityY < absVelocityX && mSwiping) {
                // dismiss only if flinging in the same direction as dragging
                dismiss = (velocityX < 0) == (deltaX < 0);
                dismissRight = mVelocityTracker.getXVelocity() > 0;
            }
            if (dismiss) {
                // dismiss
                mView.animate()
                        .translationX(dismissRight ? mViewWidth : -mViewWidth)
                        .alpha(0)
                        .setDuration(mAnimationTime)
                        .setListener(new AnimatorListenerAdapter() {
                            @Override
                            public void onAnimationEnd(Animator animation) {
                                performDismiss();
                            }
                        });
                return true;
            } else if (mSwiping) {
                // cancel
                mView.animate()
                        .translationX(0)
                        .alpha(1)
                        .setDuration(mAnimationTime)
                        .setListener(null);
            }
            mVelocityTracker.recycle();
            mVelocityTracker = null;
            mTranslationX = 0;
            mDownX = 0;
            mDownY = 0;
            mSwiping = false;
            break;
        }

        case MotionEvent.ACTION_CANCEL: {
            if (mVelocityTracker == null) {
                break;
            }

            mView.animate()
                    .translationX(0)
                    .alpha(1)
                    .setDuration(mAnimationTime)
                    .setListener(null);
            mVelocityTracker.recycle();
            mVelocityTracker = null;
            mTranslationX = 0;
            mDownX = 0;
            mDownY = 0;
            mSwiping = false;
            break;
        }

        case MotionEvent.ACTION_MOVE: {
            if (mVelocityTracker == null) {
                break;
            }

            mVelocityTracker.addMovement(motionEvent);
            float deltaX = motionEvent.getRawX() - mDownX;
            float deltaY = motionEvent.getRawY() - mDownY;
            if (Math.abs(deltaX) > mSlop && Math.abs(deltaY) < Math.abs(deltaX) / 2) {
                mSwiping = true;
                mSwipingSlop = (deltaX > 0 ? mSlop : -mSlop);
                mView.getParent().requestDisallowInterceptTouchEvent(true);

                // Cancel listview's touch
                MotionEvent cancelEvent = MotionEvent.obtain(motionEvent);
                cancelEvent.setAction(MotionEvent.ACTION_CANCEL |
                        (motionEvent.getActionIndex() <<
                                MotionEvent.ACTION_POINTER_INDEX_SHIFT));
                mView.onTouchEvent(cancelEvent);
                cancelEvent.recycle();
            }

            if (mSwiping) {
                mTranslationX = deltaX;
                mView.setTranslationX(deltaX - mSwipingSlop);
                // TODO: use an ease-out interpolator or such
                mView.setAlpha(Math.max(0f, Math.min(1f,
                        1f - 2f * Math.abs(deltaX) / mViewWidth)));
                return true;
            }
            break;
        }
    }
    return false;
}

Callback methods are designed to be called by the event handling system, not by your own code. When events share functionality, it is best to put the common code in a method which can be called by any callback method. This is preferable to calling a callback directly yourself because you have more control. You know exactly when the method will execute and you can accept only parameters which are meaningful to the task at hand.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM