Освой Android играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Компонент NumberPicker находился в разделе Advanced старых версий студии. Компонент позволяет выбрать нужное число из заданного диапазона. Принцип работы похож на револьверный барабан - можно прокручивать числа в одну или другую сторону. Когда будет достигнут заданный предел, то числа продолжат изменяться в заданном диапазоне.
Учитывая, что компонент убрали из студии, можно предположить, что он оказался не очень востребованным.
Разместим компонент на экране.
<?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="match_parent"
android:orientation="vertical" >
<NumberPicker
android:id="@+id/numberPicker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:orientation="vertical"
android:width="100dp" />
</LinearLayout>
Несмотря на то, что есть атрибут orientation, толку от него немного. Работает только вертикальный режим.
Добавляем немного кода:
NumberPicker numberPicker = findViewById(R.id.numberPicker);
numberPicker.setMaxValue(9);
numberPicker.setMinValue(0);
numberPicker.descendantFocusability = NumberPicker.FOCUS_BLOCK_DESCENDANTS // блокируем появление клавиатуры
Мы определили максимальное и минимальные числа для нашего вида. Удивительно, но через XML нельзя установить эти значения. Запустив проект, вы можете теперь выбрать нужное число через стрелочки. Недавно проверял на другом телефоне (API 19) - стрелочек уже не было.
Метод setWrapSelectorWheel() с параметром false может отключить бесконечную прокрутку:
numberPicker.setWrapSelectorWheel(false);
Добавим на экран текстовую метку, чтобы отслеживать текущее выбранное значение.
package ru.alexanderklimov.hellokot
import android.os.Bundle
import android.widget.NumberPicker
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val numberPicker: NumberPicker = findViewById(R.id.numberPicker)
val textView: TextView = findViewById(R.id.main_text)
numberPicker.minValue = 0
numberPicker.maxValue = 9
numberPicker.wrapSelectorWheel = false
numberPicker.setOnValueChangedListener { picker, oldVal, newVal ->
textView.text = "Выбранное значение: $newVal"
}
}
}
Мы можем заменить числа на свои значения через метод setDisplayedValues(), например, на имена котов. В этом случае компонент приобретает какой-то смысл.
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.
package ru.alexanderklimov.hellokot
import android.os.Bundle
import android.widget.NumberPicker
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val cats = arrayOf("Барсик", "Мурзик", "Рыжик", "Васька", "Пушок", "Снежок")
val numberPicker: NumberPicker = findViewById(R.id.numberPicker)
val textView: TextView = findViewById(R.id.main_text)
numberPicker.minValue = 0
numberPicker.maxValue = cats.size - 1
numberPicker.wrapSelectorWheel = false
numberPicker.displayedValues = cats
numberPicker.setOnValueChangedListener { _, _, newVal ->
textView.text = "Выбранное значение: ${cats[newVal]}"
}
}
}
В API 29 появились новые методы для настройки компонента.
Применение части из них рассмотрим ниже.
В API 29 появился новый метод setSelectionDividerHeight(), позволяющий установить размер высоты для разделителей. Для старых версий можно написать функцию-расширение для доступа к низкоуровневым системным функциям.
fun NumberPicker.setDividerHeight(height: Int) {
val pickerFields = NumberPicker::class.java.declaredFields
for (pf in pickerFields) {
if (pf.name == "mSelectionDividerHeight") {
pf.isAccessible = true
try {
// set divider height in pixels
pf.set(this, height)
} catch (e: java.lang.IllegalArgumentException) {
// log exception here
} catch (e: Resources.NotFoundException) {
// log exception here
} catch (e: IllegalAccessException) {
// log exception here
}
break
}
}
}
// Установим размер высоты в пикселях для API 29+
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {
numberPicker.selectionDividerHeight = 20
} else{
numberPicker.setDividerHeight(20) // для старых версий
}
Схожая проблема существует с установкой цвета. Причём, в документации говорится, что в Android 3.0 можно установить цвет через атрибут divider или программно через setDividerDrawable(), но у меня не заработало. Возможно, это ошибка в документации, и должно работать в API 29+.
Поэтому напишем функцию-расширение.
fun NumberPicker.setDividerColor(color: Int) {
val pickerFields = NumberPicker::class.java.declaredFields
for (pf in pickerFields) {
if (pf.name == "mSelectionDivider") {
pf.isAccessible = true
try {
val colorDrawable = ColorDrawable(color)
pf[this] = colorDrawable
} catch (e: java.lang.IllegalArgumentException) {
e.printStackTrace()
} catch (e: Resources.NotFoundException) {
e.printStackTrace()
} catch (e: IllegalAccessException) {
e.printStackTrace()
}
break
}
}
}
// Установим нужный цвет
numberPicker.setDividerColor(Color.MAGENTA)
Опять же, согласно документации, в API 11 есть и атрибут showDividers и метод getShowDividers(), которые удаляют разделители. Но ничего не работает. Снова поможет функция-расширение.
fun NumberPicker.removeDivider() {
val pickerFields = NumberPicker::class.java.declaredFields
for (pf in pickerFields) {
if (pf.name == "mSelectionDivider") {
pf.isAccessible = true
try {
val colorDrawable = ColorDrawable(Color.TRANSPARENT)
pf[this] = colorDrawable
} catch (e: java.lang.IllegalArgumentException) {
// log exception here
} catch (e: Resources.NotFoundException) {
// log exception here
} catch (e: IllegalAccessException) {
// log exception here
}
break
}
}
}
// Не работает
// numberPicker.showDividers = LinearLayout.SHOW_DIVIDER_NONE
numberPicker.removeDivider()
Про остальные методы почитайте в документации.