2014-05-17 31 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)