JavaFX 2.2 TableViewに単一選択のラジオボタン列を表示

見よう見まねで作ったけれど、お作法というか、やり方というか、合ってるのかしらん?

一覧画面から編集画面を開き、編集画面が閉じたタイミングで一覧画面にて再検索します。

package dbaccesstest;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ResourceBundle;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Modality;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;
import javafx.util.Callback;

/**
 * 一覧画面 Controller
 * 
 * @author denko
 */
public class SearchScreenController implements Initializable {

    @FXML
    private TableView tableView;
    @FXML
    private TableColumn selectColumn;
    @FXML
    private AnchorPane rootPane;

    @Override
    public void initialize(URL url, ResourceBundle rb) {
        tableView.setEditable(true);
        Callback<TableColumn, TableCell> cellFactory =
                new Callback<TableColumn, TableCell>() {
                    @Override
                    public TableCell call(TableColumn p) {
                        return new RadioButtonCell();
                    }
                };
        selectColumn.setCellFactory(cellFactory);

        // TableViewをデータ0件で初期化
        ObservableList<Customer> oList = FXCollections.observableArrayList(new ArrayList<Customer>());
        tableView.setItems(oList);
    }

    /**
     * 検索ボタン押下時処理
     * 
     * @param event 
     */    
    @FXML
    private void handleSearchButtonAction(ActionEvent event) {
        search();
    }

    /**
     * 顧客情報一覧検索処理
     */
    private void search() {
        tableView.getItems().clear();
        tableView.layout();// reflesh代わり http://javafx-jira.kenai.com/browse/RT-22599

        List<Customer> cList = CustomerAccess.selectAll();
        tableView.getItems().addAll(cList);
    }

    /**
     * 編集ボタン押下時処理
     * 
     * @param event
     * @throws IOException 
     */    
    @FXML
    private void handleEditButtonAction(ActionEvent event) throws IOException {
        Customer selectedCustomer = null;

        final ObservableList<Customer> items = tableView.getItems();
        for (Iterator<Customer> it = items.iterator(); it.hasNext();) {
            Customer customer = it.next();
            final Boolean selected = customer.getSelected();
            if (selected != null && selected) {
                selectedCustomer = customer;
                break;
            }
        }

        if (selectedCustomer == null) {
            showAlertStage("顧客情報が選択されていません。");
        } else {
            showUpdateStage(selectedCustomer);
        }
    }

    /**
     * アラート画面表示処理
     * 
     * @param message
     * @throws IOException 
     */
    private void showAlertStage(String message) throws IOException {
        Stage stage = new Stage();

        // FXMLファイルをロード
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("fxml/AlertScreen.fxml"));

        Scene scene = new Scene((Parent) fxmlLoader.load());
        stage.setScene(scene);

        // ロードしたFXMLファイルに関連づくControllerを取得
        final AlertScreenController controller = (AlertScreenController) fxmlLoader.getController();
        controller.setMessageText(message);

        stage.showAndWait();
    }

    /**
     * 編集画面表示処理
     * 
     * @param customer
     * @throws IOException 
     */
    private void showUpdateStage(Customer customer) throws IOException {
        // 次画面をモーダルで起動
        Stage newStage = new Stage();

        // モーダルで開いてみる
        newStage.initModality(Modality.APPLICATION_MODAL);
        newStage.initOwner(rootPane.getScene().getWindow());

        // FXMLファイルをロード
        FXMLLoader fxmlLoader = new FXMLLoader(getClass().getResource("fxml/UpdateScreen.fxml"));
        Scene scene = new Scene((Parent) fxmlLoader.load());// initialize が呼ばれる

        // ロードしたFXMLファイルに関連づくControllerを取得
        final UpdateScreenController controller = (UpdateScreenController) fxmlLoader.getController();
        controller.setId(customer.getId());

        newStage.setScene(scene);
        newStage.setOnShowing(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent t) {
                // 画面が開いたことをControllerに通知
                controller.onShown();
            }
        });
        
        // closeで閉じるとHiddenイベントが発生する
        // http://stackoverflow.com/questions/10212366/javafx-2-0-closing-a-stage-window
        newStage.setOnHidden(new EventHandler<WindowEvent>() {
            @Override
            public void handle(WindowEvent t) {
                // 編集画面終了時に自画面の再検索を行う
                search();
            }
        });
        // 新しいウインドウを表示
        newStage.show();
    }
    
    
    /**
     * RadioButtonCell用ラジオボタングループ
     */
    final ToggleGroup group = new ToggleGroup();
    /**
     * RadioButton型のTableCell
     */
    class RadioButtonCell extends TableCell<Customer, Boolean> {

        private RadioButton radio;

        public RadioButtonCell() {
            createRadioButton();
        }

        /**
         * ラジオボタン生成。
         * <p>
         * ラジオボタングループに登録する
         */
        private void createRadioButton() {
            radio = new RadioButton();
            radio.focusedProperty().addListener(new ChangeListener<Boolean>() {
                @Override
                public void changed(ObservableValue<? extends Boolean> arg0,
                        Boolean arg1, Boolean arg2) {
                    if (!arg2) {
                        commitEdit(radio.isSelected());
                    }
                }
            });
            radio.setToggleGroup(group);
        }

        @Override
        public void commitEdit(Boolean t) {
            super.commitEdit(t);
            final ObservableList<Customer> items = getTableView().getItems();
            for (int i = 0; i < items.size(); i++) {
                Customer customer = items.get(i);
                if (i == getIndex()) {
                    customer.setSelected(t);
                } else {
                    customer.setSelected(Boolean.FALSE);
                }
            }
        }

        @Override
        public void updateItem(Boolean item, boolean empty) {
            super.updateItem(item, empty);

            // データが存在する行のみラジオボタン表示
            // 引数のemptyは初期表示時全てfalseなのでこんな方法を採択
            final ObservableList<Customer> items = getTableView().getItems();
            if (items != null) {
                if (getIndex() < items.size()) {
                    setGraphic(radio);
                }
            }
        }
    }
}