EU向けの同意バナーを停止しました
【Androidアプリ開発】TimePickerDialogを実用的に使う方法

【Androidアプリ開発】TimePickerDialogを実用的に使う方法

2020年5月2日

こんにちは、コンテンツクリエイターのともすけです。

この記事の内容は、初心者向けよりすこし難しいです。

Androidアプリを作る際に使用する部品の1つにTimePickerDialogがあります。後半で具体的な記述例を示しますので、お急ぎの方は下の方へスクロールしてください。

tomosuke
tomosuke

それってどういうやつなの?

これです。

TimePickerDialog

時間を指定して分を指定して、OKを押して使います。ですが…問題が。

tomosuke
tomosuke

何が問題なの?

Android Developerサイトにサンプルが書かれているのですが、設定した値を取り込む方法が書かれていません。つまりはこういうこと。

public class TimePickerFragment extends DialogFragment
        implements TimePickerDialog.OnTimeSetListener {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);
        // Create a new instance of TimePickerDialog and return it
        return new TimePickerDialog(getActivity(), this, hour, minute,
                DateFormat.is24HourFormat(getActivity()));
    }
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time chosen by the user
    }
}

*Android Developerサイトから引用

この記述をTimePickerDialog.javaとしてプロジェクトに組み込み、

public void showTimePickerDialog(View v) {
    DialogFragment newFragment = new TimePickerFragment();
    newFragment.show(getSupportFragmentManager(), "timePicker");
}

*Android Developerサイトから引用

をプログラム上で呼び出せばいいと書いてあります。呼び出すと、先程の画面が表示されます。入力を終えてOKを押すと、onTimeSetメソッドが呼び出されます。サンプルコードにも、英語で何かをしてねと書かれています。そう、ここに書けというんです。

tomosuke
tomosuke

で、何が問題なの?

設定された値をどうやって呼び出し元のclassに渡すか。ウェブ上に点在する記事によれば、どれもログを表示するとか、その程度の実用レベルとは程遠い例しかないんです。表示するだけでは意味がないんです。制御に使えないと意味がないんです…。

tomosuke
tomosuke

何か対策はないの?

SharedPreferencesを使おうかとも思いましたが、これは値を書き込んだ後すぐに反映されない場合があって、そのため呼び出し元ですぐにリードすると設定した値が読めないことが多々あります。

いろいろ調べたり考えたりして、1つ気がついたことがありました。

return new TimePickerDialog(getActivity(), this, hour, minute,
                DateFormat.is24HourFormat(getActivity()));

第2引数がthisなんですけど、これはlistener型なんです。listenerって、これのことです。

public class TimePickerFragment extends DialogFragment
        implements TimePickerDialog.OnTimeSetListener {

二行目に、OnTimeSetListenerと書かれています。このように書くと、時間設定が終わってOKが押されたときに

    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time chosen by the user
    }

が呼び出されるということなんです。

tomosuke
tomosuke

うんうん、それで?

Dialogのclassにlistenerを記述しないで、呼び出し元に定義し、それを指定すればいいということなんです。つまり、Dialogのclass記述は結果的には

public class TimePickerFragment extends DialogFragment
        /*implements TimePickerDialog.OnTimeSetListener*/ {
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        // Use the current time as the default values for the picker
        final Calendar c = Calendar.getInstance();
        int hour = c.get(Calendar.HOUR_OF_DAY);
        int minute = c.get(Calendar.MINUTE);
        // Create a new instance of TimePickerDialog and return it
        /*return new TimePickerDialog(getActivity(), this, hour, minute,
                DateFormat.is24HourFormat(getActivity()));*/
        return new TimePickerDialog(getActivity(), (RegisterActivity) getActivity(), hour, minute,
                DateFormat.is24HourFormat(getActivity()));
    }
    /*public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        // Do something with the time chosen by the user
    }*/
}

となります。修正内容は、

  • listenerを他で定義するので、implementsの行はコメントアウト
  • returnの行で、第2引数がthisとなっていたところは(RegisterActivity)getActivity()となります。RegisterActivityは、TimePickerDialogを呼び出す元のActivityです。呼び出し元は人によって違うので、参考程度としてください。
  • 呼び出し元のActivityにimplements…の行を追加して、public void onTimeSet(上に書いてあった定義)を追加し、その中に適切な記述を書きます。

こうすると、呼び出し元で設定された値を取得できるので、その値を使って各種制御に使えることになります。

tomosuke
tomosuke

そっかー、そういうことなんだね!(わかったつもり)

いかがでしょうか。ポイントは、Dialogを定義するclassの中にlistenerを設定せず、呼び出し元のclassに定義して呼び出すことです。

MainActivityのサンプルを掲載しておきますね。

import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.DialogFragment;

import android.app.TimePickerDialog;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TimePicker;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity implements TimePickerDialog.OnTimeSetListener{

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        LinearLayout baseLayout = new LinearLayout(this);
        baseLayout.setOrientation(LinearLayout.VERTICAL);
        Button button = new Button(this);
        baseLayout.addView(button);
        setContentView(baseLayout);
        button.setText("ダイアログを表示");
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                showTimePickerDialog(v);
            }
        });
    }

    public void showTimePickerDialog(View v) {
        DialogFragment newFragment = new TimerPickerFragment();
        newFragment.show(getSupportFragmentManager(), "timePicker");
    }

    @Override
    public void onTimeSet(TimePicker view, int hourOfDay, int minute) {
        Toast.makeText(getApplicationContext(), String.format("%d時%d分がセットされました。", hourOfDay, minute), Toast.LENGTH_SHORT).show();
    }
}

それではまた