◆PROCESSING 逆引きリファレンス
カテゴリー:スマホ(AndroidMode)
ダイアログBOXを表示するには(AndroidMode編)
【概要】
PROCESSINGにAndroidMode を導入する事で、PROCESSINGで開発したプログラムをAndroid端末上で動かす事ができるようになります。
AndroidModeの導入については「PROCESSINGをAndroid端末で動かすには(4.0版)」記事を参照してください。
アプリケーションを作成していると、ときどき利用者からの入力を受け付けたくなる場面があります。
たとえばゲームなどで、「宝箱がある。開けますか?(はい)(いいえ)」のような場面を作りたい時などです。
PROCESSINGでゴリゴリとコーディングすれば、簡単な問い合わせ画面を作成する事も不可能ではありません。むしろ場合によっては、その方が良いケースもあるでしょう。
が、せっかくAndroid上で動くアプリケーションなのですから、標準的な方法で問い合わせを行う方法も知りたい所です。
Windowsのプログラム(C#やVB)でダイアログBOXを表示するのは、とても簡単です。
それに比べ、PROCESSINGやJavaでAndroid OS用のダイアログBOXを表示するのは、ちょっと大変です。Androidはスマホ用のOSであるため、たかがダイアログBOXとはいえ、いろいろな前提知識が必要となる為です。
とはいえ、基本を押さえればそんなに難しくはありませんので、初心者の方にも理解できると思います。
AndroidでダイアログBOXを表示するには、DailogFragmentを利用します。そしてPROCESSINGのプログラム(Sketch)はFragment(正確にはPFragmentから呼び出されるメソッド)です。
FragmentやDailogFragmentに不慣れな方は、下記コラムに前提となる知識をまとめておきましたので、参照してみて下さい。
ダイアログBOXの選択結果を受け取る方法は幾つか考えられますが、おおまかに言うと Main Activity を介して受け取る方法と、PFragmentで受け取る方法の2種類があります。
(画像URL:illust-AC 様:casa4さん)
Main Activity を介して応答を受け取る方法(A、B)でも良いのですが、少し回りくどい感じになりますので、ここではPFragmentで応答を受け取る方法(C)を紹介したいと思います。
【詳細】
事前準備
DialogFragmentからPFragmentに応答を戻すためには、DialogFragmentが、応答の戻し先となるPFragmentを特定できる必要があります。
このような時 Androidでよく行うのは、ダイアログBOX生成時に setTargetFragment()で呼び出し元となるFragmentを記憶し、ダイアログBOXのボタン押下時などにgetTargetFragment()で呼び出し元を特定して、応答を返す方法です。
しかしこの方法は、PROCESSINGのAndroidModeでは正しく動作しません(PROCESSINGのAndroidModeが、残念な実装になっている為です・・・)。
かわりにMain Activity からPFragmentを生成する箇所に手を入れて、PFragmentに一意のIDを付与しておきます。
そして、ダイアログBOXのボタン押下時などに findFragmentById() する事で、呼び出し元を特定し、応答を返します。
1 2 3 4 5 6 7 8 9 10 11 12 13 |
protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); FrameLayout frame = new FrameLayout(this); frame.setId(CompatUtils.getUniqueViewId()); //★ setContentView(frame, new ViewGroup.LayoutParams( ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)); //ここでスケッチとPFragmentを生成する sketch = new Sketch(); PFragment fragment = new PFragment(sketch); fragment.setView(frame, this); } |
Main ActivityでSketchとPFragmentを生成する箇所は、公式サイト の例に従っていれば、上記のようになっている筈です。
PFragmentに直接IDをつける事はできませんので、PFragmentが使う画面(View:FrameLayout)にIDを付けます。★印の箇所ですね。
公式サイト のコーディングでは getUniqueViewId() 命令でランダムなIDを付与していますが、ここを下記のようにして、私達が利用しやすいIDを付与するように変更します。
本例では sketchFrame という名前を付けています。名前はみなさんの都合が良いものに変更して下さい。
1 2 |
//frame.setId(CompatUtils.getUniqueViewId()); //<--コメント化 frame.setId(R.id.sketchFrame); //<--追加 |
また、res¥values¥strings.xml に以下の記述を追加します。
1 2 3 4 |
<resources> <string name="app_name">ProcessingSample</string> <item name="sketchFrame" type="id"/> </resources> |
ここで、「なぜViewにIDを付与したら、PFragmentにIDを付与した事になるんだ?」と疑問に思ったあなたは鋭いです(笑)。
実は fragment.setView() を行うと、PFragment が内部で
1 2 3 4 5 6 |
public void setView(View view, FragmentActivity activity) { FragmentManager manager = activity.getSupportFragmentManager(); FragmentTransaction transaction = manager.beginTransaction(); transaction.add(view.getId(), this); //<--ここでViewのIDをFragmentに付与 transaction.commit(); } |
のような処理を行っているのです。
Viewに与えたIDを、PFragmentの実体を作成する際のIDにも流用しているのですね。よって、以降は FragmentManager # findFragmentById( R.id.sketchFrame ) で探せば、PFragmentを見つけることができるようになります。
ダイアログBOXを作成する
今回の例では、ダイアログBOXの作成にはDialogFragmentを継承した専用のクラス(MyDialogクラス)を用意しています。
MyDialogクラスは以下のようになっています。
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 |
import android.app.Dialog; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.app.AlertDialog; import android.view.Window; import android.view.WindowManager; import java.util.ArrayList; /** * PROCESSING AndroidMode ダイアログBOX * @author MSLABO * @version 1.0 */ public class MyDialog extends DialogFragment { //[A] private final static String KEY_PARAM_TITLE = "MYTITLE"; private final static String KEY_PARAM_MESSAGE = "MYMESSAGE"; private final static String KEY_REQUESTCODE = "MYREQCODE"; /** * ダイアログ生成時イベント * @param savedInstanceState 保存情報 */ @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); //[B]フォーカスを与えない(ステータスバーやナビゲーションバーを隠す) Window window = getDialog().getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); } /** * デフォルトコンストラクタ(必須) */ public MyDialog(){ //[C]空っぽで良いので、作成しておくこと } /** * ファクトリーメソッド * @param title タイトル * @param message 本文 * @param requestCode リクエストコード * @return MyDialogインスタンス */ public static MyDialog newInstance(String title, String message, int requestCode){ //パラメータ保存用のBundleを作成する Bundle args = new Bundle(); //[D]パラメータ(この例ではタイトルと本文、requestCode)を保存する args.putString(KEY_PARAM_TITLE, title); args.putString(KEY_PARAM_MESSAGE, message); args.putInt(KEY_REQUESTCODE, requestCode); //ダイアログを生成する MyDialog dialog = new MyDialog(); //ダイアログに保存したパラメータを紐付ける dialog.setArguments(args); return dialog; } /** * ダイアログ表示処理 * @param savedInstanceState 保存情報 * @return Dialogインスタンス */ @NonNull @Override public Dialog onCreateDialog(Bundle savedInstanceState) { //AlertDialogを生成する final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); //[D]Bundleからパラメータを取り出して AlertDialig にセットする builder.setTitle(getArguments().getString(KEY_PARAM_TITLE)); builder.setMessage(getArguments().getString(KEY_PARAM_MESSAGE)); //OKボタンとキャンセルボタンを作成する builder.setPositiveButton( android.R.string.ok, new DialogInterface.OnClickListener(){ @Override public void onClick(DialogInterface dialogInterface, int which) { //[E]PFragment(Sketch)を探す FragmentManager manager = getActivity().getSupportFragmentManager(); Fragment sketch = manager.findFragmentById(R.id.sketchFrame); int requestCode = getArguments().getInt(KEY_REQUESTCODE); //[F]戻したい値があれば、Intentに詰める(任意) Intent intent = new Intent(); intent.putExtra("BUTTON", "OK"); sketch.onActivityResult(requestCode,0, intent); dismiss(); } }); //キャンセルボタン builder.setNegativeButton( android.R.string.no, new DialogInterface.OnClickListener(){ @Override public void onClick(DialogInterface dialogInterface, int i) { //[E]PFragment(Sketch)を探す FragmentManager manager = getActivity().getSupportFragmentManager(); Fragment sketch = manager.findFragmentById(R.id.sketchFrame); int requestCode = getArguments().getInt(KEY_REQUESTCODE); //[F]戻したい値があれば、Intentに詰める(任意) Intent intent = new Intent(); intent.putExtra("BUTTON", "CANCEL"); sketch.onActivityResult(requestCode,1, intent); dismiss(); } }); return builder.create(); } } |
Sketch側は以下のような感じです。
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
import android.content.Intent; import android.support.v4.app.Fragment; import android.util.Log; import processing.core.PApplet; import processing.core.PImage; import processing.event.TouchEvent; /** * PROCESSING AndroidMode DialogBox Sample * @author MSLABO * @version 1.0 */ public class Sketch extends PApplet { MyDialog dialog; //ダイアログBOX PImage haikei; //背景 @Override public void settings() { fullScreen(); } @Override public void setup() { //変数の初期化や、各リソースの読み込みを行う haikei = loadImage( "Dungeon.png"); } @Override public void draw() { //背景表示 background(haikei); } @Override public void touchStarted(TouchEvent touchEvent) { super.touchStarted(touchEvent); //ダイアログBOXを表示する(requestCode = 100) Fragment fragment = (Fragment)surface.getComponent(); dialog = MyDialog.newInstance( "宝箱がある", "開けますか?", 100); dialog.show(fragment.getFragmentManager(), "dialog"); } /** * ダイアログBOX応答受信処理 * @param requestCode リクエストコード * @param resultCode 応答コード * @param intent インテント */ @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { if( intent.getStringExtra("BUTTON").equals("OK")){ Log.d("AMTEST", "OK が押されました。"); } else { Log.d("AMTEST", "CANCEL が押されました。"); } } } |
下記に、ポイントとなる箇所を説明します。
A:DialogFragmentを継承する
ダイアログBOXクラスには、DialogFragmentを継承する必要があります。
1 |
public class MyDialog extends DialogFragment { //[A] |
DaialogFragmentには
- android.app.DialogFragment
- android.support.v4.app.DialogFragment
の2つがありますが、特別な理由がない限り android.support.v4.app.DialogFragment の方を利用して下さい。
B:余計なステータスバーやナビゲーションバーを隠す
PROCESSINGを全画面モードで利用していると、普段はステータスバーやナビゲーションバーが表示されることはありません。しかしダイアログBOXを開くと、突然これらが表示されるようになってしまいます。
しかもダイアログBOXを閉じても、ステータスバーやナビゲーションバーが自動で隠れる事がありません・・・。
これを防ぐためには、ダイアログBOXにフォーカスを当てないようにします。
ただしフォーカスを外すとキーボード操作を受け付けなくなりますので、ダイアログBOX内でテキスト入力が行いたいような場合には、別の手段を用いる必要があります。
ダイアログBOXからフォーカスを外すには、onActivityCreated をオーバライドして、以下の記述を加えてください。
1 2 3 4 5 6 7 |
public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); //[B]フォーカスを与えない(ステータスバーやナビゲーションバーを隠す) Window window = getDialog().getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE); } |
C:デフォルトコンストラクタを定義する
DialogFragmentもFragmentの一種ですので、必ず引数が無いデフォルトコンストラクタを定義してください。
これがないと普段は正常に動作していても、ある時(メモリが少なくなった時など)にダイアログBOXが不正終了する原因となります。
1 2 3 4 5 6 |
/** * デフォルトコンストラクタ(必須) */ public MyDialog(){ //[C]空っぽで良いので、作成しておくこと } |
D:Bundleに保存し、Bundleから取り出す
上位(本例ではSketch)から渡されたパラメータで、ダイアログBOXの表示に必要なものは、必ずBundleに保存しておきます。内部変数に格納してはいけません。
そしてダイアログBOXを表示する処理(onCreateDialog)の中で、Bundleから取り出して利用します。以下のような感じです。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
public static MyDialog newInstance( … ){ //パラメータ保存用のBundleを作成する Bundle args = new Bundle(); //[D]パラメータ(この例ではタイトルと本文、requestCode)を保存する args.putString(KEY_PARAM_TITLE, title); args.putString(KEY_PARAM_MESSAGE, message); args.putInt(KEY_REQUESTCODE, requestCode); … } public Dialog onCreateDialog(Bundle savedInstanceState) { … final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); //[D]Bundleからパラメータを取り出して AlertDialig にセットする builder.setTitle( getArguments().getString(KEY_PARAM_TITLE) ); //タイトル builder.setMessage( getArguments().getString(KEY_PARAM_MESSAGE) ); //本文 … } |
もちろん上位から何も受け取る必要がない場合は、Bundleに保存する必要はありません。
このあたりの理屈がわからない方は、下記コラムを参照してみてください。
E:戻し先を特定する
戻し先となるFragmentは、FragmentManager # findFragmentById( R.id.sketchFrame ) で見つけます。
FragmentManager は Activity の getSupportFragmentManager() で取得します。
1 2 3 4 5 |
//[E]PFragment(Sketch)を探す FragmentManager manager = getActivity().getSupportFragmentManager(); Fragment sketch = manager.findFragmentById(R.id.sketchFrame); |
getActivity() や findFragmentById() は、ボタン押下処理などの中に記述します。
ActivityやFragmentを、ファクトリーメソッドやデフォルトコンストラクタの中で取得して、変数に覚えるような作りにしてはいけません。
どうしてもボタン押下処理の中で記述したくない場合は、onActivityCreated をオーバライドして、その中に記述するのが良いでしょう。
FragmentやFragmentManagerには、それぞれ
- android.app.Fragment
- android.support.v4.app.Fragment
- android.app.FragmentManager
- android.support.v4.app.FragmentManager
の2つがありますが、特別な理由がない限り android.support.v4.app 側を利用して下さい。
F:複数の値を呼び出し元に伝えるには Intent を使う
上位に複数の値を戻す必要があるときは、Intent を使うと便利です。
今回の例であれば、応答の返却先となる上位の onActivityResult メソッドは、引数にIntentを持っていますので、任意のパラメータを伝えることが可能です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
//ダイアログBOX側 public void onClick(…){ … //[E]戻したい値があれば、Intentに詰める(任意) Intent intent = new Intent(); intent.putExtra("BUTTON", "OK"); sketch.onActivityResult(requestCode,0, intent); … } //Sketch側 @Override public void onActivityResult(int requestCode, int resultCode, Intent intent) { String buttonName = intent.getStringExtra("BUTTON"); Log.d("AMTEST", "押されたボタンは" + buttonName + "です"); } |
コラム:Fragmentに関する予備知識
DialogFragmentについて
問い合わせを行う画面を「ダイアログBOX」と呼びます。
昔は Activity.showDialog() というAndroid SDKの命令でダイアログBOXを作成しましたが、この方法はAPI13から非推奨となりました。
AndroidでダイアログBOXを作成する時は、showDialog ではなく、DialogFragment を利用します。
DialogFragmentは、その名の通りFragmentの一種です。
Androidの画面はActivityという単位で実行されますが、最近はActivityよりも応用が効くFragmentという単位で実行される事が多くなってきました。
Fragmentは「画面を持った部品」のような存在で、親となるActivityを持ち、Activityから作成されます。またFragmentからFragmentを作成する事も可能です。
表示したい画面ごとにFragmentを用意し、Fragmentを切り替えることで、画面遷移したようにみせかける事が可能なのです。
(画像URL:illust-AC 様:かみたまさん)
実はAndroid上で動作するPROCESSINGのプログラム(Sketch)も、Main Activityから呼び出される Fragment の一種(PFragment)です。
つまりダイアログBOXを表示するには、Fragment(PFragmentとSketch)からサブFragment(DialogFragment) を呼び出す形式となります(ここ重要!)。
絵にすると下図のようになります。
Fragmentは、1つのアプリケーションに幾つも作成する事ができます。各Fragmentは、Fragmentを作成する際に「TAG」とよばれる名前や一意のID(番号)を与える事で識別します。
FragmentはFragmentManagerと呼ばれるクラスで管理されます。作成したFragmentをFragmentManagerに対して、登録したり、入れ替えたり、削除するようにお願いする事で、画面に表示される仕組みとなっています。
(画像URL:illust-AC 様:natuさん、ヤマシタポテコさん)
Fragmentを使う際の注意
DialogFragmentもFragmentの一種ですので、Fragmentを扱う時と同じ注意事項があります。
それはダイアログBOX表示中にスマホを回転させたり、メモリが少なくなると、ActivityとFragmentが勝手に再作成される事です。
Windowsなどで動作するプログラムしか作ったことがないとピンとこないのですが、Android OSではメモリやバッテリーの節約のために、表示されなくなった画面はOSが勝手に破棄する仕組みとなっています。
またスマホを回転させたタイミングでも破棄されます。
画面が勝手に破棄されると、使う側(私達)にとっては都合が悪いので、その画面を再度表示する必要が生じた時に、これまたOSが勝手に画面を再作成して、何事もなかったかのように見せかけているのです。
(画像URL:illust-AC 様:キラーT細胞さん、K-factoryさん)
ActivityやFragmentが再作成されると、変数に覚えていた内容も初期化されてしまいます。
これの何が問題かというと、パラメータなどで渡された内容や、計算結果を変数に覚えておくと、再作成と共にその内容が忘れられてしまうという事です。
今回であればダイアログBOXに表示するタイトルや本文を、SketchからDialogFragmentにパラメータとして渡しますが、これをDialogFragmentの内部変数に覚えておくのはNGとなります。
(画像URL:illust-AC 様:キラーT細胞さん、K-factoryさん)
Androidにはこのような状況に対応するために、Bundleという仕組みがあります。
Bundleに記憶された内容は、ActivityやFragmentを再作成しても失われる事がありません。いうなれば大切なデータを金庫にしまうようなものですね(笑)。
必要なタイミングで大切な情報をBundleに保存しておき、画面が再作成されたら、ActivityやFragmentの開始段階で、Bundleに保存しておいた情報を取り出して、変数に再セットするのです。
(画像URL:illust-AC 様:キラーT細胞さん、K-factoryさん、よろづやさん)
ただし、OSが自動的にBundleから復元してくれるわけではありませんから、復元処理は私達の方で行う必要があります。
またダイアログBOXで選択された結果を、呼び出し元であるSketch(PFragment)に伝えるためには、DailogFragment側で「誰から呼ばれたのか」を特定できる必要があります。
このような時のために、AndroidにはsetTargetFragment()とgetTargetFragment()という命令が用意されています。
DialogFragment#setTargetFragment() を実行すると、ちょうど Bundle に変数を覚えるのと同じような仕組みで、戻り先となる Fragment の情報を覚えておくことができます。
そして呼び出し元に結果を伝えたい時に、DialogFragment#getTargetFragment() で戻り先の情報を取り出し、該当Fragmentの特定メソッドへ応答を返すのが一般的な手法となります。
(画像URL:illust-AC 様:よろづやさん、minokikakuさん、ナナとハチさん)
ただしこの方法は、PROCESSINGのAndroidModeでは利用できないので注意してください。
PROCESSINGで応答を戻す方法については、詳細の説明を参考にしてください。
この他にもFragmentを扱う時には、以下のような注意事項があります。
- 引数なしのコンストラクタを用意する事
- ファクトリーメソッドを使ってインスタンスを作成する事
少し補足しておくと、Fragmentの処理を独立したクラスで実現している場合、該当クラスのコンストラクタに引数を渡してインスタンスを作成するようなコーディングはNGとなります。
以下はNGな例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public void hoge() { //ダイアログBOXを生成する MyDialog dialog = new MyDialog(100); dialog.show( sketch.getFragmentManager(), "dialog"); … } public class MyDialog extends DialogFragment { //コンストラクタ public MyDialog( int requestCode ){ … return; } } |
そうではなく、以下のようにします。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
public void hoge() { //ダイアログBOXを生成する MyDialog dialog = MyDialog.newInstance(100); dialog.show( sketch.getFragmentManager(), "dialog"); … } public class MyDialog extends DialogFragment { //引数なしのデフォルトコンストラクタを用意する public MyDialog( ){ //処理は何も書かなくても良い } //ファクトリーメソッド public static MyDialog newInstance(int requestCode){ //ダイアログBOXを生成する MyDialog dialog = new MyDialog(); … return dialog; } } |
ファクトリーメソッドと呼ばれる関数を用意して、そこでインスタンスを作成します。
そしてコンストラクタは、引数無しのものを必ず用意しておきます。
この辺りについては、以下のページなどが参考となります。
- 夜でもアッサム 様
- Tech Sheets 様
【関連記事】
自力ダイアログBOX例:
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 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 |
import android.graphics.Point; import android.util.Log; import processing.core.PApplet; import processing.core.PImage; import processing.event.TouchEvent; /** * PROCESSING AndroidMode DialogBox Sample * @author MSLABO * @version 1.0 */ public class Sketch extends PApplet { OriginalDialogBox dialog; PImage haikei; @Override public void settings() { fullScreen(); } @Override public void setup() { //変数の初期化や、各リソースの読み込みを行う dialog = new OriginalDialogBox(); haikei = loadImage( "Dungeon.png"); } @Override public void draw() { //背景表示 background(haikei); //ダイアログBOX表示 dialog.Show(); } @Override public void touchStarted(TouchEvent touchEvent) { super.touchStarted(touchEvent); int Action = touchEvent.getAction(); if( Action == TouchEvent.START){ if( !dialog.isDisp()){ //ダイアログBOX非表示なら表示する dialog.DispDialogBox("宝箱がある","開けますか?"); } else { //ダイアログBOX表示中なら、タッチされた座標を得る float x = touchEvent.getPointerX(0); float y = touchEvent.getPointerY(0); //ボタン押下を判定する int ret = dialog.isPush(x,y); if( ret == dialog.POSITIVE_PUSH){ Log.d("AMTEST", "OK が押されました。"); } else if( ret == dialog.NEGATIVE_PUSH){ Log.d("AMTEST", "CANCEL が押されました。"); } } } } /** * ダイアログBOX表示クラス */ public class OriginalDialogBox { final int ButtonWidth = 180; //ボタン幅 final int ButtonHeight = 100; //ボタン高さ final int BackColor = 220; //背景色 final int MarginWidth = 10; //マージン横 final int MarginHeight = 10; //マージン縦 final int POSITIVE_PUSH = 0; //OK押下 final int NEGATIVE_PUSH = 1; //CANCEL押下 final int NO_PUSH = 2; //押下なし final String OK_TEXT = "OK"; //OKボタン文字 final String CANCEL_TEXT = "CANCEL"; //CANCELボタン文字 float myWidth, myHeight; //ダイアログBOX幅と高さ Point box = new Point(); //ダイアログBOX左肩座標 Point positive = new Point(); //OKボタン左肩座標 Point negative = new Point(); //CANCELボタン左肩座標 float fontSize; //文字サイズ boolean dispDialog; //表示中FLG String title; //タイトル String message; //本文 /** * コンストラクタ */ public OriginalDialogBox(){ //縦横の回転に応じて、ダイアログBOXの横幅を調整 if( width < height ){ myWidth = width * 0.85f; } else { myWidth = width * 0.75f; } //縦サイズは固定 myHeight = 320; //ダイアログBOXの左肩原点と文字サイズ設定 //ダイアログBOXは画面中央に配置する box.x = (int)(width - myWidth)/2; box.y = (int)(height - myHeight)/2; fontSize = 16 * displayDensity; //タイトルと本文を初期化 title = ""; message = ""; } /** * タイトル表示処理 * @param title タイトル * @param x 表示開始横座標 * @param y 表示開始縦座標 */ private void ShowTitle(String title, float x, float y ){ float len = 0; String disp = ""; //ダイアログBOXの横幅に収まるだけタイトルを表示する for( int i = 0; i < title.length(); i++ ){ String c = title.substring(i, i + 1); len = len + textWidth(c); if( len > myWidth ) { break; } disp = disp + c; } text( disp, x, y ); } /** * 本文表示処理 * @param message 本文 * @param x 表示開始横座標 * @param y 表示開始縦座標 */ private void ShowMessage(String message, float x, float y){ float len = 0; float fx = x; float fy = y; //ダイアログBOXの横幅に収まるだけ本文を表示し、改行する for( int i = 0; i < message.length(); i++ ){ String c = message.substring(i, i + 1); len = len + textWidth(c); if( len > myWidth ) { fx = x; fy = fy + fontSize + MarginHeight; len = 0; } text( c, fx, fy ); fx = fx + textWidth(c); } } /** * 背景画像暗転処理 */ private void BackFilter(){ //半透明画像を読み込む PImage work = loadImage("back.png"); //描画する image(work,0,0); } /** * OKボタン描画処理 */ private void ShowPositivButton(){ textAlign(LEFT, TOP); textSize(fontSize); fill(color(BackColor)); positive.x = (int)(box.x + myWidth) - (ButtonWidth + MarginWidth) * 2 ; positive.y = (int)(box.y + myHeight) - (ButtonHeight + MarginHeight ); //rect( positive.x, positive.y, ButtonWidth, ButtonHeight); fill(0); float fx = positive.x+(ButtonWidth-(int)textWidth(OK_TEXT))/2; float fy = positive.y+(ButtonHeight-fontSize)/2; text( OK_TEXT, fx, fy); } /** * CANCELボタン描画処理 */ private void ShowNegativeButton(){ textAlign(LEFT, TOP); textSize(fontSize); fill(color(BackColor)); negative.x = (int)(box.x + myWidth) - (ButtonWidth + MarginWidth) ; negative.y = (int)(box.y + myHeight) - (ButtonHeight + MarginHeight); //rect( negative.x, negative.y, ButtonWidth, ButtonHeight); fill(0); float fx = negative.x + (ButtonWidth - (int)textWidth(CANCEL_TEXT)) / 2; float fy = negative.y + (ButtonHeight - fontSize)/2; text( CANCEL_TEXT, fx, fy); } /** * ダイアログBOX描画処理 */ public void Show(){ if( !dispDialog ){ //表示中でなければ、何もしない return; } //暗転処理 BackFilter(); //BOXを描画 stroke(0); strokeWeight(2); fill(color(BackColor)); rect(box.x, box.y, myWidth, myHeight); //ボタン描画 ShowPositivButton(); ShowNegativeButton(); //タイトルとメッセージ描画 ShowTitle( title, box.x + MarginWidth, box.y + MarginHeight); ShowMessage( message, box.x+MarginWidth,box.y+fontSize * 2 ); } /** * ダイアログBOX表示開始処理 * @param _title タイトル * @param _message 本文 */ public void DispDialogBox(String _title, String _message){ dispDialog = true; title = _title; message = _message; } /** * 表示中判定処理 * @return true : 表示中 */ public boolean isDisp(){ return dispDialog; } /** * ボタン押下判定処理 * @param x 判定横座標 * @param y 判定縦座標 * @return POSITIVE_PUSH / NEGATIVE_PUSH / NO_PUSH */ public int isPush(float x, float y){ if( x > positive.x && x < (positive.x + ButtonWidth) && y > positive.y && y < (positive.y + ButtonHeight)){ dispDialog = false; return POSITIVE_PUSH; } if( x > negative.x && x < (negative.x + ButtonWidth) && y > negative.y && y < (negative.y + ButtonHeight)){ dispDialog = false; return NEGATIVE_PUSH; } return NO_PUSH; } } } |
Android SDK を利用せずに、PROCESSINGの命令だけでダイアログBOXっぽいものを表示する例です。
画面をタッチすると、OKとCANCELボタンをもったダイアログBOXを表示し、ボタンの入力を受け付けます。
単純なダイアログBOXを表示するなら、Android SDKの命令を使ったほうが楽ですね(汗)。
<出力サンプル>
(画像URL:photo-AC 様:acworksさん、涼介さん、 illust-AC 様:casa4さん)
本ページで利用しているアイコン画像は、下記サイト様より拝借しております。各画像の著作権は、それぞれのサイト様および作者にあります。