はじめに
記事にするのを1年間忘れてたので、備忘録といっても今更役に立つやら。
一般的な手法なのかわかりませんが、
「5分刻み TimePicker」とかで検索すると、
Android標準のタイムピッカーを改造する例がちょいちょい見つかります。
しかしこのタイムピッカーが、
API30以上がターゲットの場合にクラッシュするようになりました。
https://developer.android.com/about/versions/11/non-sdk-11
APIへのアクセスが禁止されたものの中に、TimePickerがあります。
今は新しいTimePickerが提供されているので、そっちを使ってねってことみたいです。
でもダイアル式のタイムピッカーが使いたいので、
クラッシュ原因以外の部分だけ切り取って、それっぽいものに復元しました。
画面表示する都合で、
最終的に取得できる時間と分を数値にしています。
タイムピッカー
TimePickerDialogFragment.java
package com.example.myapplication;
import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.NumberPicker;
import androidx.fragment.app.DialogFragment;
import java.util.ArrayList;
import java.util.List;
public class TimePickerDialogFragment extends DialogFragment {
public interface TimePickerDialogFragmentDelegate {
void onClickPositiveButton(int hour, int minute);
// キャンセル押した時の処理を追加したい場合はコメントを外す
// void onClickNegativeButton();
}
private final static int TIME_PICKER_INTERVAL = 5; //5分刻みにする
private final static int MIN_VALUE_OF_HOUR = 0;
private final static int MAX_VALUE_OF_HOUR = 23;
private final static int MIN_VALUE_OF_MINUTE = 0;
private final static int MAX_VALUE_OF_MINUTE = 55;
// タイムピッカーをループさせるためのもの
private final static int TIME_PICKER_THRESHOLD_HOUR_ADD = 55;
private final static int TIME_PICKER_THRESHOLD_HOUR_REMOVE = -55;
private final int indexForHour;
private final int indexForMinute;
private final TimePickerDialogFragmentDelegate delegate;
public TimePickerDialogFragment(int hour,
int minute,
TimePickerDialogFragmentDelegate delegate) {
this.delegate = delegate;
this.indexForHour = hour;
this.indexForMinute = minute / TIME_PICKER_INTERVAL;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
final LayoutInflater inflater = (LayoutInflater) getActivity().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View view = inflater.inflate(R.layout.fragment_timepicker_dialog, null);
final NumberPicker numberPickerHour = view.findViewById(R.id.numpicker_hours);
numberPickerHour.setMinValue(MIN_VALUE_OF_HOUR);
numberPickerHour.setMaxValue(MAX_VALUE_OF_HOUR);
numberPickerHour.setValue(indexForHour);
final NumberPicker numberPickerMinutes = view.findViewById(R.id.numpicker_minutes);
numberPickerMinutes.setMinValue(MIN_VALUE_OF_MINUTE);
numberPickerMinutes.setMaxValue(MAX_VALUE_OF_MINUTE / TIME_PICKER_INTERVAL);
numberPickerMinutes.setDisplayedValues(createDisplayedValuesForMinute());
numberPickerMinutes.setValue(indexForMinute);
numberPickerMinutes.setOnValueChangedListener(new NumberPicker.OnValueChangeListener() {
@Override
public void onValueChange(NumberPicker picker, int oldVal, int newVal) {
final int diff = (oldVal - newVal) * TIME_PICKER_INTERVAL;
int val;
switch (diff) {
case TIME_PICKER_THRESHOLD_HOUR_ADD:
val = 1;
break;
case TIME_PICKER_THRESHOLD_HOUR_REMOVE:
val = -1;
break;
default:
return;
}
numberPickerHour.setValue(numberPickerHour.getValue() + val);
}
});
final Button negativeButton = view.findViewById(R.id.negativeButton);
final Button positiveButton = view.findViewById(R.id.positiveButton);
negativeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// キャンセル押した時の処理を追加したい場合はコメントを外す
// delegate.onClickNegativeButton();
dismiss();
}
});
positiveButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (delegate == null) {
return;
}
delegate.onClickPositiveButton(numberPickerHour.getValue(), numberPickerMinutes.getValue() * TIME_PICKER_INTERVAL);
dismiss();
}
});
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setView(view);
return builder.create();
}
private String[] createDisplayedValuesForMinute() {
List<String> displayedValues = new ArrayList<>();
for (int i = 0; i <= MAX_VALUE_OF_MINUTE; i += TIME_PICKER_INTERVAL) {
displayedValues.add(String.format( "%02d", i));
}
return displayedValues.toArray(new String[0]);
}
}
レイアウト
fragment_timepicker_dialog.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="16dp"
android:gravity="center"
android:orientation="horizontal">
<NumberPicker
android:id="@+id/numpicker_hours"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<NumberPicker
android:id="@+id/numpicker_minutes"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="16dp"
android:layout_marginRight="16dp" />
</LinearLayout>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#dcdcdc" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<Button
android:id="@+id/negativeButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#444444"
android:background="@android:color/transparent"
android:text="@android:string/cancel"
android:textAllCaps="false" />
<View
android:layout_width="1dp"
android:layout_height="match_parent"
android:background="#dcdcdc" />
<Button
android:id="@+id/positiveButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#444444"
android:background="@android:color/transparent"
android:text="@android:string/ok"
android:textAllCaps="false" />
</LinearLayout>
</LinearLayout>
呼び出し元
新規プロジェクトを作って、Hello World!のところに表示してみる。
MainActivity.java
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
TextView textView = findViewById(R.id.text);
final String tag = MainActivity.class.getName();
// ピッカーの初期値
final int hour = 0;
final int minute = 0;
final TimePickerDialogFragment dialog = new TimePickerDialogFragment(hour, minute,
(hourOfDay, miniteOfDay) -> {
textView.setText(String.format("%02d:%02d", hourOfDay, miniteOfDay));
});
dialog.show(getSupportFragmentManager(), tag);
}
結果
分を一周すると時間が増えたり減ったりします。