2016-07-18 14 views
0

私はカウントダウンタイマーを含むアンドロイドアプリを構築しようとしています。各インターバルで3つの画像を表示する必要があります - 200 msのマスキング画像、20 msの刺激画像再び200msの間、別のマスキング画像を生成する。Android:強制的に最新のUIを更新する

このアプリは、メインUIスレッドと時間管理スレッドの2つのスレッドで動作します。

問題は、その間にスリープして画像が表示されないため、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(); 
     } 
    } 
} 

画像の表示時間を別の方法で正確に制御する方法を知っている人はいますか?最良の解決策は、1つのフレームだけの刺激画像を表示することです。しかし、私はフレームレートにアクセスする方法を知らない。

私はすぐにUIスレッドを強制的にリフレッシュする可能性がありますか?

+1

ほとんどの携帯電話には、リフレッシュレートが固定されています。参照してください:http://stackoverflow.com/questions/3966468/controlling-display-refresh-rate-on-android-device –

+0

私の知る限り、ほとんどの携帯電話は60 Hzのリフレッシュレートを持って、それは1のレート/ 60秒= 16.7ms。 – MBot

+0

はい固定リフレッシュレートの倍数(16.7 msまたは33.4 ms)で画像が表示されます。許容範囲内であれば、CanvasまたはOpenGL ESで達成可能であることを確認してください。 –

答えて

0

Thread.sleep通常はお勧めできません。そして、私はあなたが何を記述するための二次スレッドが必要かわからない。

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

そして、あなたのハンドラが唯一それがメッセージの引数で受信した画像を表示します:あなたは、単に将来的にUIの更新が発生するため Handler.sendMessageDelayedを使用することができます

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

しかし、その心に留めておきますあなたは正確にリフレッシュレートを制御しないので、画像が正確に20ms表示されることを保証するものではありません。

+0

あなたの助けに感謝ザビエル。残念ながら、画像は正確に20ミリ秒表示されることが重要です。 – MBot

関連する問題