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

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

ただいまの
回答率

88.06%

Android java ボタンをクリックすると、Textviewを動的変更させたい

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 327

score 11

Android Studio、Android javaを使って、初めてのモバイルアプリを作成しています。

【実現したいこと】
DBに文字列が登録されている状態で(実装済み)、以下の画面があります。

イメージ説明

「再生開始」ボタンを押下すると、
登録された文字列が、先頭から5文字ずつ順番にTextviewに切り替え表示されるようにしたいです。 ユーザーがその都度ボタンを押下して切り替えるのではなく、自動的に切り替えさせたいです。

例えば、
登録されている文字列:「あああああいいいいいうううううえええええおおおおお」の場合、
ユーザーがボタンを押下

あああああ を表示

n秒後、いいいいい に切り替え表示

さらにn秒後、ううううう に切り替え表示

さらにさらにn秒後、えええええ に切り替え表示
以下続く...

【わからないこと】
「ユーザーがその都度ボタンを押下して切り替えるのではなく、 自動的に切り替えさせたい」 のですが、その実装方法がわかりません。

どのような言葉で検索すれば良いか自体がわからないため、
こんな単語で検索すれば良いですとか、このメソッドを使えば良い、
というのをご助言頂きたく思います。

よろしくお願いいたします。

参考(現在のJavaファイル)

package com.example.textapp;

import androidx.appcompat.app.AppCompatActivity;

import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;

import java.util.HashMap;
import java.util.Map;

public class ScTextPlay extends AppCompatActivity implements View.OnClickListener{

    //再生状態 1=停止状態、2=再生状態
    private int state=1;
    private Text text;
    private StringBuilder sb = new StringBuilder();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_sc_text_play);

        //「再生」ボタンのオブジェクトを取得とイベントリスナ登録
        Button playButton = (Button)findViewById(R.id.playButton);
        playButton.setOnClickListener(this);

        //リセットボタンのオブジェクトを取得し、リスナ登録する
        Button resetButtonPlay = (Button)findViewById(R.id.resetButtonPlay);
        resetButtonPlay.setOnClickListener(this);

        //インテントを取得(呼び出し元から封筒を受け取る)
        Intent intent = getIntent();

        //インテントからBundleを取得(封筒を開封する)
        Bundle bundle = intent.getExtras();

        //Bundleから「TEXT_DATA」というキーで設定しているTextオブジェクトを取得
        text = (Text)bundle.getSerializable("TEXT_DATA");

        ((TextView)findViewById(R.id.textViewPlay)).setText(text.getText());
        sb.append(text.getText());
    }

    @Override
    public void onClick(View v) {
        //押されたボタンのIDを取得
        int id = v.getId();
        Button playButton = (Button)findViewById(R.id.playButton);
        Button resetButtonPlay = (Button)findViewById(R.id.resetButtonPlay);

        switch (id) {
            case R.id.playButton: //「再生開始」ボタンを押した場合
                if(state == 1){//停止状態の場合、再生開始
                    state = 2;
                    //ボタン名変化
                    playButton.setText(getResources().getString(R.string.play));
                    //リセットボタン有効化
                    resetButtonPlay.setEnabled(true);
                    getSplitPosition(sb);

                }else{//再生状態の場合、再生停止
                    state = 1;
                    //ボタン名変化
                    playButton.setText(getResources().getString(R.string.stop));
                    //リセットボタン無効化
                    resetButtonPlay.setEnabled(false);
                }
                break;

            case R.id.resetButtonPlay: //「リセット」ボタンを押した場合

                break;
        }
    }
  • 気になる質問をクリップする

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+1

周期処理を実装するには、Handler、Timer、Thread、AsyncTaskなどいくつか方法があります(AsyncTaskは最新APIレベルで非推奨となったので用いない方が良いでしょう)。

このうち、Handlerを用いる例として、そちらのコードに載せた形でサンプルを書いてみました。

public class MainActivity extends AppCompatActivity implements View.OnClickListener {
    final Handler handler = new Handler();
    int count;
    //再生状態 1=停止状態、2=再生状態
    private int state = 1;
    boolean start = false;
    int i = 0;

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

        (この辺省略)

    }

    @Override
    public void onClick(View v) {
        //押されたボタンのIDを取得
        int id = v.getId();
        Button playButton = (Button) findViewById(R.id.playButton);
        Button resetButtonPlay = (Button) findViewById(R.id.resetButtonPlay);

        switch (id) {
            case R.id.playButton: //「再生開始」ボタンを押した場合
                if (state == 1) {//停止状態の場合、再生開始
                    state = 2;
                    //ボタン名変化
                    playButton.setText(getResources().getString(R.string.play));
                    //リセットボタン有効化
                    resetButtonPlay.setEnabled(true);
                    //getSplitPosition(sb);

                    if (!start) {
                        // 停止状態からの開始のときだけ行う処理
                        count = 500;
                        start = true;
                        handler.post(runnable); // Runnableの処理を開始する
                    }

                } else {//再生状態の場合、再生停止
                    state = 1;
                    //ボタン名変化
                    playButton.setText(getResources().getString(R.string.stop));
                    //リセットボタン無効化
                    resetButtonPlay.setEnabled(false);
                }
                break;

            case R.id.resetButtonPlay: //「リセット」ボタンを押した場合
                handler.removeCallbacks(runnable); // コールバックを除去する
                start = false;
                break;
        }
    }

    Runnable runnable = new Runnable() {
        @Override
        public void run() {
            // 再生状態の場合、カウントダウン
            if (state == 2) {
                count--;
            }

            if (count == 0) {
                /*
                  ここに5秒ごとに行う処理を記述
                 */

                count = 500; // カウンターをリセット
            }

            handler.postDelayed(this, 10); // 10ミリ秒後に自身を再実行
        }
    };
}

開始ボタンを押すと、handler.post()によって変数runnableに実装した処理が呼び出されます。この処理の末尾では、自身を10ミリ秒ごとに再呼び出しするように、handler.postDelayed()を呼び出しています。こんな感じで周期処理を実現できるでしょう。

停止状態からの開始なのか、一時停止からの再開なのかの判断が必要だったので、変数startを追加しています。

runnableの呼び出しが500回繰り返されたときに「ここに5秒ごとに行う処理を記述」というところが実行すれば、「5秒ごとに表示更新」が実現できるでしょう。一時停止したときは、runnableの周期呼び出しは止めず、countの減算だけを止めます(変数stateで判断)。周期処理を完全に除去したいときは、removeCallbacks()を呼びます。

こんな感じでもう少し肉付けすれば想定の形になるのでは。

投稿

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2020/08/23 21:28

    keicha_hrs様

    ご回答ありがとうございます!
    返信が遅くなり申し訳ありません。

    教えて頂いたハンドラで、最初の1周目を再生することができました!

    しかし、再度、再生開始ボタンを押下した時に、文字列の先頭「あああああ」からまた順次表示していきたいのですが、その挙動が変になっております。インターバルが指定のものより速くなり、かつバラバラになっています。

    ハンドラーによる周期処理では、「あああああ」~「おおおおお」まで回したあと、ボタン押下によりまた「あああああ」から表示させることはできないのでしょうか?

    以下はソースになります。
    教えて頂けると大変助かります。

    -------------------------------

    package com.example.textapp;

    import androidx.appcompat.app.AppCompatActivity;

    import android.content.Intent;
    import android.content.SharedPreferences;
    import android.graphics.Color;
    import android.os.Bundle;
    import android.os.Handler;
    import android.view.Menu;
    import android.view.MenuItem;
    import android.view.View;
    import android.widget.Button;
    import android.widget.TextView;
    import android.widget.Toast;

    import java.util.HashMap;
    import java.util.Map;

    public class ScTextPlay extends AppCompatActivity implements View.OnClickListener{

    //再生状態 1=停止状態、2=再生状態
    final Handler handler = new Handler();
    private int state=1;//状態管理用
    private Text text;
    private StringBuilder sb = new StringBuilder();
    private int repeatInterval = 1000;//1秒

    //最大字数
    private int max = 40;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_sc_text_play);

    //「再生」ボタンのオブジェクトを取得とイベントリスナ登録
    Button playButton = (Button)findViewById(R.id.playButton);
    playButton.setOnClickListener(this);

    //リセットボタンのオブジェクトを取得し、リスナ登録する
    Button resetButtonPlay = (Button)findViewById(R.id.resetButtonPlay);
    resetButtonPlay.setOnClickListener(this);

    //インテントを取得(呼び出し元から封筒を受け取る)
    Intent intent = getIntent();

    //インテントからBundleを取得(封筒を開封する)
    Bundle bundle = intent.getExtras();

    //Bundleから「TEXT_DATA」というキーで設定しているTextオブジェクトを取得
    text = (Text)bundle.getSerializable("TEXT_DATA");

    ((TextView)findViewById(R.id.textViewPlay)).setText(text.getText());
    sb.append(text.getText());
    }

    @Override
    public void onClick(View v) {
    //押されたボタンのIDを取得
    int id = v.getId();
    Button playButton = (Button)findViewById(R.id.playButton);
    Button resetButtonPlay = (Button)findViewById(R.id.resetButtonPlay);

    switch (id) {
    case R.id.playButton: //「再生開始」ボタンを押した場合

    //state:再生状態 1=停止状態、2=再生状態
    if(state == 1){//停止状態から再生状態へ
    state = 2;
    //再生中、ボタン名は、「停止」にする。
    playButton.setText(getResources().getString(R.string.stop));
    playButton.setBackgroundColor(Color.parseColor("#F40254"));
    //リセットボタン無効化
    resetButtonPlay.setEnabled(false);

    handler.post(runnable);

    }else{//再生状態から停止状態へ
    state = 1;
    //停止中、ボタン名は、「再生開始」にする
    playButton.setText(getResources().getString(R.string.play));
    playButton.setBackgroundColor(Color.parseColor("#2196F3"));
    //リセットボタン有効化
    resetButtonPlay.setEnabled(true);
    handler.removeCallbacks(runnable); // コールバックを除去する
    }
    break;

    case R.id.resetButtonPlay: //「リセット」ボタンを押した場合
    //文字列を初期状態(開始位置を先頭)に戻す。
    sb.delete(0,sb.length());
    sb.append(text.getText());
    break;
    }
    }

    Runnable runnable = new Runnable() {
    @Override
    public void run() {
    //文字がなくなるまで周期処理を実行する。
    //whileではなく、if
    if (sb.length() > 0){
    if(state == 2){
    //文字列を分割して順に表示する。
    getSplitPosition(sb);//このメソッドでsbは最終的に0になります。
    }
    }else {
    state = 1;
    //停止中、ボタン名は、「再生開始」にする
    Button playButton = (Button)findViewById(R.id.playButton);
    Button resetButtonPlay = (Button)findViewById(R.id.resetButtonPlay);
    playButton.setText(getResources().getString(R.string.play));
    //リセットボタン有効化
    resetButtonPlay.setEnabled(true);

    //文字列を初期状態に戻す。
    sb.delete(0,sb.length());
    sb.append(text.getText());
    handler.removeCallbacks(runnable);
    }

    handler.postDelayed(this, repeatInterval); //ミリ秒後に自身を再実行
    }
    };

    /*
    文字列を分割する位置を取得する
    */
    public void getSplitPosition(StringBuilder sb){
    //code
    }
    }

    ---------------------------------------------------

    キャンセル

  • 2020/08/23 21:50

    申し訳ありません。
    コメントした直後に自己解決できました。

    run()の中で、単純にreturnを書けば良いだけでした。
    ありがとうございます!!

    -------------------
    handler.removeCallbacks(runnable);

    //文字列を初期状態に戻す。
    sb.delete(0,sb.length());
    sb.append(text.getText());
    return;

    }

    handler.postDelayed(this, repeatInterval); //ミリ秒後に自身を再実行

    キャンセル

  • 2020/08/23 22:14

    解決したのなら良かったです。Handlerによる方法は手軽な反面、カウントを継続するときは自分でコールバックを再postしなければならないので、その処理を誤るとRunnableを多重呼び出ししてカウントが早まってしまったり、呼び出しが途切れてカウントが止まってしまったりします。その辺りに注意を払う必要があります。

    キャンセル

  • 2020/08/24 09:22

    ありがとうございます!助かりました。

    キャンセル

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

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

関連した質問

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