質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

ただいまの
回答率

88.04%

Object.wait()メソッドを使うと処理が続けて実行されない。

解決済

回答 3

投稿 ・編集

  • 評価
  • クリップ 0
  • VIEW 9,304

score 19

テキストビューに配列の要素であるString文字列を一つずつセットしています。
一文字ずつセットされているのが分かるようにObject.wait()メソッドを使って、一文字追加されるたびにミリ秒待つ処理を入れています。
テキストビューに配列の文字列がミリ秒毎にセットされていく筈なのですが、一時、間があった後に全て同時にテキストビューにセットされるといった動き方をします。
一体何故このような動きをするのでしょうか?
メソッドを直そうにも原因が分からず困っています。
よろしくお願いします。

public void textmessageset(){
        String str = texts[0][0].toString();
        char data[] = str.toCharArray();
        textmessage.setText("");
        for(int i = 0; i < data.length; i++){
            //ミリ秒毎にテキストビューにセット
            sleep(50);
            String a = textmessage.getText().toString();
            String tt = a.toString() + String.valueOf(data[i]).toString();
            textmessage.setText(tt.toString());
        }
    }

//ミリ秒待つメソッド
public synchronized void sleep(long msec)
    {
        try
        {
            wait(msec);
        }catch(InterruptedException e){
        }
    }
  • 気になる質問をクリップする

    クリップした質問は、後からいつでもマイページで確認できます。

    またクリップした質問に回答があった際、通知やメールを受け取ることができます。

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 過去に投稿した質問と同じ内容の質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 3

checkベストアンサー

+1

まず、Object.wait()は、synchronized内で別スレッドを待機するのに使う機能です。
スリープ機能として使うのには適していません。
Javaでスリープをする一般的な方法は、Thread.sleep(long)です。


Androidで、今回のケースのような場合は、AsyncTaskHandlerを組み合わせるのが一般的のようです。
また、ご質問とは関係ありませんが、TextViewに文字列を追加する箇所はTextView#append(String)が使えますよ。

import android.app.Activity;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.TextView;

public class MainActivity extends Activity {

    Handler handler = new Handler();

    private class MessageTask extends AsyncTask<String, Void, Integer> {

        @Override
        protected Integer doInBackground(String... args) {
            // 非UIスレッドで実行
            String[] texts = { /* ... テキストの配列 ... */ };
            for (String text : texts) {
                char[] a = text.toCharArray();
                for (int i = 0; i < a.length; i++) {
                    appendText(String.valueOf(a[i]));
                    try {
                        Thread.sleep(100L);
                    } catch (InterruptedException e) {
                        throw new RuntimeException(e);
                    }
                }
            }
            return 0;
        }

    }

    void appendText(final String text) {
        handler.post(new Runnable() { // UIスレッドで実行
            @Override
            public void run() {
                TextView view = (TextView) findViewById(R.id.textView);
                view.append(text);
            }
        });
    }

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        new MessageTask().execute();
    }

    // 以下略

}


(2015/01/16 01:03, 01:09, 01:32のコメントへの回答)

サンプルコードは、ご質問の問題に焦点を絞って単独で試せるように書いているので、細かいところは応用して使っていただければと思います。
サンプルコードの変数viewtextmessageに読み替えてください。

クラスの外にあるtextsにはMessageTaskからはMainActivity.this.textsでアクセスできると思います。(static classにしているとこれではできませんがstaticでないので)
それよりも、MainActivitygetClickedTextメソッドを作ってそれを呼ぶようにした方が良いかもしれませんね。
具体的には、こんな感じです。
// MainActivity
String getClickedText() {
    return texts[event][click].toString();
}

// MessageTask#doInBackground
String text = getClickedText();
char[] a = text.toCharArray();
for (int i = 0; i < a.length; i++) {
    appendText(String.valueOf(a[i]));
    try {
        Thread.sleep(100L);
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
}

textmessage.setTextにつきましては、サンプルコードではview.append(text)としています。textShownew MessageTask().execute()を書いてみるとどうなりますか?

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/01/16 17:43

    UIとの非同期処理は良く使いますが少し難易度が高いので、
    初心者の方には最初の難関かも知れません。
    自由に使えるようになるには時間がかかると思いますがめげずに頑張って下さい。

    素敵なアプリができると良いですね。

    キャンセル

  • 2015/01/17 00:45

    補足:KSwordOfHasteさんのSwingの例は、AndroidのUIスレッドとの非同期処理という観点と(GUIの非同期処理という点については)本質的には同じです。AndroidでないGUIをお使いの方々にもご理解いただければ幸いです。

    キャンセル

  • 2015/01/17 08:37

    有難う御座います。励みになります。

    キャンセル

+1

setTextの結果が実際にビジュアルに反映される(JTextFieldなどの表示に反映される)のはtextmessagesetメソッドの実行が終わり、イベントディスパッチャーへ戻った後に(JTextFieldが更新されたために起動される再描画制御の中で)JTextField.paintComponent()が呼ばれたタイミングになると思います。

つまりsetText()の延長上でpaintComponent()が呼ばれるわけではないので、最終的な内容しか画面上に見えないことになります。もしアニメーション的なことがしたければ、別スレッドで待ちをはさみながらsetText要求をEDTへ投げるということをする必要があると思います。

void じわじわ設定(String message) {
  new Thread() {
    @Override
    public void run() {
      try {
        for (int i = 0; i <= message.length(); i++) {
          Thread.sleep(50);
          String s = message.substring(0, i);
          SwingUtilities.invokeLater(()->textmessage.setText(s));
        }
      } catch (InterruptedException e) {}
    }
  }.start();
}

追記: 本件にObject#waitは無関係な気がするので普通にThread#sleepで書いています。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/01/16 00:08

    SwingUtilities.invokeLater(()->textmessage.setText(s));
    は何をしているところでしょうか?コンパイルエラーが出てしまうのですが。
    最初の方の説明が難しくてもう少し自分の勉強が必要に感じました。
    とりあえず、別のスレッドで実行をする方法を模索するなり回答者さんに聞くなりしてみたいと思います。
    解答有難う御座いました。

    キャンセル

  • 2015/01/16 01:04

    前提環境がandroidなのを見落としてました!自分のコメントは一般OS用のswingでの話しです。混乱させてしまい申し訳ないです。
    argiusさんのコメントを拝見するとUIスレッドとは別のスレッドでコントロールを更新するという考え方自体はandroid/swingともに共通のようですが、実装方法が違います。argiusさんのコメントを参照ください。失礼しました。

    キャンセル

  • 2015/01/16 16:15

    swingのことは分かりませんが、共通の形で実装できることを理解できれば、自分なりに工夫できたと思っています。
    そもそも、質問には、直しようがないので原因を知りたいという風に書いていますので、解答自体にはかなり大きな意義がありました。
    有難う御座いました。

    キャンセル

0

メインスレッド上でwaitしていませんか?
別スレッドでwaitしてHandlerなのでポストしてみてください。

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2015/01/15 19:37

    public void textmessageset() throws InterruptedException{
    String str = texts[event][click].toString();
    char data[] = str.toCharArray();
    textmessage.setText("");
    Thread timeWait = new Thread();
    for(int i = 0; i < data.length; i++){
    timeWait.start();
    try{
    timeWait.join();
    String a = textmessage.getText().toString();
    String tt = a.toString() + String.valueOf(data[i]).toString();
    textmessage.setText(tt.toString());
    }catch(InterruptedException e){

    }
    }

    }
    public void run(){
    sleep(50);
    }

    このような感じでやってみたのですが、上手くいきませんでした。
    この場合の別スレッドでwait()させるにはどうすればよいでしょうか?

    キャンセル

15分調べてもわからないことは、teratailで質問しよう!

  • ただいまの回答率 88.04%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

同じタグがついた質問を見る