処理が終わった、モードが変更されたなどを知りたい場合があります。Listenerがもともと存在するAPIであればいいのですが、そういったものがない場合にカスタムでlistenerを実装し通知させることも可能です。
2021.2.1
custom listener
ボタンをユーザーがタップしたのを知るためにlistenerをセットします。同様にあるタスクが終了、あるいは事象が変更されたらお知らせを出すメソッドが実装されているAPIがあります。こういったlistenerをカスタムでつくるためにinterfaceを活用します。
interfaceを実装するときはimplementsを使っています、Buttonがいい例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
public class MainActivity extends Activity implements View.OnClickListener { @Override protected void onCreate(Bundle savedInstanceState) { ... final Button button1 = findViewById(R.id.button); button1.setOnClickListener(this); ... } ... public void onClick(View view){ switch (view.getId()) { case R.id.button_1: Log.d("debug","button, Perform action on click"); break; ... } } } |
interface, implements
重いタスクをさせて、それが終了したら知らせるという形でクラスを作ってみます。
本来はこういった重いタスクは非同期がいいのですが、ここでは簡略化します。
以下はAsyncTaskでcustom listenerを使った例
重いタスクとして1〜20までを加算させまてみます(あまり重くないのですが)
HeavyTask.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
class HeavyTask { private TestListener listener; // interface 設定 interface TestListener { void onSuccess(int result); } // listener void setListener(TestListener listener) { this.listener = listener; } void taskStart() { int sum = 1; int i= 0; while( i< 20 ){ sum += sum; i++; } if(listener != null) { // 計算が終わったら結果をlistenerに渡す listener.onSuccess(sum); } } } |
これをMainActivityに実装してみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
// HeavyTaskクラスのTestListenerを実装 public class MainActivity extends AppCompatActivity implements HeavyTask.TestListener { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); HeavyTask heavyTask = new HeavyTask(); // listenerをセット heavyTask.setListener(this); // タスクを開始 heavyTask.taskStart(); } // タスクが終わったら結果を受け取る @Override public void onSuccess(int result) { Log.d("debug", String.valueOf(result)); } } |
ログの結果
1 |
D/debug: 1048576 |
匿名クラス
ボタンでよくやる匿名クラスでの実装
HeavyTask.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
class HeavyTask { // interface 設定 interface TestListener { void onSuccess(int result); } void TaskStart(final TestListener listener) { int sum = 1; int i= 0; while( i< 20 ){ sum += sum; i++; } if(listener != null) { listener.onSuccess(sum); } } } |
MainActivity
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class MainActivity extends AppCompatActivity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); HeavyTask heavyTask = new HeavyTask(); heavyTask.TaskStart(new HeavyTask.TestListener() { @Override public void onSuccess(int result) { Log.d("debug", String.valueOf(result)); } }); } } |
まとめのコード
上の2つの組み合わせ的ですがsetListenerを明示できてわかりやすいのかもしれません
スリープさせて重そうなTaskにして、ボタンタップのタイミングで開始するようにしました。
HeavyTask.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 |
class HeavyTask { private TestListener listener; // interface 設定 interface TestListener { void onSuccess(int result); } void setListener(TestListener listener) { this.listener = listener; } void taskStart() { int sum = 1; int i =0; while( i< 20 ){ try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } sum += sum; i++; } if(listener != null) { listener.onSuccess(sum); } } } |
MainActivity.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 |
package your.package.name; import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private TextView textView; private HeavyTask heavyTask; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); heavyTask = new HeavyTask(); Button button = findViewById(R.id.button); // lambda を使えますがあえて button.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { heavyTask.setListener(createListener()); heavyTask.taskStart(); } }); } // lambda を使えますがあえて private HeavyTask.TestListener createListener(){ return new HeavyTask.TestListener() { @Override public void onSuccess(int result) { textView = findViewById(R.id.text_view); textView.setText(String.valueOf(result)); Log.d("debug", String.valueOf(result)); } }; } } |
レイアウトです。HeavyTaskを起動するボタンとその結果を表示するTextViewをおきました。
activity_main.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 |
<?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" android:background="#dfe" tools:context=".MainActivity"> <TextView android:id="@+id/text_view" android:textColor="#00f" android:textSize="36sp" android:layout_width="wrap_content" android:layout_height="wrap_content" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/button" android:layout_width="88dp" android:layout_height="wrap_content" android:layout_marginBottom="111dp" android:layout_marginEnd="148dp" android:layout_marginStart="148dp" android:layout_marginTop="81dp" android:text="@string/button" app:layout_constraintBottom_toTopOf="@+id/text_view" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> </androidx.constraintlayout.widget.ConstraintLayout> |
strings.xml
1 2 3 4 |
<resources> <string name="app_name">YourAppName</string> <string name="button">Button</string> </resources> |
References:
Observer Design Pattern in Java
How to create our own Listener interface in android? – Stack Overflow