24 June 2014

(Android) 日時入力のダイアログ

日付、時刻を入力するためのダイアログの作成方法

AlertDialogでテキストボックスを用いる方法

20140621-datetime-alertdlg.jpg

テキストボックスを2つ表示するためには、レイアウト(LinearLayout)にテキストボックスを入れて、そのレイアウトをAlertDialogにセットするという2段階の実装方法を取る

import android.R.integer;
import android.support.v7.app.ActionBarActivity;
import android.support.v7.app.ActionBar;
import android.support.v4.app.Fragment;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.os.Build;
 
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.res.ColorStateList;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.EditText;
import android.text.InputType;
import android.graphics.Color;
 
import java.util.Calendar;
import java.text.*;
import java.util.Date;
 
// DatePickerDialog または TimePickerDialogを使う場合
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.widget.TimePicker;
import android.widget.DatePicker;
 
import android.util.Log;
 
public class MainActivity extends ActionBarActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    }
 
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }
 
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        if (id == R.id.action_settings) {
            return true;
        }
        // 「日時入力のテスト」メニュー
        else if (id == R.id.menu_datetime_test) {
            // DateTime_Test_func();
            Time_Test_func();
        }
        // 「プログラム終了」メニューが押された場合
        else if (id == R.id.menu_quit) {
            android.os.Process.killProcess(android.os.Process.myPid());
            System.exit(1);
        }
        return super.onOptionsItemSelected(item);
    }
 
    private void DateTime_Test_func(){
        final LinearLayout layout = new LinearLayout(MainActivity.this);
        layout.setOrientation(LinearLayout.VERTICAL);
        
        TextView text1 = new TextView(this);
        text1.setText("日付の入力");
        text1.setTextColor(Color.LTGRAY);
        layout.addView(text1);
        
        final EditText editViewDate = new EditText(MainActivity.this);
        editViewDate.setLines(1);   // 1行
        editViewDate.setInputType(InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_DATE);
        SimpleDateFormat dateFmt = new SimpleDateFormat("yyyy/MM/dd");
        Date dateNow = new Date();
        editViewDate.setText(dateFmt.format(dateNow));
        layout.addView(editViewDate);
        
        TextView text2 = new TextView(this);
        text2.setText("時間の入力");
        text2.setTextColor(Color.LTGRAY);
        layout.addView(text2);
        
        final EditText editViewTime = new EditText(MainActivity.this);
        editViewTime.setLines(1);   // 1行
        editViewTime.setInputType(InputType.TYPE_CLASS_DATETIME | InputType.TYPE_DATETIME_VARIATION_TIME);
        dateFmt.applyPattern("HH:mm:ss");
        editViewTime.setText(dateFmt.format(dateNow));
        layout.addView(editViewTime);
        
        // AlertDialogを構築する
        AlertDialog.Builder dlg = new AlertDialog.Builder(this);
        dlg.setTitle("テキスト入力2個のAlertDialog");
        dlg.setMessage("日時を入力します");
        dlg.setView(layout);
 
        dlg.setPositiveButton("決定", new DialogInterface.OnClickListener(){
            public void onClick(DialogInterface dialog, int which) {
                //Yesボタンが押された時の処理
                Log.d("EditViewDate", editViewDate.getText().toString());
                Log.d("EditViewTime", editViewTime.getText().toString());
                Calendar cal = Calendar.getInstance();
                cal = Str2Date(editViewDate.getText().toString(), editViewTime.getText().toString());
                SimpleDateFormat sdateFmt = new SimpleDateFormat("yyyy年MM月dd日 HH時mm分ss秒");
                Log.d("EditViewDateTime", sdateFmt.format(cal.getTime()));
            }});
        dlg.setNegativeButton("キャンセル", new DialogInterface.OnClickListener(){
            public void onClick(DialogInterface dialog, int which) {
                //Noボタンが押された時の処理
            }});
        // AlertDialogを表示する
        dlg.show();
    }
}

文字列を日付、時刻に分解してCalendarクラスに格納する関数は

    private Calendar Str2Date(String StrDate, String StrTime){
        Calendar calendarResult = Calendar.getInstance();
        calendarResult.clear();
        SimpleDateFormat format = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
        format.setLenient(false);   // 日時チェックを厳密に行う
        ParsePosition pos = new ParsePosition(0);
        try{
            calendarResult.setTime(format.parse(StrDate + " " + StrTime, pos));
        } catch(Exception e){
            calendarResult.setTimeInMillis(0);
            Log.d("Calendar", "Lenient check ERROR");
        };
        return(calendarResult);
    }

DatePickerDialogを用いて年月日を扱う方法

20140621-datepickdlg.jpg

ボタン関数のOnClickListner内で、DatePickerから日付を得るために、DatePickerインスタンスを一旦外に取り出して保存しておくのが、datePicker = datePickerDialog.getDatePicker();の部分。これはAndroid 3.0以降でしかサポートされないので、2.3以前でもサポートされる方法は、次のセクションのTimePickerDialogの例のような方法を取れば良い。

    private void Date_Test_func(){
        DatePickerDialog datePickerDialog;
        // 日付設定時のリスナ作成
        DatePickerDialog.OnDateSetListener DateSetListener = new DatePickerDialog.OnDateSetListener() {
            public void onDateSet(android.widget.DatePicker datePicker, int year,
                    int monthOfYear, int dayOfMonth) {
                // OKボタン、キャンセルボタンにかかわらず、ダイアログが閉じるときに呼び出される
                // *** Androidシステムのバグにより、
                //     OKボタンが押された後に、onDateSetが2回呼びだされることがある ***
                // ログにデバッグ出力
                Log.d("DatePicker", "year=" + String.valueOf(year) + " month=" + String.valueOf(monthOfYear + 1)
                        + " day=" + String.valueOf(dayOfMonth));
            }
        };
        // 現在日時をセットする
        Calendar calNow = Calendar.getInstance();
        datePickerDialog = new DatePickerDialog(this, DateSetListener,
                calNow.get(Calendar.YEAR), calNow.get(Calendar.MONTH), calNow.get(Calendar.DAY_OF_MONTH));
        // ボタンのOnClickListener関数内で用いるため、widgetをクラス変数にセットする
        final DatePicker datePicker;
        datePicker = datePickerDialog.getDatePicker();  // API11(Android 3)以降でサポート
        // 「キャンセル ボタン」を追加する
        datePickerDialog.setButton(
                DialogInterface.BUTTON_NEGATIVE, 
                "キャンセル", 
                new DialogInterface.OnClickListener() {
                  public void onClick(DialogInterface dialog, int which) {
                    // Negative Button がクリックされた時の動作
                  }
                }
              );
        // デフォルトで表示される「OK ボタン」処理を上書き
        datePickerDialog.setButton(
          DialogInterface.BUTTON_POSITIVE,
            "設定", 
            new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int which) {
                  // Positive Button がクリックされた時の動作
                  // とりあえず、ログに書き出す
                  Log.d("DatePicker", "Year=" + String.valueOf(datePicker.getYear()) +
                          ", Month=" + String.valueOf(datePicker.getMonth() + 1) +
                          ", Day=" + String.valueOf(datePicker.getDayOfMonth()));
              }
            }    
        );
        datePickerDialog.setMessage("年月日ダイアログのテスト"); 
        // ダイアログを表示する(Androidの場合モードレスダイアログ)
        datePickerDialog.show();
    }

TimePickerDialogを用いて時・分を扱う方法

20140621-timepickdlg.jpg

DatePickerDialogのようにインスタンスを得る関数(getDatePicker();)が存在しないため、OKボタンを押された時のフラグ変数を定義する。変数は1個で良いのだが、Javaコンパイラのエラーが出るため、配列として定義し、その1個目のみを使うというエラーの無理矢理回避を行っている。

    private void Time_Test_func(){
        TimePickerDialog timePickerDialog;
        // OKボタンを押した時 true をセットする(配列にするのは、『エンクロージング型に定義されているため、
        //      ローカル変数 を代入することができません』エラーの回避のため)
        final Boolean flagOk[] = new Boolean[1];
        flagOk[0] = false;
        //時刻設定時のリスナ登録
        TimePickerDialog.OnTimeSetListener TimeSetListener = new TimePickerDialog.OnTimeSetListener() {
            public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
                // OKボタン、キャンセルボタンにかかわらず、ダイアログが閉じるときに呼び出される
                // *** Androidシステムのバグにより、
                //     OKボタンが押された後に、onDateSetが2回呼びだされることがある ***
                // ログにデバッグ出力
                if(flagOk[0]){
                    Log.d("TimePicker","hourOfDay=" + String.valueOf(hourOfDay) + " minute=" + String.valueOf(minute));
                    flagOk[0] = false;
                }
                else{
                    Log.d("TimePicker","(キャンセルボタン またはAndroidバグでonSetTimeの呼び出しが2回めのためブロック");
                }
            }
        };
        // 現在日時をセットする
        Calendar calNow = Calendar.getInstance();
        timePickerDialog = new TimePickerDialog(this, TimeSetListener,
                calNow.get(Calendar.HOUR_OF_DAY), calNow.get(Calendar.MINUTE), true);
        // 「キャンセル ボタン」を追加する
        timePickerDialog.setButton(
                DialogInterface.BUTTON_NEGATIVE, 
                "キャンセル", 
                new DialogInterface.OnClickListener() {
                  public void onClick(DialogInterface dialog, int which) {
                    // Negative Button がクリックされた時の動作
                  }
                }
              );
        // デフォルトで表示される「OK ボタン」処理を上書き
        timePickerDialog.setButton(
          DialogInterface.BUTTON_POSITIVE,
            "設定", 
            new DialogInterface.OnClickListener() {
              public void onClick(DialogInterface dialog, int which) {
                  // Positive Button がクリックされた時の動作
                  flagOk[0] = true;
              }
            }    
        );
        // ダイアログを表示する(Androidの場合モードレスダイアログ)
        timePickerDialog.show();   
    }

AlertDialogでDatePickerを2つ表示する

20140621-date2-alertdlg.jpg

    private Calendar[] Add_Random_CallLog(){
        final LinearLayout layout = new LinearLayout(MainActivity.this);
        layout.setOrientation(LinearLayout.VERTICAL);
        
        TextView text1 = new TextView(this);
        text1.setText("集計開始日");
        text1.setTextColor(Color.LTGRAY);
        layout.addView(text1);
        
        final DatePicker date1 = new DatePicker(MainActivity.this);
        date1.setCalendarViewShown(false);
        Calendar calNow = Calendar.getInstance();
        calNow.add(Calendar.MONTH, -1);
        date1.updateDate(calNow.get(Calendar.YEAR), calNow.get(Calendar.MONTH), calNow.get(Calendar.DAY_OF_MONTH));
        layout.addView(date1);
        
        TextView text2 = new TextView(this);
        text2.setText("集計終了日");
        text2.setTextColor(Color.LTGRAY);
        layout.addView(text2);
        
        final DatePicker date2 = new DatePicker(MainActivity.this);
        date2.setCalendarViewShown(false);
        layout.addView(date2);
        
        // AlertDialogを構築する
        AlertDialog.Builder dlg = new AlertDialog.Builder(this);
        dlg.setTitle("処理期間の入力");
        dlg.setView(layout);
 
        dlg.setPositiveButton("決定", new DialogInterface.OnClickListener(){
            public void onClick(DialogInterface dialog, int which) {
                //Yesボタンが押された時の処理
                Log.d("date1 (start)", "year=" + String.valueOf(date1.getYear()) +
                        ", month=" + String.valueOf(date1.getMonth() + 1) + ", day=" + String.valueOf(date1.getDayOfMonth()));
                Log.d("date2 (end)", "year=" + String.valueOf(date2.getYear()) +
                        ", month=" + String.valueOf(date2.getMonth() + 1) + ", day=" + String.valueOf(date2.getDayOfMonth()));
            }});
        dlg.setNegativeButton("キャンセル", new DialogInterface.OnClickListener(){
            public void onClick(DialogInterface dialog, int which) {
                //Noボタンが押された時の処理
            }});
        // AlertDialogを表示する
        dlg.show();
    }