【问题标题】:TableView goes haywire when keeping arrow key pressed按住箭头键时,TableView 出现故障
【发布时间】:2019-09-09 14:58:39
【问题描述】:

通过使用此示例使用箭头键遍历可编辑的TableView

How to use arrow buttons to traverse cells in edit mode in TableView

按住向下箭头时会出现一个奇怪的问题。它似乎工作得很好,但如果你按下向下箭头键和向上箭头键,桌子就会开始陷入快速上下移动的循环中,你无法摆脱它。只有在表格上存在滚动条时才会发生这种情况,因此您必须向表格中添加一些项目。

这是我的代码:

Test.java

package test;

import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.scene.Scene;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.stage.Stage;

public class Test extends Application
{

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args)
    {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) throws Exception
    {
        TableView<Model> table = new TableView();
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());
        table.getItems().add(new Model());

        table.getSelectionModel().setCellSelectionEnabled(true);
        TableColumn<Model, String> column = new TableColumn<>("Column");
        column.setCellFactory(CustomTableCell.forTableColumn(i -> table.getItems().get(i).getNameProperty()));
        table.getColumns().add(column);

        final Scene scene = new Scene(table);

        primaryStage.setScene(scene);
        primaryStage.show();
    }

    public class Model
    {

        private SimpleStringProperty name;

        public Model()
        {
            name = new SimpleStringProperty();
        }

        /**
         * @return the string
         */
        public SimpleStringProperty getNameProperty()
        {
            return name;
        }

    }
}

CustomTableCell.java

package test;

import java.util.Objects;
import java.util.function.IntFunction;
import javafx.application.Platform;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ObjectProperty;
import javafx.beans.property.Property;
import javafx.beans.property.SimpleObjectProperty;
import javafx.event.EventDispatcher;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TextField;
import javafx.scene.input.KeyEvent;
import javafx.util.Callback;
import javafx.util.StringConverter;
import javafx.util.converter.DefaultStringConverter;

public class CustomTableCell<S, T> extends TableCell<S, T>
{

    public static <S> Callback<TableColumn<S, String>, TableCell<S, String>> forTableColumn(
            IntFunction<Property<String>> extractor)
    {
        return forTableColumn(extractor, new DefaultStringConverter());
    }

    public static <S, T> Callback<TableColumn<S, T>, TableCell<S, T>> forTableColumn(
            IntFunction<Property<T>> extractor, StringConverter<T> converter)
    {
        Objects.requireNonNull(extractor);
        Objects.requireNonNull(converter);
        return column -> new CustomTableCell<>(extractor, converter);
    }

    private final ObjectProperty<IntFunction<Property<T>>> extractor = new SimpleObjectProperty<>(this, "extractor");

    public final void setExtractor(IntFunction<Property<T>> callback)
    {
        extractor.set(callback);
    }

    public final IntFunction<Property<T>> getExtractor()
    {
        return extractor.get();
    }

    public final ObjectProperty<IntFunction<Property<T>>> extractorProperty()
    {
        return extractor;
    }

    private final ObjectProperty<StringConverter<T>> converter = new SimpleObjectProperty<>(this, "converter");

    public final void setConverter(StringConverter<T> converter)
    {
        this.converter.set(converter);
    }

    public final StringConverter<T> getConverter()
    {
        return converter.get();
    }

    public final ObjectProperty<StringConverter<T>> converterProperty()
    {
        return converter;
    }

    private Property<T> property;
    private TextField textField;

    public CustomTableCell(IntFunction<Property<T>> extractor, StringConverter<T> converter)
    {
        setExtractor(extractor);
        setConverter(converter);
    }

    @Override
    public void updateSelected(boolean selected)
    {
        super.updateSelected(selected);
        if (selected && !isEmpty())
        {
                Platform.runLater(() -> textField.requestFocus());
        }
    }

    @Override
    protected void updateItem(T item, boolean empty)
    {
        super.updateItem(item, empty);
        if (empty)
        {
            setText(null);
            setGraphic(null);
            clearProperty();
        } else
        {
            initializeTextField();
            clearProperty();

            property = getExtractor().apply(getIndex());
            Bindings.bindBidirectional(textField.textProperty(), property, getConverter());

            setGraphic(textField);
            if (isSelected())
            {
                Platform.runLater(() -> textField.requestFocus());

            }
        }
    }

    private void clearProperty()
    {
        if (property != null)
        {
            Bindings.unbindBidirectional(textField.textProperty(), property);
            textField.setText(null);
            property = null;
        }
    }

    private void initializeTextField()
    {
        if (textField == null)
        {
            textField = new TextField();
            textField.focusedProperty().addListener((observable, wasFocused, isFocused) ->
            {
                if (isFocused && !isSelected())
                {
                    getTableView().getSelectionModel().clearAndSelect(getIndex(), getTableColumn());
                }
            });

            /*
             * TableView has key handlers that will select cells based on arrow keys being
             * pressed, scrolling to them if necessary. I find this mechanism looks cleaner
             * because, unlike TableView#scrollTo, it doesn't cause the cell to jump to the
             * top of the TableView.
             *
             * The way this works is by bypassing the TextField if, and only if, the event
             * is a KEY_PRESSED event and the pressed key is an arrow key. This lets the
             * event bubble up back to the TableView and let it do what it needs to. All
             * other key events are given to the TextField for normal processing.
             *
             * NOTE: The behavior being relied upon here is added by the default TableViewSkin
             *       and its corresponding TableViewBehavior. This may not work if a custom
             *       TableViewSkin skin is used.
             */
            EventDispatcher oldDispatcher = textField.getEventDispatcher();
            textField.setEventDispatcher((event, tail) ->
            {
                if (event.getEventType() == KeyEvent.KEY_PRESSED
                        && ((KeyEvent) event).getCode().isArrowKey())
                {
                    return event;
                } else
                {
                    return oldDispatcher.dispatchEvent(event, tail);
                }
            });
        }
    }

}

【问题讨论】:

  • 我似乎无法重现该问题。您使用的是什么版本的 JavaFX?我使用 12.0.2 进行了测试。
  • 我使用的是 JavaFX8
  • 嗯。刚刚下载并尝试了包含 JavaFX 的 Java 8u222 的 Zulu 发行版,但仍然无法重现。如果您使用的是旧版本,那么可能已经修复了一个错误。按住向下箭头键,然后同时按住向上箭头键时应该会出现问题,对吗?因为也许我做错了。
  • 我测试了它,它发生在221上---你能不能按住“向下”键直接走到桌子底部,然后按住向上键走所有到达顶部?我就是这样做的。我确实想通了,“清除和选择”必须包含在 Platform.runlater...
  • 设法重现了 8u222 上的问题。似乎在选择一系列项目时卡住了。例如,它会选择项目 54,然后是 55,然后是 56,一直到 63,然后又回到 54——一遍又一遍;即使在释放密钥后也会发生这种情况。我不知道为什么会出现这个问题,但Platform.runLater 解决方案似乎确实有效。

标签: java javafx


【解决方案1】:

我想通了,我必须将“clearAndSelect”行包含在Platform.runLater()

getTableView().getSelectionModel().clearAndSelect(getIndex(), getTableColumn());

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-06-12
    • 1970-01-01
    • 1970-01-01
    • 2014-08-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多