汎用的な日付選択ダイアログ/Android(Java)



おことわり

このブログは個人的なプログラミング学習の備忘録を綴ったもので。この実装で動作することは確認済みですが、もっと簡単でいい方法があったり 、あるいはセキュリティや保守の面からあまり好ましい実装ではない可能性があります。また、コードに関する言い回し等も不正確なことが多々あると思われますので、参考にされる場合は十分に検証の上、自己責任でお願いします。
※尚、このブログは適宜加筆修正いたします。


こんにちはHideです。前回はカレンダー型ダイアログをやりましたが前回のやり方だと同じアプリ内で使いたい箇所ごとに同じコードを書かないといけないので今回はダイアログを表示する処理を別クラスにして使い回せるようにしてみました。

さて、まずメイン画面のレイアウトからです。

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    tools:context=".MainActivity"
    tools:ignore="HardcodedText,Autofill" >

    <EditText android:id="@+id/edit_text"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:cursorVisible="false"
        android:focusable="false"
        android:text=""
        android:inputType="date"
        android:textSize="32sp"
        android:hint="選択した日付"/>

    <Button android:id="@+id/dateButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:layout_gravity="center"
        android:onClick="calendarButton"
        android:text="日付選択"/>
</LinearLayout>

これは前回と全く同じなので説明は割愛します。
前回のブログはこちら
 


次にダイアログを表示するクラスを作ります。
呼び出し元のActivityに結果を返さなければいけないのでDialogFragmentを継承せずAppCompatActivityを継承しています。 Viewはセットしていません。

CalendarDialog.java

import androidx.appcompat.app.AppCompatActivity;
import android.content.DialogInterface;
import android.os.Bundle;
import android.app.DatePickerDialog;
import android.widget.DatePicker;
import android.content.Intent;
import java.util.Locale;
import java.util.Calendar;

public class CalendarDialog extends AppCompatActivity {

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        Calendar calendar = Calendar.getInstance();
        DatePickerDialog datePickerDialog = new DatePickerDialog(this,
            new DatePickerDialog.OnDateSetListener() {
                @Override
                public void onDateSet(DatePicker view, int year, int month, int dayOfMonth) {
                    String str = (String.format(Locale.JAPAN, "%02d / %02d / %02d", year, month + 1, dayOfMonth));
                    Intent intent = new Intent();
                    intent.putExtra("selected_date", str);
                    setResult(RESULT_OK, intent);
                    finish();
                }
            },
            calendar.get(Calendar.YEAR),
            calendar.get(Calendar.MONTH),
            calendar.get(Calendar.DAY_OF_MONTH)
        );
    //キャンセル時の処理
        datePickerDialog.setButton(
            DialogInterface.BUTTON_NEGATIVE,
            "キャンセル",
            new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    finish();
                }
            }
        );
    //ダイアログの外側のタッチ操作を無効にしています。
        datePickerDialog.setCanceledOnTouchOutside(false);
    
        datePickerDialog.show();
    }
}

前回の日付選択ボタンの処理をそのままonCreateに移して、onDateSetメソッドの内容を書き換えてます。
あと今回はキャンセルボタンで同時にActivityも終了させたいのでキャンセル時の処理を追加して、ダイアログの外側のタッチ操作は無効にしています。

onDateSetでは呼び出し元へ返す日付データをsetResultにセットしています。
このActivityが終了すると自動的に呼び出し元のonActivityResult()メソッドが呼ばれます。

onDateSet()

// String型の変数を用意して呼び出し元へ返す文字列を代入します。
String str = (String.format(Locale.JAPAN, "%02d / %02d / %02d", year, month + 1, dayOfMonth));

//Intentをインスタンス化し、putExtraで”キー”と値をセットします。
  Intent intent = new Intent();
  intent.putExtra("selected_date", str);

//setResultメソッドにResultCodeとintentをセットします。
  setResult(RESULT_OK, intent);

//activityを終了します。
  finish();


RESULT_OK はActivityクラスの定数で値は -1 です。

では、次にMainActivityです。

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.EditText;

public class MainActivity extends AppCompatActivity {
  
    //呼び出し元を識別するための定数
    private final int REQUEST_CODE = 0;

    EditText editText;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

         editText = findViewById(R.id.edit_text);
    }
    
    public void calendarButton(View view) {
        Intent intent = new Intent(MainActivity.this, CalendarDialog.class);
        startActivityForResult(intent, REQUEST_CODE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data){
        super.onActivityResult(requestCode, resultCode, data);
        if(requestCode == REQUEST_CODE && resultCode == RESULT_OK){
            editText.setText(data.getStringExtra("selected_date"));
        }
    }
}

今回の日付選択ボタン(calendarButton)ではIntentをインスタンス化し、startActivityForResult()メソッドでActivityを遷移しているだけです。

startActivityForResult()は遷移先から結果を返してもらうことを前提にしたstartActivityメソッドで、第2引数には呼び出し元を識別するための定数をセットしています。

遷移先から返される値はonActivityResult()メソッドで 受け取ります。
引数のrequestCode と 定数のREQUEST_CODEが同じで、且つ、resultCode が定数RESULT_OKかどうかを判定してeditTextにIntentで渡された日付データをセットしています。


このままだとMainActivityの上にダイアログが開いたように見えないのでCalendarDialogクラス用のテーマをstyles.xmlで用意してManifestで適用しています。

styles.xml

<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.NoActionBar">
       ...省略...
    </style> 

    <!-- CaalendarDialog用のtheme -->
    <style name="AppThemeDialog" parent="Theme.AppCompat.Light.Dialog">
        <item name="windowNoTitle">true</item>
    </style>

</resources>


AndroidManifest.xml

<application  ...省略... >

    <activity android:name=".CalendarDialog"
              android:theme="@style/AppThemeDialog"/>
</application>

これで前回と同じ挙動になります。
この方法ならCalendarDialogクラスは別のアプリでもコピペで使い回せますよね。
もっといい方法があると思いますが
それはそれ、これはこれということで(^^;)

さて、では今回はこの辺で、
最後までご拝読いただきありがとうございました。m(_ _)m


スポンサーリンク


コメントは受け付けていません。