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

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

新規登録して質問してみよう
ただいま回答率
85.44%
Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

Q&A

解決済

2回答

7446閲覧

長い処理中にくるくるプログレスバーを表示させたいが挙動がおかしい(Android Studio Kotlin)

mukuc

総合スコア1

Android

Androidは、Google社が開発したスマートフォンやタブレットなど携帯端末向けのプラットフォームです。 カーネル・ミドルウェア・ユーザーインターフェイス・ウェブブラウザ・電話帳などのアプリケーションやソフトウェアをひとつにまとめて構成。 カーネル・ライブラリ・ランタイムはほとんどがC言語/C++、アプリケーションなどはJavaSEのサブセットとAndroid環境で書かれています。

Kotlin

Kotlinは、ジェットブレインズ社のアンドリー・ブレスラフ、ドミトリー・ジェメロフが開発した、 静的型付けのオブジェクト指向プログラミング言語です。

0グッド

0クリップ

投稿2021/03/25 23:12

前提・実現したいこと

お世話になります。

Android StudioとKotlinを使って
長い処理中にProgressBarStyleを表示させるプログラムを作りたいです。

  1. ボタンを押す
  2. くるくる回るProgressBarStyleを表示
  3. 長い処理
  4. ProgressBarStyleを非表示

このようなイメージです。

発生している問題

色々試してみましたが
・ProgressBarStyleが表示されない
・処理が終わった後にProgressBarStyleが表示される

このような状況になってしまい、実現したいことにたどり着いていません。

どうすればよいか教えてくださいませ。

該当のソースコード

MainActivity.kt

package com.example.progressbarstyletest import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.widget.ProgressBar import com.example.progressbarstyletest.databinding.ActivityMainBinding class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.button.setOnClickListener{ binding.progressBar.visibility = ProgressBar.VISIBLE //プログレスバー表示 nagaisyori() binding.progressBar.visibility = ProgressBar.INVISIBLE //プログレスバー非表示 } } fun nagaisyori() { //長い処理 var sum = 0.0 for(i in 0..1000000000) { sum += i } binding.textView1.setText("処理が終わりました") } }

activity_main.xml

<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <Button android:id="@+id/button" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Button" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:layout_constraintVertical_bias="0.72" /> <ProgressBar android:id="@+id/progressBar" style="?android:attr/progressBarStyle" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" android:visibility="invisible"/> <TextView android:id="@+id/textView1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="TextView" app:layout_constraintBottom_toTopOf="@+id/progressBar" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout>

試したこと

binding.progressBar.visibility = ProgressBar.VISIBLE
によりプログレスバーの表示が出来ることは確認しました

以下のようにすると、プログレスバーが表示されてないように見える。

binding.progressBar.visibility = ProgressBar.VISIBLE //プログレスバー表示 nagaisyori() binding.progressBar.visibility = ProgressBar.INVISIBLE //プログレスバー非表示

以下のようにしてみた所、なぜか長い処理が終わった後にプログレスバーがくるくる回り始めてしまう。

binding.progressBar.visibility = ProgressBar.VISIBLE //プログレスバー表示 nagaisyori()

WEBの記事を見つけて以下のような記述をしてみたけどそもそもエラーになってしまう。引数が空のnagaisyori()に入れようとしてるのでエラーが出るのは理解できます。

binding.progressBar.visibility = ProgressBar.VISIBLE nagaisyori{ binding.progressBar.visibility = ProgressBar.INVISIBLE }

「コールバックを使え」と書いてありましたが理解できませんでした。

海外のstackoverflowに、「そもそも同じスレッドだから実現できない。ASyncTaskを使え」という記述を見かけました。
またや「コルーチン」のヒントになりそうな単語が出てきて調べていますが、理解が出来ていない状態です。

progressBarという用意されたViewの一般的な使い方だと思うのでそんなに難しいコードは書かないだろうと思って、
かれこれ半日さまよっていまして‥よろしくお願いいたします。m(__)m

補足情報(FW/ツールのバージョンなど)

Windows 10 10.0
Android Studio 4.1.3
Build #AI-201.8743.12.41.7199119, built on March 11, 2021
Runtime version: 1.8.0_242-release-1644-b01 amd64
VM: OpenJDK 64-Bit Server VM by JetBrains s.r.o
GC: ParNew, ConcurrentMarkSweep
Memory: 1237M

気になる質問をクリップする

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

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

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

ベストアンサー

同じ描画ループで表示、非表示の切り替えをしているので期待通りに動いていないのだと思います。

めちゃくちゃかみ砕いて話しますが以下のイメージです。

ヤマト運輸が「プログレスバーを表示する機械」と「プログレスバーを非表示にする機械」
を運んで画面に接続するとします。機械が画面に接続されるとプログレスバーが表示/非表示となります。

■期待
①ヤマト運輸が「プログレスバーを表示する機械」を運んで画面に接続する
②プログレスバーが表示される
③ヤマト運輸が「プログレスバーを非表示にする機械」を運んで画面に接続する
④プログレスバーが非表示になる

■今の実装
①ヤマト運輸が「プログレスバーを表示する機械」と「プログレスバーを非表示にする機械」を一緒に運ぶ
②最後に渡された機械を画面に接続する
③プログレスバーは最後に渡された機械の挙動をする

■改善方法
プログレスバーの表示と非表示の描画ループを分ける。
MainThreadでプログレスバーを更新してWorkerThreadで長い処理が完了した後に、またMainthreadに戻ってきてプログレスバーを更新する(最初のプログレスバー更新と描画ループが異なる)


UIを更新する処理はMainThreadでやる必要があり
長い時間がかかる処理はWorkerThreadでやるのが一般的です。

もしコルーチンを使ってやるとしたら以下になるのかなと思います。

build.gradleに★を追加

dependencies { (省略) implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.3.1'★ }
class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) binding.button.setOnClickListener { // ★MainThreadでUIを更新する binding.progressBar.visibility = ProgressBar.VISIBLE lifecycleScope.launch { nagaisyori() // ★WorkerThreadからMainThreadに戻ってくる // MainThreadを離れて戻ってきたので最初のプログレスバー更新とは別の描画ループ binding.progressBar.visibility = ProgressBar.INVISIBLE binding.textView1.setText("処理が終わりました") } } } suspend fun nagaisyori()= withContext(Dispatchers.Default) { // ★Dispatchers.Defaultがポイント、MainThreadではなくWorkerThreadになる var sum = 0.0 for (i in 0..1000000000) { sum += i } } }

投稿2021/03/26 00:39

Sagamaru

総合スコア70

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mukuc

2021/03/26 21:06

お世話になります。 「今の実装のイメージ」とてもわかりやすいです。最後に渡された機械が動いちゃってるんですね。 またコルーチンでのご回答ありがとうございます! 提示いただいたコードを読みながら試してみたいと思います。m(__)m
mukuc

2021/03/26 21:27 編集

動きました!! 記述されているクラスやメソッドの深い理解までは至っていませんが MainThreadとWorkerThreadでそれぞれ仕事をやらせることは理解できました。 理想の挙動になりました。ありがとうございます。
Sagamaru

2021/03/26 21:44

良かったです!コルーチンは自分も最初は取っ付きにくかったです。でもAndroid開発をするならKotlinが主流でコルーチンは避けては通れぬ知識なので、実際に動かして挙動を見たりログ出力すると分かりやすいです。
guest

0

AsyncTask の使い方は、とりあえずこんな感じでしょうか。
(現在 AsyncTask は非推奨になったので、本当はコルーチンの方が望ましいのですが…。)

Kotlin

1package com.example.progressbarstyletest 2 3import android.os.AsyncTask 4import android.os.Bundle 5import android.view.View 6import androidx.appcompat.app.AppCompatActivity 7import com.example.progressbarstyletest.databinding.ActivityMainBinding 8 9class MainActivity : AppCompatActivity() { 10 11 private lateinit var binding: ActivityMainBinding 12 13 override fun onCreate(savedInstanceState: Bundle?) { 14 super.onCreate(savedInstanceState) 15 binding = ActivityMainBinding.inflate(layoutInflater) 16 setContentView(binding.root) 17 18 binding.button.setOnClickListener { 19 binding.textView1.setText("処理中") 20 binding.progressBar.visibility = View.VISIBLE //プログレスバー表示 21 22 // AsyncTask による非同期処理を開始 23 NagaiShori().execute() 24 25 // ここで処理を OS に返すことにより、UI (プログレスバーなど) が更新される 26 } 27 } 28 29 // MainActivity のプロパティ (binding) にアクセスするために inner class にする 30 inner class NagaiShori: AsyncTask<Unit, Unit, Unit>() { 31 32 override fun doInBackground(vararg params: Unit?) { 33 //長い処理 (バックグラウンドスレッドで処理される) 34 var sum = 0.0 35 for(i in 0..1000000000) { 36 sum += i 37 } 38 } 39 40 override fun onPostExecute(result: Unit?) { 41 // UI 更新 (メインスレッドに戻って処理される) 42 binding.textView1.setText("処理が終わりました") 43 binding.progressBar.visibility = View.INVISIBLE //プログレスバー非表示 44 } 45 } 46}

投稿2021/03/26 00:24

hoshi-takanori

総合スコア7895

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

mukuc

2021/03/26 00:49

早速のご回答ありがとうございます!! さっそく同じコードを記述してみたところ想定している通り動きました。 「コルーチン」についてですが、初学者が取り組むのは難しそうですね。 全体的に知識の底上げをしてから取り組んでみたいと思いました。
mukuc

2021/03/26 01:47

追記 AsyncTaskを使うと警告が出てしまうのが怖いですね‥ This AsyncTask class should be static or leaks might occur
hoshi-takanori

2021/03/26 01:55

はい。それもあって非推奨になりました、とはいえ、実際にリークするのは AsyncTask の処理が終わる前に Activity を閉じた場合で、現段階ではスレッドや非同期処理を学ぶ上では AsyncTask の方が分かりやすいと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.44%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問