执行前取消一个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);