更新UI(通过循环)没有延迟?

时间:2014-05-17 15:53:31

标签: java android user-interface

我希望在应用运行时(无限循环)更改应用的背景 - 然而,应用停止响应或完全空白。我认为这是由于使用Thread.sleep(1); - 这只会让应用程序进入睡眠状态。

我目前的代码:

    runOnUiThread(new Runnable(){
        @SuppressWarnings("deprecation")
        @Override
        public void run(){
            GradientDrawable gd = new GradientDrawable(Orientation.TL_BR, green);
            gd.setGradientType(GradientDrawable.RADIAL_GRADIENT);
            gd.setGradientCenter(0.5f, 1.25f);
            gd.setCornerRadius(0f);
            for(int x = 0; x < 400; x++){
                Thread.sleep(20);
                gd.setGradientRadius((float) 400 - x);
                findViewById(R.id.ms_layout).setBackgroundDrawable(gd);
            }
        }
    });

在for循环中,我想要更改渐变的半径,然后更新背景,但是,这不起作用:我猜测它何时睡眠整个应用程序停止然后恢复,发现它需要再次停止。

我该如何解决这个问题? 换句话说,如何在不中断应用程序主线程的情况下使用循环更新UI?

3 个答案:

答案 0 :(得分:0)

您不应该在UI线程上调用Thread.sleep()。正如你所说,它冻结了用户界面。你应该在另一个线程中做很长的工作(比如Thread.sleep()),然后在UI线程上更新UI。

我做的一个应用程序的例子:

private void startProgressThread() {
    new Thread(new Runnable() {

        @Override
        public void run() {
            while (!isPaused) {

                // Thread.sleep() is called off the UI thread
                try {
                    Thread.sleep(500);
                }
                catch (InterruptedException e) {
                    e.printStackTrace();
                }

                // The progress bar is then updated on the UI thread
                progressBar.post(new Runnable() {

                    @Override
                    public void run() {
                        int time = service.getTimeElapsed();
                        progressBar.setProgress(time);
                        elapsedTime.setText(millisToMinutes(time));                            
                    }

                });

            }
        }

    }).start();
}

答案 1 :(得分:0)

绝对不要在主线程上使用sleep!你需要使用一个计时器,但是有一个问题,你只能从主线程修改UI,所以你需要使用Runnable,如下所述:http://www.techsono.com/consult/update-android-gui-timer/

答案 2 :(得分:0)

你需要post Runnable在主线程中一次又一次地执行,而不是让它睡觉。类似的东西:

GradientDrawable gd;
Handler uiHandler;
private View view;
private void start(){
            // create the drawable
            gd = new GradientDrawable(Orientation.TL_BR, green);
            gd.setGradientType(GradientDrawable.RADIAL_GRADIENT);
            gd.setGradientCenter(0.5f, 1.25f);
            gd.setCornerRadius(0f);

            // put it on the view
            view = findViewById(R.id.ms_layout);
            view.setBackgroundDrawable(gd);

            // get a handler for the main thread looper and run
            uiHandler = new Handler(Looper.getMainLooper());
            uiHandler.post(myRun);
}


private Runnable myRun = new Runnable(){

        int x = 0;
        @SuppressWarnings("deprecation")
        @Override
        public void run(){
            for(int x = 0; x < 400; x++){
                // do the change
                gd.setGradientRadius((float) 400 - x);
                // ask system to re-draw it
                gd.invalidateSelf();
                // update n re-post loop
                x++;
                if(x < 400)
                  uiHandler.post(myRun);
            }
        }
    };

ps.1:这一切都是在没有测试的情况下完成的,你必须自己调整细节。

ps.2:你也可以像postDelayed

一样使用uiHandler.postDelayed(myRun, 20);

修改

如果你想做一些更模块化的事情,你可以创建自己的Drawable,从GradientDrawable扩展并使用setLevel(int)开始动画。我一直这样做,像这样:

public class MyCustomDrawable extends GradientDrawable{

 // put the same constructors GradientDrawable uses.


    private int x;
    protected boolean onLevelChange (int level){
       x = 0;
       invalidateSelf(); // this will make the thing call draw
    }

    public void draw (Canvas canvas){

        // adjust what to draw
        setGradientRadius((float) 400 - x);

        // let the GradientDrawable do its normal drawing thing
        super.draw(canvas);

        // and then check if it needs another update
        x++;
        if(x < getLevel())
            invalidateSelf();
    }
}

将其放在视图的背景中,并在每次要动画时调用gd.setLevel(400);

这种方法的优点在于你可以自由调整它,例如,根据你喜欢的时间,4级将是4秒,然后你在onLevelChange期间进行数学运算long animationEndTime = System.currentTimeMilis() + (level * 1000)draw期间,您调用invalidateSelf()if(System.currentTimeMilis() < animationEndTime)