如何在Android Studio中正确使用postDelayed()?

51

我有一个倒计时器,如果用户在第12秒内没有点击游戏按钮,我想调用gameOver方法。

问题是当倒计时器为12时,游戏功能要么立即被调用,要么计时器继续倒计时。

因此,我尝试使用postDelayed()方法来给用户一秒钟的时间来点击按钮并让倒计时器继续,但是按照我的代码,游戏无论如何都会在12停止。

import android.app.Activity;
import android.os.CountDownTimer;
import android.os.Handler;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;

public class GameScreen extends Activity {
    private TextView time;
    private Button start;
    private Button cancel;
    private Button gameButton;
    private CountDownTimer countDownTimer;
    public static int count = 0;
    public static int countFail = 0;

    final Handler handler = new Handler();
    final Runnable r = new Runnable() {
        public void run() {
            handler.postDelayed(this, 1000);
            gameOver();
        }
    };

    private View.OnClickListener btnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            switch(v.getId()){
                case R.id.start_ID :
                    start();
                    break;
                case R.id.cancel :
                    cancel();
                    break;
                case R.id.gameButton_ID :
                    gameButton();
                    break;
            }
        }
    };


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_game_screen);

        start = (Button) findViewById(R.id.start_ID);
        start.setOnClickListener(btnClickListener);
        cancel = (Button) findViewById(R.id.cancel);
        cancel.setOnClickListener(btnClickListener);
        time = (TextView) findViewById(R.id.time);
        gameButton = (Button) findViewById(R.id.gameButton_ID);
        gameButton.setOnClickListener(btnClickListener);
    }

    public void start() {
        time.setText("16");
        //This doesn't work and makes app crash when you hit start button
        countDownTimer = new CountDownTimer(16 * 1000, 1000) {
            @Override
            public void onTick(long millsUntilFinished) {
                time.setText("" + millsUntilFinished / 1000);

                //turns textview string to int
                int foo = Integer.parseInt(time.getText().toString());

                if (time.getText().equals("12")) {
                    r.run();
                }
            }

            public void onFinish() {
                time.setText("Done !");
            }
        };
        countDownTimer.start();
    }

    private void cancel() {
        if(countDownTimer != null){
            countDownTimer.cancel();
            countDownTimer = null;
        }
    }

    private void gameOver() {
        Toast.makeText(getApplicationContext(), "You scored " + count, Toast.LENGTH_SHORT).show();
        count = 0;
        countFail = 0;
        cancel();
    }

    private void gameButton() {
        int foo = Integer.parseInt(time.getText().toString());

        if(foo  % 2 == 0 ) {
            Toast.makeText(getApplicationContext(), "PASS", Toast.LENGTH_SHORT).show();
            handler.removeCallbacks(r);
            ++count;
        }
        else {
            gameOver();
        }
    }
}
5个回答

87

你几乎正确地使用了postDelayed(Runnable, long),但还差一点。让我们来看一下你的Runnable。

final Runnable r = new Runnable() {
    public void run() {
        handler.postDelayed(this, 1000);
        gameOver();
    }
};

当我们调用 r.run(); 时,它会首先告诉您的 handler 在1000毫秒后运行相同的Runnable,然后调用gameOver()。实际上,这将导致您的gameOver()方法被调用两次:一次是立即调用,另一次是等待Handler等待1000毫秒后再次调用。

相反,您应该将您的Runnable更改为以下内容:

final Runnable r = new Runnable() {
    public void run() {
        gameOver();
    }
};

并且这样调用:

handler.postDelayed(r, 1000);

@DanielLaneDC说:“在1000毫秒后运行相同的Runnable处理程序”,但是Runnable还包括gameover()方法,每次执行run方法时都应该执行该方法。 - Shivanshu
2
gameOver() 不是每秒钟被调用无限次,而是被调用两次吗?看起来像是无限递归。 - starriet

30

以下是我使用的代码,它能够与被接受的答案产生相同的效果,但编写和理解起来更加简单。

final Handler handler = new Handler();
handler.postDelayed(new Runnable() {
    @Override
    public void run() {
        // Write whatever to want to do after delay specified (1 sec)
        Log.d("Handler", "Running Handler");
    }
}, 1000);

8
Thread(Runnable {
    // background work here ... 
    Handler(Looper.getMainLooper()).postDelayed(Runnable {
        // Update UI here ...
    }, 10000) // It will wait 10 sec before updating UI
}).start()

4

使用无参处理程序构造函数已经过时,而不使用lambda表达式也会使代码看起来笨重,因此,以下是更现代化的用法:

final Runnable _r_ = new Runnable(){...};

Handler handler = new Handler(Looper.getMainLooper());
handler.postDelayed(() -> _r_.run(), 666);

你也可以更新它以使用方法引用

handler.postDelayed(_r_::run(), 666);

或者更简单,只需要一个限定词

handler.postDelayed(_r_, 666);

0
看到这个例子,应该很容易:
Handler().postDelayed({
    val intent = Intent(this, MainActivity::class.java)
    startActivity(intent)
    finish()
}, 4000) // delaying for 4 seconds...

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接