0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

DialogFragmentで5分刻みのタイムピッカー

Last updated at Posted at 2021-12-24

はじめに

記事にするのを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);
    }

結果

分を一周すると時間が増えたり減ったりします。

picker.gif

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?