Android (X06HT Desire) 入力ダイアログ、確認ダイアログを共通化。

今日は、X06HTが販売終了するって話が飛び交っている。

有機ELの供給不足でX06HT自体を安定供給できないための一時販売休止だと思いたい。公式発表を待つ。

販売終了でも、Softbank から、Android 携帯が発売されれば、まぁ良とするが、されなかったら至極残念だ。

 

まぁいいや。

Android では、ダイアログボックスを使うのに、JavaScript の alert や confirm みたいに気軽に使えるといいのだけど、どうも用意されていないみたい。使い回したいので共通化しときたいが、どうしたらいいかな~と。

手元の本 にも、そこまでのことは書いてない。

 

一晩考えて、使えそうな形になったので、メモ

共通で作成するダイアログの種類

  • alert ・・・ OK ボタンのみ。警告
  • confirm ・・・ OK 、Cancel 確認
  • inputBox ・・・ 入力エリア、OK、Cancel

の3つをアプリケーションのどこからでも使えるように、android.app.Application クラスを継承して、static メソッドとしてそれぞれ作成する。

問題点

alert は、単に OK ボタンがおされたら閉じるだけなので、特に問題ない。

問題は、残りの2つダイアログでのユーザー入力をどうやって呼び出し元に返すか。

 

JavaScript 等のように結果を Boolean や String で単純に受け取るわけには、いかなそうだ。

EventLister( OnClickListener ) を利用方法に合わせて個別に作成する方法をとってみる。もっと良い方法があれば知りたいが。

対応

confirm については、AlertDialog.Builder を利用するため、ClickListener にダイアログの参照が渡ってくるので、これをつかってダイアログを消すことができる。

inputBox については、UI を XML で定義することにしたので、View.OnClickListener でイベントを取る必要があるが、これだと、ボタン押下時にダイアログを消すことができない。

苦肉の策として、View.OnClickListener を 実装した static 抽象クラス InputBoxOnClickListener  を アプリケーションクラスにもたせ、inputBox メソッドの中でダイアログを生成した後、参照をセットするようにしてみた。

 

 package info.typea.myapp;
 
 import android.app.AlertDialog;
 import android.app.Application;
 import android.app.Dialog;
 import android.content.Context;
 import android.content.DialogInterface;
 import android.view.View;
 import android.widget.Button;
 import android.widget.EditText;
 
 public class MyApplication extends Application {
 
     /**
      * Application 共通警告ダイアログ
      * @param titleId
      * @param msg
      */
     public static void alert(Context context, int titleId, String msg) {
         AlertDialog.Builder b = new AlertDialog.Builder(context);
         b.setTitle(titleId);
         b.setIcon(android.R.drawable.ic_dialog_alert);
         b.setMessage(msg);
         b.setPositiveButton("OK", new DialogInterface.OnClickListener() {
             @Override
             public void onClick(DialogInterface dialog, int which) {
                 dialog.dismiss();
             }
         });
         b.show();
     }
 
     /**
      * Application 共通 確認ダイアログ
      * @param titleId
      * @param msgId
      */
     public static void confirm(Context context, int titleId, int msgId, 
                                 DialogInterface.OnClickListener lisner) {
         AlertDialog.Builder b = new AlertDialog.Builder(context);
         b.setTitle(titleId);
         b.setIcon(android.R.drawable.ic_dialog_info);
         b.setMessage(msgId);
         b.setPositiveButton("OK", lisner);
         b.setNegativeButton("Cancel", lisner);
         b.show();
     }
     
     /**
      * Application 共通 入力ダイアログ
      * @param context
      * @param msg
      * @param lsnr
      */
     public static void inputBox(Context context, String title, 
                                 String contents, InputBoxOnClickListener lsnr ) {
         
         Dialog dialog = new Dialog(context);
         dialog.setContentView(R.layout.input_dialog);
         dialog.setTitle(title);
         
         EditText txtContent = (EditText) dialog.findViewById(R.id.txt_contents);
         txtContent.setText(contents);
         
         Button btnOk     = (Button) dialog.findViewById(R.id.btn_ok);
         Button btnCancel = (Button) dialog.findViewById(R.id.btn_cancel);
         
         lsnr.setDialog(dialog);
         
         btnOk.setOnClickListener(lsnr);
         btnCancel.setOnClickListener(lsnr);
         
         dialog.show();        
     }
     
     /**
      * Application 共通入力ダイアログ 用 Click イベントリスナー
      */
     public static abstract class InputBoxOnClickListener implements View.OnClickListener {
         // ダイアログを消すためにダイアログの参照を保持できるように
         protected Dialog dialog;
         public void setDialog(Dialog dialog) {
             this.dialog = dialog;
         }
         public void dismiss() {
             if (this.dialog != null) {
                 this.dialog.dismiss();
             }
         }
     }
 }

inputBox の XML レイアウト

 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent"
     android:gravity="center">
    <EditText android:text="" android:id="@+id/txt_contents" 
                              android:layout_width="wrap_content" 
                              android:layout_height="wrap_content" 
                              android:minWidth="200sp" 
                              android:scrollbars="vertical" 
                              android:maxLines="3">
    </EditText>
    <LinearLayout 
        android:orientation="horizontal"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content">
        <ImageView android:id="@+id/img_icon" 
                   android:layout_width="wrap_content" 
                   android:layout_height="wrap_content">
        </ImageView>
        <TextView android:text="" android:id="@+id/txt_msg" 
                 android:layout_width="wrap_content" 
                 android:layout_height="wrap_content">
        </TextView>
    </LinearLayout>
    <LinearLayout 
        android:orientation="horizontal"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent" android:gravity="center">
        <Button android:text="@string/lbl_btn_ok" 
                android:id="@+id/btn_ok" 
                android:layout_height="wrap_content" 
                android:minWidth="80sp" 
                android:layout_width="wrap_content">
        </Button>   
        <Button android:text="@string/lbl_btn_cancel" 
                android:id="@+id/btn_cancel" 
                android:layout_height="wrap_content" 
                android:minWidth="80sp" 
                android:layout_width="wrap_content">
        </Button>
    </LinearLayout>
 </LinearLayout>

呼び出し方

ここまで、準備すれば、あとは好きなように Listener を 実装して、Click イベントをハンドリングすればいい。

例えば、confirm を利用して、ファイルの削除確認をしたいなら以下の様なファイル削除用のリスナーを作成して、

 class FileDeleteConfirmDialogOnClickListener implements DialogInterface.OnClickListener {
    private HogeInfo hogeInfo;  // 何かしらダイアログで操作したい情報をリスナーに詰めておく
    public FileDeleteConfirmDialogOnClickListener(HogeInfo hogeInfo) {
        super();
        this.hogeInfo = hogeInfo; 
    }
    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (DialogInterface.BUTTON1 == which) {
            // OK が押されたらファイルを削除する処理をさせる
            File f = new File(hogeInfo.getPath());
            if (f.isFile()) {
                f.delete();
            }
        } else if 
            (DialogInterface.BUTTON2 == which) {
            // NOP
        }
        dialog.dismiss();
    }
 }

こんな、感じで呼び出せばいい。

 MyApplication.confirm(this, R.string.lbl_confirm_dialog_title, R.string.confirm_delete_file, 
     new FileDeleteConfirmDialogOnClickListener(hogeInfo));

inputBox を利用して、新規で作るファイル名を指定させたいなら、

 class FileNameDialogOnClickListener extends MyApplication.InputBoxOnClickListener {
     @Override
     public void onClick(View v) {
         if (v.getId() == R.id.btn_ok) {
         
             EditText txtContent = (EditText) dialog.findViewById(R.id.txt_contents);
             // 入力された値はファイル名
             String filename = txtContent.getText().toString();
             try {
                 File f = new File(filename);
                 f.createNewFile();
             } catch(Exception e) {
                 ((ImageView) dialog.findViewById(R.id.img_icon))
                     .setImageResource(android.R.drawable.ic_dialog_alert);
                 ((TextView) dialog.findViewById(R.id.txt_msg))
                     .setText(e.toString());
                 return;
             }
         }
         this.dialog.dismiss();
     }
 }

こんな感じの専用リスナーを作成して、

 Date d = new Date();
 String msg = String.format("card_%tY%tm%td%tH%tM",d,d,d,d,d);
 String title = "filenam?";
 MyApplication.inputBox(this, title, msg, new FileNameDialogOnClickListener());

こんな感じで呼び出せばいい。

device01 device02

いじょ。