数字入力をダイヤル式のナンバーピッカーに変えて使いやすくした話。
例えば
musicLineでは、拍子の指定にテキスト入力を使っていました。
でも、テキスト入力だと
- 範囲がわからない
- キーボードが押しづらい
- 今の値を削除する必要がある
と色々不満があります。
このテキスト入力をナンバーピッカーに変えることで不満を解消しました。
テキスト入力は自由度が高いことが利点ですが、入力できる範囲を明示的に示したい場合などはナンバーピッカーを使用することで使いやすくなります。
ナンバーピッカーの欠点
ナンバーピッカーは範囲がある時は有効だと書きましたが、範囲が広すぎる場合はスワイプする回数が増えるため使いづらくなります。
その場合はテキスト入力やスライダーを使用した方がいいでしょう。
musicLineでは曲のテンポを指定する方法を、スライダーとテキスト入力で併用しています。
スライダーで直感的に操作できますが、直接指定したい時はテキスト入力で行います。また、テンポは一般的には300までの範囲ですが、それ以上を超えたい特殊の場合はテキスト入力で指定できるようにしています。
実装
Android標準のNumberPickerをポップアップでダイアログ表示できるようにラッパークラスを作成しました。
全コードを表示
<?xml version="1.0" encoding="utf-8"?> <layout xmlns:android="http://schemas.android.com/apk/res/android"> <data/> <FrameLayout android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"> <NumberPicker android:id="@+id/number_picker" android:layout_width="80dp" android:layout_height="150dp" android:layout_gravity="center" android:layout_margin="30dp" android:gravity="center" /> </FrameLayout> </layout>
class NumberPickerDialogFragment : BaseDialogFragment() { companion object { private const val BUNDLE_KEY_VALUE = "value" private const val BUNDLE_KEY_MIN = "min" private const val BUNDLE_KEY_MAX = "max" // 引数:初期値、最小値、最大値 fun createInstance(value: Int, min: Int, max: Int) = NumberPickerDialogFragment().also { fragment -> fragment.arguments = Bundle().apply { putInt(BUNDLE_KEY_VALUE, value) putInt(BUNDLE_KEY_MIN, min) putInt(BUNDLE_KEY_MAX, max) } } } var onResult: (Int) -> Unit = {} private lateinit var binding: DialogNumberPickerBinding override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DataBindingUtil.inflate( LayoutInflater.from(requireActivity()), R.layout.dialog_number_picker, null, false) binding.numberPicker.run { maxValue = requireArguments().getInt(BUNDLE_KEY_MAX) minValue = requireArguments().getInt(BUNDLE_KEY_MIN) value = requireArguments().getInt(BUNDLE_KEY_VALUE) wrapSelectorWheel = false // ループをOFFにする } return AlertDialog.Builder(requireActivity(), R.style.CustomSlimAlertDialog) .setView(binding.root).create() } override fun onPause() { super.onPause() // 実際、コールバックはsetResultListenerを使うべき onResult(binding.numberPicker.value) } }
<resources> <style name="CustomSlimAlertDialog" parent="Theme.AppCompat.Light.Dialog.Alert"> <item name="android:windowMinWidthMajor">0%</item> <item name="android:windowMinWidthMinor">0%</item> <item name="dialogCornerRadius">8dp</item> </style> </resources>
val numberPickerDialogFragment = NumberPickerDialogFragment .createInstance(4, 2, 8).apply { onResult = { number -> // ピックした数字を使う } } numberPickerDialogFragment.show(parentFragmentManager, "beat_picker")
ポイント
- NumberPickerをレイアウトに貼り付け
<NumberPicker android:id="@+id/number_picker" android:layout_width="80dp" android:layout_height="150dp" ...
- NumberPickerに範囲の最大・最小と初期値を設定
binding.numberPicker.run { maxValue = requireArguments().getInt(BUNDLE_KEY_MAX) minValue = requireArguments().getInt(BUNDLE_KEY_MIN) value = requireArguments().getInt(BUNDLE_KEY_VALUE) ...
- ダイアログ閉じるときに数字を取得
onResult = { number -> // ピックした数字を使う }
ちなみに
NumberPickerのdisplayedValues
を設定すると、選択肢に数字以外を含めることもできます。