開発メモ

開発用のメモです。

JavaFX でデータピッカー

f:id:alice_asahina:20141209172010p:plain

FXML

<?xml version="1.0" encoding="UTF-8"?>
    
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.TextField?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.control.Button?>
<?import javafx.scene.control.ScrollPane?>
<?import javafx.scene.control.TabPane?>
<?import javafx.scene.control.Tab?>
<?import javafx.scene.control.TableView?>
<?import javafx.scene.control.TableColumn?>
<?import javafx.scene.control.cell.PropertyValueFactory?>
<?import javafx.scene.text.Font?>
<?import javafx.collections.FXCollections?>
<?import javafx.scene.control.ComboBox?>
<?import java.lang.String?>

<AnchorPane xmlns:fx="http://javafx.com/fxml" fx:controller="application.DatePickerController">
    <ComboBox prefWidth="100" prefHeight="30" fx:id="year" onAction="#changeYearMonth" />
    <Label text="年" AnchorPane.leftAnchor="100" prefWidth="50" prefHeight="30" alignment="CENTER" />
    <ComboBox prefWidth="100" prefHeight="30" AnchorPane.leftAnchor="150" fx:id="month" onAction="#changeYearMonth" />
    <Label text="月" AnchorPane.leftAnchor="250" prefWidth="50" prefHeight="30" alignment="CENTER" />
    <AnchorPane AnchorPane.topAnchor="40">
        <Button text="前年" onAction="#pvYear" prefWidth="40" AnchorPane.leftAnchor="10" />
        <Button text="前月" onAction="#pvMonth" prefWidth="40" AnchorPane.leftAnchor="65" />
        <Button text="当月" onAction="#toMonth" prefWidth="40" AnchorPane.leftAnchor="120" />
        <Button text="次月" onAction="#nxMonth" prefWidth="40" AnchorPane.leftAnchor="175" />
        <Button text="次年" onAction="#nxYear" prefWidth="40" AnchorPane.leftAnchor="230" />
    </AnchorPane>
    <AnchorPane AnchorPane.topAnchor="80" AnchorPane.leftAnchor="20" prefWidth="270" prefHeight="280" fx:id="dataPane" />
    <AnchorPane AnchorPane.bottomAnchor="10" AnchorPane.leftAnchor="10" prefWidth="270" prefHeight="20">
        <Button text="設定" AnchorPane.leftAnchor="0" onAction="#doSet" />
        <Button text="クリア" AnchorPane.rightAnchor="50" onAction="#doClear" />
        <Button text="閉じる" AnchorPane.rightAnchor="0" onAction="#doClose" />
    </AnchorPane>
</AnchorPane> 

Java / Controller

package application;

import java.net.URL;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;
import java.util.ResourceBundle;

import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.Event;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.Scene;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.AnchorPane;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;

public class DatePickerController implements Initializable {

    protected static final Logger logger;
    static {
        logger = Logger.getLogger(DatePickerController.class);
    }

    @FXML
    public ComboBox<Integer> year;
    @FXML
    public ComboBox<Integer> month;
    @FXML
    public AnchorPane dataPane;

    private TextField target;

    public Scene scene;

    List<RadioButton> buttons = new ArrayList<RadioButton>();

    ToggleGroup group = new ToggleGroup();

    public void setTarget(TextField target) throws Exception {
        this.target = target;
        if (target == null) {
            return;
        }
        String regex = "^([0-9]{4})[^0-9][0]?([0-9]{1,2})[^0-9][0]?([0-9]{1,2})[^0-9]*$";
        if (target.getText().matches(regex)) {
            String temp = target.getText().replaceAll(regex, "$1/$2/$3");
            Calendar c = Calendar.getInstance();
            c.setTime(new SimpleDateFormat("yyyy/M/d").parse(temp));

            year.setValue(c.get(Calendar.YEAR));
            month.setValue(c.get(Calendar.MONTH) + 1);

            changeYearMonth(null);

            for (int i = c.get(Calendar.DAY_OF_MONTH) - 1; i < buttons.size(); i++) {
                RadioButton r = buttons.get(i);
                String value = String.valueOf(r.getUserData());
                String test = Integer.toString(c.get(Calendar.DAY_OF_MONTH));
                r.setSelected(StringUtils.equals(value, test));

                if (r.isSelected()) {
                    break;
                }
            }
        }

    }

    @Override
    public void initialize(URL location, ResourceBundle resources) {
        List<Integer> yearOptions = new ArrayList<Integer>();
        for (int i = 1900; i <= 2050; i++) {
            logger.debug("年 : " + i);
            yearOptions.add(i);
        }
        List<Integer> monthOptions = new ArrayList<Integer>();
        for (int i = 1; i <= 12; i++) {
            logger.debug("月 : " + i);
            monthOptions.add(i);
        }

        year.setItems(FXCollections.observableArrayList(yearOptions));
        month.setItems(FXCollections.observableArrayList(monthOptions));

        year.setValue(Calendar.getInstance().get(Calendar.YEAR));
        month.setValue(Calendar.getInstance().get(Calendar.MONTH) + 1);

        changeYearMonth(null);
    }

    @FXML
    public void changeYearMonth(ActionEvent event) {
        if (isNull(year) || isNull(month)) {
            return;
        }
        Calendar calendar1;
        Calendar calendar2;
        int y = year.getValue();
        int m = month.getValue() - 1;
        calendar1 = new GregorianCalendar(y, m, 1);

        while (calendar1.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
            calendar1.add(Calendar.DATE, -1); // ���j���ɂȂ�܂őO����
        }

        calendar2 = new GregorianCalendar(y, m, 1);
        calendar2.add(Calendar.MONTH, 1);
        while (calendar2.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) {
            calendar2.add(Calendar.DATE, 1); // ���j���ɂȂ�܂ŗ�����
        }

        int target = Calendar.DAY_OF_YEAR;
        double date = 0D;

        dataPane.getChildren().clear();
        buttons.clear();
        while (calendar1.get(target) != calendar2.get(target)) {

            AnchorPane pane = new AnchorPane();
            pane.setPrefSize(30, 40);

            Label lab = new Label(day(calendar1));
            lab.setPrefSize(30D, 20D);

            RadioButton radio = new RadioButton();
            radio.setPrefSize(30, 20);
            radio.setToggleGroup(group);
            radio.setDisable(calendar1.get(Calendar.MONTH) != (month.getValue() - 1));
            radio.setUserData(day(calendar1));

            AnchorPane.setTopAnchor(lab, 0D);
            AnchorPane.setBottomAnchor(radio, 0D);

            pane.getChildren().addAll(lab, radio);

            AnchorPane.setTopAnchor(pane, Math.floor(date / 7) * 40D);
            AnchorPane.setLeftAnchor(pane, Math.floor(date % 7) * 40D);

            dataPane.getChildren().add(pane);
            date++;

            buttons.add(radio);

            calendar1.add(Calendar.DATE, 1);
        }

    }

    @FXML
    public void doSet(Event event) {
        logger.info("#doSet2");
        target.setText(new SimpleDateFormat("yyyy.MM.dd").format(value()));
        scene.getWindow().hide();
    }

    @FXML
    public void doClear(Event event) {
        logger.info("#doClear2");
        target.setText("");
        scene.getWindow().hide();
    }

    @FXML
    public void doClose(Event event) {
        logger.info("#doClose2");
        scene.getWindow().hide();
    }

    @FXML
    public void pvYear(Event event) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year.getValue());
        c.set(Calendar.MONTH, month.getValue() - 1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.add(Calendar.YEAR, -1);
        setYearMonth(c);
    }

    @FXML
    public void nxYear(Event event) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year.getValue());
        c.set(Calendar.MONTH, month.getValue() - 1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.add(Calendar.YEAR, 1);
        setYearMonth(c);
    }

    @FXML
    public void pvMonth(Event event) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year.getValue());
        c.set(Calendar.MONTH, month.getValue() - 1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.add(Calendar.MONTH, -1);
        setYearMonth(c);
    }

    @FXML
    public void nxMonth(Event event) {
        Calendar c = Calendar.getInstance();
        c.set(Calendar.YEAR, year.getValue());
        c.set(Calendar.MONTH, month.getValue() - 1);
        c.set(Calendar.DAY_OF_MONTH, 1);
        c.add(Calendar.MONTH, 1);
        setYearMonth(c);
    }

    @FXML
    public void toMonth(Event event) {
        Calendar c = Calendar.getInstance();
        setYearMonth(c);
    }

    private void setYearMonth(Calendar c) {
        year.setValue(c.get(Calendar.YEAR));
        month.setValue(c.get(Calendar.MONTH) + 1);
        changeYearMonth(null);
    }

    private String day(Calendar c) {
        return String.format("%d", c.get(Calendar.DAY_OF_MONTH));
    }

    private boolean isNull(ComboBox<Integer> x) {
        return x == null || x.getValue() == null;
    }

    private Date value() {
        int y = year.getValue();
        int m = month.getValue();
        int d = 1;

        for (RadioButton button : buttons) {
            if (button.isSelected()) {
                d = Integer.valueOf(button.getUserData().toString());
            }
        }
        return new GregorianCalendar(y, m, d).getTime();
    }

}

起動

   private void doDatePicker(TextField target, Event event) throws Exception {
        if (event.getSource() instanceof Button) {
            Window owner = ((Button) event.getSource()).getScene().getWindow();

            FXMLLoader loader = new FXMLLoader(getResource("DatePicker.fxml"));
            loader.load();

            Pane root = loader.getRoot();
            Scene scene = new Scene(root);

            Stage stage = new Stage(StageStyle.UTILITY);
            stage.setScene(scene);
            stage.initOwner(owner);
            stage.setResizable(false);
            stage.initModality(Modality.WINDOW_MODAL);

            DatePickerController dpc = loader.getController();
            dpc.setTarget(target);
            dpc.scene = scene;

            stage.showAndWait();
        }
    }
Twitter: @asahina_alice