执行前取消一个runnable/handler
cancel a runnable/handler before execution
我有一个设置了 onClickListener 的 textView。当用户按住手指 7 秒然后抬起时,会弹出一个对话框,在单击肯定按钮时触发意图,如果单击否定按钮则取消操作。当用户按住 textView 的时间足够长时,我想要一个视觉提示,所以我创建了一个处理程序和一个在 7000 毫秒后触发的可运行对象,并稍微更改了根视图的背景颜色。一开始效果很好。如果用户按住 7 秒,背景会发生变化,您可以抬起手指来显示对话框。如果您取消,一切都会恢复正常,包括背景颜色。问题是如果用户在 7 秒之前抬起手指,我不知道如何取消 MotionEvent.ACTION_UP 上的处理程序。这是我的代码:
View.OnTouchListener listener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final Handler handler = new Handler();
final Runnable runnable = new Runnable(){
@Override
public void run(){
Toast.makeText(mContext, "This is working", Toast.LENGTH_SHORT).show();
mMainContainer.setBackgroundColor(getResources().getColor(R.color.alert_color));
}
};
View rootView = LayoutInflater.from(mContext).inflate(R.layout.admin_dialog, null);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// get the first touch in the series
start = System.currentTimeMillis();
handler.postDelayed(runnable, 7000);
}
if (event.getAction() == MotionEvent.ACTION_UP) {
handler.removeCallbacks(runnable);
stop += System.currentTimeMillis();
if ((stop - start) > 7000){
// reset values
start = 0;
stop = 0;
final EditText password = (EditText)rootView.findViewById(R.id.admin_password_et);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext)
.setView(rootView)
.setTitle("Edit Configuration?")
.setPositiveButton(R.string.yes_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int counter = 1;
if(password.getText().toString().equalsIgnoreCase("mq")){
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
} else {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
Toast.makeText(mContext, "Wrong password, Access denied", Toast.LENGTH_SHORT)
.show();
dialog.dismiss();
}
}
})
.setNegativeButton(R.string.no_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
dialog.dismiss();
}
});
dialog.create();
dialog.show();
} else {
// reset values
start = 0;
stop = 0;
}
}
return true;
}
};
mAppTitle.setOnTouchListener(listener);
我认为调用 handler.removeCallbacks(runnable);是答案,但没有用。就像现在一样,如果你按下 textView 并在 7 秒前松开手指,对话框不会弹出,但 7 秒后背景颜色仍会改变。我怎样才能取消那个操作?我需要以不同的方式实现吗?
您可以尝试不使用 7000 作为您的重复,您可以做一个更小的间隔,而不是做一个时间差异,所以在您的处理程序中,您检查该差异是否超过 7000 毫秒。一旦满足条件,您就执行您的操作。希望我正确理解了你的问题。
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
if (diff >7000) {
handler.removeCallbacks(runnable);
} else {
handler.postDelayed(runnable, 500);
}
}
};
我想通了!感谢大家的帮助,但我想出了我认为最好的解决方案。我在 OnTouchListener 之外创建了一个 CountDownTimer 对象。在 MotionEven.ACTION_UP 我取消了计时器。这是代码,如果它可以帮助其他人:
final CountDownTimer timer = new CountDownTimer(7000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
mMainContainer.setBackgroundColor(getResources().getColor(R.color.alert_color));
}
};
View.OnTouchListener listener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
View rootView = LayoutInflater.from(mContext).inflate(R.layout.admin_dialog, null);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// get the first touch in the series
start = System.currentTimeMillis();
timer.start();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
timer.cancel();
stop += System.currentTimeMillis();
if ((stop - start) > 7000) {
// reset values
start = 0;
stop = 0;
final EditText password = (EditText) rootView.findViewById(R.id.admin_password_et);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext)
.setView(rootView)
.setTitle("Edit Configuration?")
.setPositiveButton(R.string.yes_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int counter = 1;
if (password.getText().toString().equalsIgnoreCase("mq")) {
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
} else {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
Toast.makeText(mContext, "Wrong password, Access denied", Toast.LENGTH_SHORT)
.show();
dialog.dismiss();
}
}
})
.setNegativeButton(R.string.no_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
dialog.dismiss();
}
});
dialog.create();
dialog.show();
} else {
// reset values
start = 0;
stop = 0;
}
}
return true;
}
};
mAppTitle.setOnTouchListener(listener);
}
你的实际问题是你每次onTouch都创建了一个新的Runnable实例,所以removeCallback不会匹配你之前发布的那个,解决方案是这样的:
在方法范围之外声明 Runnable 并仅在关闭时对其进行初始化。
我还添加了 ACTION_CANCEL 作为 UP 因为这种行为可能会发生。
View.OnTouchListener listener = new View.OnTouchListener() {
Runnable runnable;
@Override
public boolean onTouch(View v, MotionEvent event) {
final Handler handler = new Handler();
View rootView = LayoutInflater.from(mContext).inflate(R.layout.admin_dialog, null);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// get the first touch in the series
start = System.currentTimeMillis();
runnable = new Runnable(){
@Override
public void run(){
Toast.makeText(mContext, "This is working", Toast.LENGTH_SHORT).show();
mMainContainer.setBackgroundColor(getResources().getColor(R.color.alert_color));
}
};
handler.postDelayed(runnable, 7000);
}
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
handler.removeCallbacks(runnable);
stop += System.currentTimeMillis();
if ((stop - start) > 7000){
// reset values
start = 0;
stop = 0;
final EditText password = (EditText)rootView.findViewById(R.id.admin_password_et);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext)
.setView(rootView)
.setTitle("Edit Configuration?")
.setPositiveButton(R.string.yes_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int counter = 1;
if(password.getText().toString().equalsIgnoreCase("mq")){
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
} else {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
Toast.makeText(mContext, "Wrong password, Access denied", Toast.LENGTH_SHORT)
.show();
dialog.dismiss();
}
}
})
.setNegativeButton(R.string.no_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
dialog.dismiss();
}
});
dialog.create();
dialog.show();
} else {
// reset values
start = 0;
stop = 0;
}
}
return true;
}
};
mAppTitle.setOnTouchListener(listener);
我有一个设置了 onClickListener 的 textView。当用户按住手指 7 秒然后抬起时,会弹出一个对话框,在单击肯定按钮时触发意图,如果单击否定按钮则取消操作。当用户按住 textView 的时间足够长时,我想要一个视觉提示,所以我创建了一个处理程序和一个在 7000 毫秒后触发的可运行对象,并稍微更改了根视图的背景颜色。一开始效果很好。如果用户按住 7 秒,背景会发生变化,您可以抬起手指来显示对话框。如果您取消,一切都会恢复正常,包括背景颜色。问题是如果用户在 7 秒之前抬起手指,我不知道如何取消 MotionEvent.ACTION_UP 上的处理程序。这是我的代码:
View.OnTouchListener listener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
final Handler handler = new Handler();
final Runnable runnable = new Runnable(){
@Override
public void run(){
Toast.makeText(mContext, "This is working", Toast.LENGTH_SHORT).show();
mMainContainer.setBackgroundColor(getResources().getColor(R.color.alert_color));
}
};
View rootView = LayoutInflater.from(mContext).inflate(R.layout.admin_dialog, null);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// get the first touch in the series
start = System.currentTimeMillis();
handler.postDelayed(runnable, 7000);
}
if (event.getAction() == MotionEvent.ACTION_UP) {
handler.removeCallbacks(runnable);
stop += System.currentTimeMillis();
if ((stop - start) > 7000){
// reset values
start = 0;
stop = 0;
final EditText password = (EditText)rootView.findViewById(R.id.admin_password_et);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext)
.setView(rootView)
.setTitle("Edit Configuration?")
.setPositiveButton(R.string.yes_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int counter = 1;
if(password.getText().toString().equalsIgnoreCase("mq")){
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
} else {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
Toast.makeText(mContext, "Wrong password, Access denied", Toast.LENGTH_SHORT)
.show();
dialog.dismiss();
}
}
})
.setNegativeButton(R.string.no_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
dialog.dismiss();
}
});
dialog.create();
dialog.show();
} else {
// reset values
start = 0;
stop = 0;
}
}
return true;
}
};
mAppTitle.setOnTouchListener(listener);
我认为调用 handler.removeCallbacks(runnable);是答案,但没有用。就像现在一样,如果你按下 textView 并在 7 秒前松开手指,对话框不会弹出,但 7 秒后背景颜色仍会改变。我怎样才能取消那个操作?我需要以不同的方式实现吗?
您可以尝试不使用 7000 作为您的重复,您可以做一个更小的间隔,而不是做一个时间差异,所以在您的处理程序中,您检查该差异是否超过 7000 毫秒。一旦满足条件,您就执行您的操作。希望我正确理解了你的问题。
Handler handler = new Handler();
Runnable runnable = new Runnable() {
@Override
public void run() {
if (diff >7000) {
handler.removeCallbacks(runnable);
} else {
handler.postDelayed(runnable, 500);
}
}
};
我想通了!感谢大家的帮助,但我想出了我认为最好的解决方案。我在 OnTouchListener 之外创建了一个 CountDownTimer 对象。在 MotionEven.ACTION_UP 我取消了计时器。这是代码,如果它可以帮助其他人:
final CountDownTimer timer = new CountDownTimer(7000, 1000) {
@Override
public void onTick(long millisUntilFinished) {
}
@Override
public void onFinish() {
mMainContainer.setBackgroundColor(getResources().getColor(R.color.alert_color));
}
};
View.OnTouchListener listener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
View rootView = LayoutInflater.from(mContext).inflate(R.layout.admin_dialog, null);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// get the first touch in the series
start = System.currentTimeMillis();
timer.start();
}
if (event.getAction() == MotionEvent.ACTION_UP) {
timer.cancel();
stop += System.currentTimeMillis();
if ((stop - start) > 7000) {
// reset values
start = 0;
stop = 0;
final EditText password = (EditText) rootView.findViewById(R.id.admin_password_et);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext)
.setView(rootView)
.setTitle("Edit Configuration?")
.setPositiveButton(R.string.yes_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int counter = 1;
if (password.getText().toString().equalsIgnoreCase("mq")) {
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
} else {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
Toast.makeText(mContext, "Wrong password, Access denied", Toast.LENGTH_SHORT)
.show();
dialog.dismiss();
}
}
})
.setNegativeButton(R.string.no_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
dialog.dismiss();
}
});
dialog.create();
dialog.show();
} else {
// reset values
start = 0;
stop = 0;
}
}
return true;
}
};
mAppTitle.setOnTouchListener(listener);
}
你的实际问题是你每次onTouch都创建了一个新的Runnable实例,所以removeCallback不会匹配你之前发布的那个,解决方案是这样的:
在方法范围之外声明 Runnable 并仅在关闭时对其进行初始化。
我还添加了 ACTION_CANCEL 作为 UP 因为这种行为可能会发生。
View.OnTouchListener listener = new View.OnTouchListener() {
Runnable runnable;
@Override
public boolean onTouch(View v, MotionEvent event) {
final Handler handler = new Handler();
View rootView = LayoutInflater.from(mContext).inflate(R.layout.admin_dialog, null);
if (event.getAction() == MotionEvent.ACTION_DOWN) {
// get the first touch in the series
start = System.currentTimeMillis();
runnable = new Runnable(){
@Override
public void run(){
Toast.makeText(mContext, "This is working", Toast.LENGTH_SHORT).show();
mMainContainer.setBackgroundColor(getResources().getColor(R.color.alert_color));
}
};
handler.postDelayed(runnable, 7000);
}
if (event.getAction() == MotionEvent.ACTION_UP || event.getAction() == MotionEvent.ACTION_CANCEL) {
handler.removeCallbacks(runnable);
stop += System.currentTimeMillis();
if ((stop - start) > 7000){
// reset values
start = 0;
stop = 0;
final EditText password = (EditText)rootView.findViewById(R.id.admin_password_et);
AlertDialog.Builder dialog = new AlertDialog.Builder(mContext)
.setView(rootView)
.setTitle("Edit Configuration?")
.setPositiveButton(R.string.yes_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
int counter = 1;
if(password.getText().toString().equalsIgnoreCase("mq")){
Intent intent = new Intent(mContext, MainActivity.class);
startActivity(intent);
} else {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
Toast.makeText(mContext, "Wrong password, Access denied", Toast.LENGTH_SHORT)
.show();
dialog.dismiss();
}
}
})
.setNegativeButton(R.string.no_button, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
mMainContainer.setBackgroundColor(getResources().getColor(android.R.color.background_light));
dialog.dismiss();
}
});
dialog.create();
dialog.show();
} else {
// reset values
start = 0;
stop = 0;
}
}
return true;
}
};
mAppTitle.setOnTouchListener(listener);