Есть ли подход, который может задерживать определенные секунды между каждым вызовом функции, который должен обновлять компоненты пользовательского интерфейса?

Я пишу игру-лабиринт, и одной из ее функций является «Показать решение». Решение может содержать много шагов, я использую цикл for для повторения каждого шага, чтобы показать его игроку ясно, я хочу, чтобы он мог делать паузу в одну секунду после каждого шага. Я знаю, что операцию задержки нельзя поместить в поток пользовательского интерфейса, поэтому я попробовал несколько подходов к асинхронной задержке. К сожалению, из-за его асинхронной природы, если решение состоит из более чем одного шага, пользовательский интерфейс не может обновляться до тех пор, пока не завершится последний шаг. Это не то, что я хочу, конечно.

Я пробовал handler.postDelayed подход, new Timer().schedule подход.

    public void showSolutionClick(View view) {
        int stage = currentGame.currentStage;
        int[][] solution = GameMap.solutionList[stage];
        drawStage(stage);
        for(int[] step: solution) {
            ImageView v = (ImageView)findViewById(viewIdList[step[0]][step[1]]);
            setTimeout(() -> {v.callOnClick();}, 1);
        }
    }

    private void setTimeout(Runnable r, int seconds) {
        Handler handler = new Handler();
        handler.postDelayed(r, seconds * 1000);
    }

Возможно, мне нужен подход с синхронной задержкой, и он не должен блокировать обновление пользовательского интерфейса.

обновление: CountDownTimer подходит для этой ситуации. На самом деле, я видел ответ CountDownTime на другие вопросы, я должен был сначала попробовать этот подход, прежде чем спрашивать. Спасибо всем, кто дал ответ. Вот мой окончательный код:

    public void showSolutionClick(View view) {
        int stage = currentGame.currentStage;
        int[][] solution = GameMap.solutionList[stage];
        drawStage(stage);
        int stepTotal = solution.length;
        int stepPause = 1000;
        int timeTotal = (stepTotal + 1) * stepPause;

        new CountDownTimer(timeTotal, stepPause) {
            int stepIndex = 0;
            public void onTick(long timeRemain) {
                int[] pos = solution[stepIndex];
                ImageView v = (ImageView)findViewById(viewIdList[pos[0]][pos[1]]);
                v.callOnClick();
                stepIndex += 1;
                Log.d("time remain:", "" + timeRemain / 1000);
            }

            public void onFinish() {
                Log.d("final step", ":" + (stepIndex - 1));
            }
        }.start();
    }
0
0
51
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Попробуйте что-то вроде этого,

public void showSolution(View view) {
// Do something long
Runnable runnable = new Runnable() {
    @Override
    public void run() {
int stage = currentGame.currentStage;
    int[][] solution = GameMap.solutionList[stage];
    drawStage(stage);
    for(int[] step: solution) {
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            handler.post(new Runnable() {
                @Override
                public void run() {
                       ImageView v = (ImageView)findViewById(viewIdList[step[0]][step[1]]);
        setTimeout(() -> {v.callOnClick();}, 1);
                }
            });
        }
    }
};
new Thread(runnable).start();
}

Я тоже попробовал этот подход. К сожалению, это не работает. Он выдает незахваченное исключение «android.view.ViewRootImpl$CalledFromWrongThreadException: только исходный поток, создавший иерархию представлений, может касаться своих представлений».

shukebeta 28.05.2019 01:37
Ответ принят как подходящий

Вы можете использовать таймер обратного отсчета.

 new CountDownTimer(30000, 1000){
                    public void onTick(long millisUntilFinished){
                        doUpdateOnEveryTick()
                    }
                    public  void onFinish(){
                       doActionOnFinish()
                    }
                }.start();

Работает как часы. Я вставил свой окончательный код в свой вопрос, большое спасибо!

shukebeta 27.05.2019 22:41

Другие вопросы по теме