Как стать автором
Обновить
556.21
OTUS
Цифровые навыки от ведущих экспертов

JSpinner: меньше багов, больше стрелочек

Уровень сложностиСредний
Время на прочтение5 мин
Количество просмотров409

Привет, Хабр!

Сегодня поговорим про JSpinner — инструмент, который может показаться чем-то простым и заурядным. Но это только на первый взгляд. На деле он может стать мастхев инструментом в разработке удобного и надёжного интерфейса.

JSpinner — это компонент для выбора значений из заранее заданной последовательности. Цифры, строки, даты — всё, что душе угодно. И его главная фича — минимизация ошибок: пользователь выбирает значения, а не вводит их вручную.

Типы моделей JSpinner

В основе каждого спиннера лежит его модель SpinnerModel. Это некий управленец, который определяет диапазоны значений, шаги и поведение при прокрутке. Разберём три основных типа:

SpinnerNumberModel

Числовая модель. Идеально для ситуаций, когда у вас есть фиксированный диапазон значений. Например:

JSpinner numberSpinner = new JSpinner(new SpinnerNumberModel(10, 0, 100, 5));
  • 10 — начальное значение.

  • 0 — минимальное значение.

  • 100 — максимальное значение.

  • 5 — шаг изменения.

SpinnerListModel

Позволяет выбирать значения из списка. Пример:

String[] days = {"Monday", "Tuesday", "Wednesday", "Thursday", "Friday"};
JSpinner listSpinner = new JSpinner(new SpinnerListModel(days));

Если вы передадите пустой список, компонент просто «сломается». Поэтому всегда проверяйте, что данные есть.

SpinnerDateModel

Работа с датами. Пример:

import java.util.Calendar;

Calendar calendar = Calendar.getInstance();
calendar.add(Calendar.YEAR, -1); // Минимальная дата - год назад
calendar.add(Calendar.YEAR, 2);  // Максимальная дата - через 2 года

SpinnerDateModel dateModel = new SpinnerDateModel(new Date(), calendar.getTime(), null, Calendar.DAY_OF_MONTH);
JSpinner dateSpinner = new JSpinner(dateModel);

С настройкой формата отображения:

JSpinner.DateEditor editor = new JSpinner.DateEditor(dateSpinner, "dd/MM/yyyy");
dateSpinner.setEditor(editor);

Кастомизация JSpinner

Теперь переходим к самому интересному. В реальных проектах вы не обойдётесь только стандартными настройками. Посмотрим, как кастомизировать JSpinner под конкретные задачи.

Изменение редактора

Стандартный редактор — это JFormattedTextField, но его можно заменить.

Пример:

JSpinner spinner = new JSpinner(new SpinnerNumberModel(1, 1, 10, 1));

// Настраиваем кастомный редактор
JSpinner.DefaultEditor editor = new JSpinner.DefaultEditor(spinner);
editor.getTextField().setColumns(10); // Увеличиваем ширину
spinner.setEditor(editor);

Обработка некорректного ввода

Случай, когда пользователь вручную вводит недопустимое значение:

try {
    spinner.commitEdit();
} catch (ParseException e) {
    JOptionPane.showMessageDialog(null, "Неверное значение! Возвращаем последнее валидное.");
    spinner.setValue(spinner.getPreviousValue());
}

Примеры использования

Панель настроек приложения

Допустим, есть десктопное приложение, которое обновляет данные с сервера каждые X секунд. Чтобы пользователь не ввёл «100 500» или «минус 42», вы ограничиваете диапазон и позволяете изменять настройки только через спиннер.

import javax.swing.*;
import java.awt.*;

public class UpdateSettings {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Update Settings");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 200);
            frame.setLayout(new FlowLayout());

            JLabel label = new JLabel("Update interval (seconds):");
            JSpinner spinner = new JSpinner(new SpinnerNumberModel(10, 1, 60, 1));

            // Логика изменения интервала
            spinner.addChangeListener(e -> {
                int interval = (Integer) spinner.getValue();
                System.out.println("Update interval set to: " + interval + " seconds");
                // Тут можно добавить метод для обновления таймера
            });

            frame.add(label);
            frame.add(spinner);
            frame.setVisible(true);
        });
    }
}

Бронирование времени

Теперь представим, что есть система записи на встречи, и нужно выбрать дату и время. Пользователь кликает, крутит стрелки, а вы спокойно спите, потому что все вводимые данные корректны.

import javax.swing.*;
import java.util.Calendar;
import java.util.Date;

public class BookingSystem {
    public static void main(String[] args) {
        SwingUtilities.invokeLater(() -> {
            JFrame frame = new JFrame("Booking System");
            frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
            frame.setSize(400, 300);
            frame.setLayout(new BoxLayout(frame.getContentPane(), BoxLayout.Y_AXIS));

            JLabel dateLabel = new JLabel("Select Date:");
            Calendar calendar = Calendar.getInstance();

            // Устанавливаем диапазон: сегодня и до месяца вперёд
            Date today = calendar.getTime();
            calendar.add(Calendar.MONTH, 1);
            Date maxDate = calendar.getTime();

            SpinnerDateModel dateModel = new SpinnerDateModel(today, today, maxDate, Calendar.DAY_OF_MONTH);
            JSpinner dateSpinner = new JSpinner(dateModel);
            JSpinner.DateEditor dateEditor = new JSpinner.DateEditor(dateSpinner, "dd/MM/yyyy");
            dateSpinner.setEditor(dateEditor);

            JLabel timeLabel = new JLabel("Select Time:");
            SpinnerDateModel timeModel = new SpinnerDateModel();
            JSpinner timeSpinner = new JSpinner(timeModel);
            JSpinner.DateEditor timeEditor = new JSpinner.DateEditor(timeSpinner, "HH:mm");
            timeSpinner.setEditor(timeEditor);

            JButton bookButton = new JButton("Book Appointment");
            bookButton.addActionListener(e -> {
                Date selectedDate = (Date) dateSpinner.getValue();
                Date selectedTime = (Date) timeSpinner.getValue();
                System.out.println("Appointment booked for: " + selectedDate + " at " + selectedTime);
                // Тут можно отправить данные на сервер или сохранить локально
            });

            frame.add(dateLabel);
            frame.add(dateSpinner);
            frame.add(timeLabel);
            frame.add(timeSpinner);
            frame.add(bookButton);

            frame.setVisible(true);
        });
    }
}

Что еще нужно знать про JSpinner

  1. Swing — не потокобезопасен
    Если работаете с GUI из другого потока — только через SwingUtilities.invokeLater().

  2. Собственные модели
    Стандартных мало? Напишите свою:

    class CustomSpinnerModel extends AbstractSpinnerModel {
        private int value = 0;
    
        @Override
        public Object getValue() { return value; }
    
        @Override
        public void setValue(Object value) {
            if (value instanceof Integer) {
                this.value = (Integer) value;
                fireStateChanged();
            }
        }
    
        @Override
        public Object getNextValue() { return value + 1; }
    
        @Override
        public Object getPreviousValue() { return value - 1; }
    }
    
    JSpinner customSpinner = new JSpinner(new CustomSpinnerModel());
  3. Ограничьте ввод
    Чтобы пользователь не ввёл лишнего, отключите редактирование напрямую:

    ((JSpinner.DefaultEditor) spinner.getEditor()).getTextField().setEditable(false);
  4. Форматируйте как хотите
    Например, показывайте числа с разделителями:

    spinner.setEditor(new JSpinner.NumberEditor(spinner, "#,###.##"));
  5. Обработка ошибок
    Пользователь ввёл что-то не то? Ловите и исправляйте:

    try {
        spinner.commitEdit();
    } catch (ParseException e) {
        System.out.println("Неверное значение, откатываемся!");
    }

Если у вас есть свои истории про JSpinner — делитесь в комментариях! А если ещё не использовали его в своих проектах — самое время попробовать.


Spring Framework занимает лидирующие позиции в экосистеме Java, но на рынке существуют и другие фреймворки, которые можно использовать для решения различных задач.

Приглашаем вас на открытый урок 27 января, посвященный изучению альтернативных фреймворков для разработки приложений на Java. На занятии мы разберем, почему Spring столь популярен, почему появились альтернативные решения, а также познакомимся с такими фреймворками, как Quarkus, Micronaut и Helidon. Записаться можно по ссылке.

Теги:
Хабы:
0
Комментарии0

Публикации

Информация

Сайт
otus.ru
Дата регистрации
Дата основания
Численность
101–200 человек
Местоположение
Россия
Представитель
OTUS