Java 理解为何在onDispatchTouchEvent()返回True后仍调用onClick()
比如说,在Android应用程序中,我们希望能够在任何时候暂时可靠地忽略所有用户触摸 根据我对堆栈溢出所做的研究,一致同意的解决方案似乎是这样的: (MainActivity.java的代码): 然而,当我们引入并快速向应用程序发送触摸事件时,很明显,猪开始在量子级别飞行——我们可以调用Java 理解为何在onDispatchTouchEvent()返回True后仍调用onClick(),java,android,user-interface,onclick,ontouch,Java,Android,User Interface,Onclick,Ontouch,比如说,在Android应用程序中,我们希望能够在任何时候暂时可靠地忽略所有用户触摸 根据我对堆栈溢出所做的研究,一致同意的解决方案似乎是这样的: (MainActivity.java的代码): 然而,当我们引入并快速向应用程序发送触摸事件时,很明显,猪开始在量子级别飞行——我们可以调用onClick(),即使在“blocktouch”设置为true之后/期间也是如此 我的问题是:为什么会这样?--这是Android的正常行为,还是我在代码中犯了错误?:) 注意:我已经排除了由触摸以外的用户输入
onClick()
,即使在“blocktouch”设置为true之后/期间也是如此
我的问题是:为什么会这样?--这是Android的正常行为,还是我在代码中犯了错误?:)强>
注意:我已经排除了由触摸以外的用户输入调用onClick()。。。通过在monkey命令中添加“-pct touch 100”
以下是我用于此测试的代码:
主要活动:
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>
如果您不希望在任何视图单击时触发onClick()
以下是需要注意的步骤
创建自定义视图组,例如:MyConstraintLayout并添加所有子视图
里面
重写onInterceptTouchEvent(MotionEvent ev)并返回其为真
注意:根据需要使用setViewsTouchable()方法,如果将参数传递为true,则所有视图都不可单击;如果为false,则视图都可单击。我不明白为什么会这样做,因为MainActivity.onDispatchTouchEvent()在我的示例中已返回true,并且存在于上游(发生时间早于)视图组的。onInterceptTouchEvent。。。但我试着测试你描述的方法,不管怎样。。。但遇到了一个问题:我不知道如何将“充气(context,R.layout.custom_view,this)”适应我的用例,其中自定义约束视图(MyConstraintLayout)将是activity_main.xml的根视图。您确定您的monkey脚本没有单击allowedButton
和notAllowedButton
?你能完全禁用它们然后再试一次吗?它可以而且确实会按设计同时单击它们。
<?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>
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;
}
}