こんにちは、コンテンツクリエイターのともすけです。
この記事の内容は、初心者向けよりすこし難しいです。
Androidアプリを作る際に使用する部品の1つにTimePickerDialogがあります。後半で具体的な記述例を示しますので、お急ぎの方は下の方へスクロールしてください。

それってどういうやつなの?
これです。

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

何が問題なの?
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メソッドが呼び出されます。サンプルコードにも、英語で何かをしてねと書かれています。そう、ここに書けというんです。

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

何か対策はないの?
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
}
が呼び出されるということなんです。

うんうん、それで?
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(上に書いてあった定義)を追加し、その中に適切な記述を書きます。
こうすると、呼び出し元で設定された値を取得できるので、その値を使って各種制御に使えることになります。

そっかー、そういうことなんだね!(わかったつもり)
いかがでしょうか。ポイントは、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();
}
}
それではまた
