代码之家  ›  专栏  ›  技术社区  ›  Nerdy Bunz

理解为什么onClick()在onDispatchTouchEvent()返回True后仍被调用

  •  4
  • Nerdy Bunz  · 技术社区  · 6 年前

    比方说,在Android应用程序中,我们希望能够在任何时刻暂时可靠地忽略所有用户触摸。

    根据我对堆栈溢出和 here here ,和 here ,商定的解决方案似乎是这样的:

    // returning true should mean touches are ignored/blocked
    @Override
    public boolean dispatchTouchEvent(MotionEvent pEvent) {
    
        if (disableTouches) {
            return true;
        } else {
            return super.dispatchTouchEvent(pEvent);
        }
    
    }
    

    Android Monkey Exerciser Tool 并且以快速的速度向应用程序发送触摸事件,很明显,猪开始在量子层次上飞翔——我们可以接到呼叫 onClick() 即使在“blocktouchs”设置为true之后/期间。

    我的问题是:为什么会这样?——这是Android的正常行为,还是我的代码出错了?:)

    我已经排除了 由用户输入而不是触摸调用(因此不受 onDispatchTouchEvent()

    下面是我用于此测试的代码:

    public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    
        View rootView; // turns black when "touch rejection" is in progress
    
        View allowedButton;
        View notAllowedButton;
    
        // Used to decide whether to process touch events.
        // Set true temporarily when notAllowedButton is clicked.
        boolean touchRejectionAnimationInProgress = false;
    
        int errorCount = 0; // counting "unexpected/impossible" click calls
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_main);
    
            rootView = findViewById(R.id.rootView);
    
            allowedButton = findViewById(R.id.allowedButton);
            notAllowedButton = findViewById(R.id.notAllowedButton);
    
            allowedButton.setOnClickListener(this);
            notAllowedButton.setOnClickListener(this);
    
            allowedButton.setBackgroundColor(Color.GREEN);
            notAllowedButton.setBackgroundColor(Color.RED);
    
        }
    
        // returning true should mean touches are ignored/blocked
        @Override
        public boolean dispatchTouchEvent(MotionEvent pEvent) {
    
            if (touchRejectionAnimationInProgress) {
                Log.i("XXX", "touch rejected in dispatchTouchevent()");
                return true;
            } else {
                return super.dispatchTouchEvent(pEvent);
            }
    
        }
    
        @Override
        public void onClick(View viewThatWasClicked){
    
            Log.i("XXX", "onClick() called.  View clicked: " + viewThatWasClicked.getTag());
    
            //checking for unexpected/"impossible"(?) calls to this method
            if (touchRejectionAnimationInProgress) {
                Log.i("XXX!", "IMPOSSIBLE(?) call to onClick() detected.");
                errorCount ++;
                Log.i("XXX!", "Number of unexpected clicks: " + errorCount);
                return;
            } // else proceed...
    
            if (viewThatWasClicked == allowedButton) {
                // Irrelevant
            } else if (viewThatWasClicked == notAllowedButton) {
                // user did something that is not allowed.
                touchRejectionAnimation();
            }
    
        }
    
        // When the user clicks on something "illegal,"
        // all user input is ignored temporarily for 200 ms.
        // (arbitrary choice of duration, but smaller is better for testing)
        private void touchRejectionAnimation() {
    
            Log.i("XXX", "touchRejectionAnimation() called.");
    
            touchRejectionAnimationInProgress = true;
            rootView.setBackgroundColor(Color.BLACK);
    
            // for logging/debugging purposes...
            final String rejectionID = (new Random().nextInt() % 9999999) + "";
            Log.i("XXX", "rejection : " + rejectionID + " started.");
    
            Thread thread = new Thread(new Runnable() {
                @Override
                public void run() {
                    try { Thread.sleep(200); } catch (Exception e) {
                        Log.e("XXX", "exception in touchRejection() BG thread!");
                    }
                    runOnUiThread(new Runnable() {
                        @Override
                        public void run() {
                            Log.i("XXX", "rejection " + rejectionID + " ending");
                            rootView.setBackgroundColor(Color.WHITE);
                            touchRejectionAnimationInProgress = false;
                        }
                    });
                }
            });
    
            thread.start();
    
        }
    
    }
    

    layout.xml格式:

    <?xml version="1.0" encoding="utf-8"?>
    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:id="@+id/rootView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".MainActivity">
    
        <View
            android:id="@+id/allowedButton"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="32dp"
            android:layout_marginBottom="32dp"
            android:tag="allowedButton"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toStartOf="@+id/notAllowedButton"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent" />
    
        <View
            android:id="@+id/notAllowedButton"
            android:layout_width="0dp"
            android:layout_height="0dp"
            android:layout_marginTop="32dp"
            android:layout_marginBottom="32dp"
            android:tag="view2"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toEndOf="@+id/allowedButton"
            app:layout_constraintTop_toTopOf="parent" />
    
    </android.support.constraint.ConstraintLayout>
    
    1 回复  |  直到 6 年前
        1
  •  -1
  •   Jitesh Mohite    6 年前

    如果不想在任何视图上触发onClick(),请单击。

    创建自定义视图组例如:MyConstraintLayout并添加所有子视图

    重写onInterceptTouchEvent(MotionEvent ev)并返回true。

    public class MyConstraintLayout extends ConstraintLayout {
    
        private boolean mIsViewsTouchable;
    
        public ParentView(Context context) {
            super(context);
        }
    
        public ParentView(Context context, AttributeSet attrs) {
            super(context, attrs);
            inflate(context, R.layout.custom_view, this);
        }
    
        public ParentView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }
    
        public void setViewsTouchable(boolean isViewTouchable) {
            mIsViewsTouchable = isViewTouchable;
        }
    
        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return mIsViewsTouchable;
        }
     }