Android:立即强制UI刷新

时间:2016-07-18 15:59:43

标签: java android multithreading

我正在尝试构建一个包含倒计时器的Android应用程序,并且应该在每个间隔中显示三个图像一段时间 - 一个200毫秒的掩蔽图像,一个20毫秒的激励图像和另一个再次的掩蔽图像200毫秒。

该应用程序使用两个线程 - 主UI线程和时间管理线程。

问题是,UI线程不刷新自身,因为它在两者之间休眠,因此不会显示任何图像。

我已经搜索了很多时间来找到一种强制UI线程立即刷新自己的方法,但直到现在我都没有成功。 例如,invalidate()或postinvalidate()方法不会做任何有用的事情。

如果有人提出这个问题的提示或解决方案,那就太好了。

感谢您的帮助。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {

    Button button;
    ImageView imageView;
    TextView textViewCounter;

    boolean buttonWasPressed = false;
    double startTime;
    double currentTime;
    double timer;
    final int INTERVALS = 2;

    final double SECONDS_TO_NANOSECONDS_COEFFICIENT = 1000000000.0;
    // length of the interval in seconds
    final double INTERVAL_LENGTH = 10 * SECONDS_TO_NANOSECONDS_COEFFICIENT;
    int intervalCounter = 0;

    // masking time in milliseconds (200)
    final double MASKING_TIME = 0.2 * SECONDS_TO_NANOSECONDS_COEFFICIENT;
    // stimulus time in milliseconds (20)
    final double STIMULUS_TIME = 0.02 * SECONDS_TO_NANOSECONDS_COEFFICIENT;

    boolean stimuliShouldBeDisplayed = false;
    boolean stimuliIsDisplayed = false;
    boolean imageViewShouldBeCleared = false;

    Handler handler;

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

        button = (Button) this.findViewById(R.id.button);

        if (button != null) {
            button.setOnClickListener(this);
        }

        imageView = (ImageView) this.findViewById(R.id.imageView);
        textViewCounter = (TextView) this.findViewById(R.id.textViewCounter);

        // messages are sent to the thread where the Handler was created
        handler = new Handler() {
            @Override
            public void handleMessage(Message msg) {

                // not sure if you must always clear the message queue
                this.removeMessages(0);

                double tempStartTime = System.nanoTime();

                // milliseconds are okay
                textViewCounter.setText(String.valueOf(timer / 1000000) + "  ms");

                if (stimuliShouldBeDisplayed && !stimuliIsDisplayed) {

                    stimuliIsDisplayed = true;

                    // show mask
                    imageView.setImageResource(R.mipmap.mask);

                    try {
                        Thread.sleep((long) (MASKING_TIME / 1000000));
                    } catch (InterruptedException e) {
                        e.printStackTrace(

                    // update our timer (milliseconds are okay)
                    timer += System.nanoTime() - tempStartTime;
                    textViewCounter.setText(String.valueOf(timer / 1000000) + "  ms");

                    // show stimulus
                    imageView.setImageResource(R.mipmap.stimulus);

                    try {
                        Thread.sleep((long) (STIMULUS_TIME / 1000000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    // update our timer (milliseconds are okay)
                    timer += System.nanoTime() - tempStartTime;
                    textViewCounter.setText(String.valueOf(timer / 1000000) + "  ms");

                    // show mask
                    imageView.setImageResource(R.mipmap.mask);

                    try {
                        Thread.sleep((long) (MASKING_TIME / 1000000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }

                    // update our timer (milliseconds are okay)
                    timer += System.nanoTime() - tempStartTime;
                    textViewCounter.setText(String.valueOf(timer / 1000000) + "  ms");
            }

                // clear the imageView
                if (imageViewShouldBeCleared) {
                    imageView.setImageResource(0);
                    imageViewShouldBeCleared = false;
                    stimuliIsDisplayed = false;
                    stimuliShouldBeDisplayed = false;
                }
            }
        };
    }

    @Override
    public void onClick(View v) {

        if (v == button && !buttonWasPressed) {

            buttonWasPressed = true;

            // let's start our timer
            startTime = System.nanoTime();

            Runnable runnableTimeManagement = new Runnable() {
                @Override
                public void run() {

                    while (currentTime - startTime <= INTERVAL_LENGTH && intervalCounter < INTERVALS) {

                        currentTime = System.nanoTime();

                        timer = currentTime - startTime;

                        // next interval
                        if (timer > INTERVAL_LENGTH) {

                            intervalCounter++;
                            startTime = currentTime;
                            imageViewShouldBeCleared = true;
                        }

                        // 1 seconds extra for the communication time between TimeManagement Thread and GUI Thread
                        if (timer + SECONDS_TO_NANOSECONDS_COEFFICIENT >= INTERVAL_LENGTH - 2 * MASKING_TIME - STIMULUS_TIME) {

                            stimuliShouldBeDisplayed = true;
                        }

                        // we must always create a new empty message
                        Message message = Message.obtain();
                        // we send message to the main UI thread
                        handler.sendMessage(message);

                        try {
                            Thread.sleep(5);
                        } catch (InterruptedException e) {
                            e.printStackTrace();
                        }
                    }

                    // time is over
                    buttonWasPressed = false;
                    intervalCounter = 0;
                }
            };

            new Thread(runnableTimeManagement).start();
        }
    }
}

有人知道以另一种方式精确控制图像显示时间的方法吗?最佳解决方案是仅显示一帧的刺激图像。但我不知道如何访问帧速率。

是否有可能强制UI线程立即刷新自己?

1 个答案:

答案 0 :(得分:0)

Thread.sleep通常不是一个好主意。而且我不确定你需要一个二级线程来描述你所描述的内容。您可以简单地使用Handler.sendMessageDelayed进行UI更新:

// will run as soon as possible (almost immediately)
handler.sendMessage(handler.obtainMessage(0, R.mimap.mask, 0));
// will run in 200ms
handler.sendMessageDelayed(handler.obtainMessage(0, R.mimap.stimulus, 0), 200);
// will run in 220ms
handler.sendMessageDelayed(handler.obtainMessage(0, R.mimap.mask, 0), 220);

然后你的处理程序只显示它在消息的参数中收到的图像:

@Override
public void handleMessage(Message msg) {
    imageView.setResource(msg.arg1);
}

但请记住,您并未完全控制刷新率,因此无法保证图像显示的时间恰好为20毫秒