2014-05-17 27 views
0

我想在应用程序运行时更改背景(无限循环) - 但是,应用程序要么停止响应,要么完全空白。我认为这是由于使用Thread.sleep(1); - 这只会让应用程序睡眠。更新UI(通过循环)没有延迟?

我当前的代码:

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?

+0

“我猜它什么时候睡觉整个应用程序暂停,然后继续找出它需要再次停止。” - 不,这是因为您没有将主应用程序线程的控制权返回给框架,因此无法绘制您的更改。 – CommonsWare

回答

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(); 
} 
0

需要postRunnable再次执行并再次在主线程中不睡觉。类似的东西:

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);

编辑:

的情况下,你想要做的事更模块化,你可以创建自己的绘制对象,从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)