给JavaFX Control的Scroll Bar绑定方向键

By | 2017年4月26日

下面的例子中:

  • ctrl + 左方向键:水平滚动条向左移
  • ctrl + 右方向键:水平滚动条向右移
  • ctrl + 上方向键:垂直滚动条向上移
  • ctrl + 下方向键:垂直滚动条向下移

对于没有滚动条的控件,如果Button,上面的四个按键没有任何效果。

AdjustType:

  • UNIT:效果相当于点击滚动条两端的箭头,移动的少。
  • BLOCK:效果相当于点击滚动条上的空白,移动的多。
import javafx.event.EventHandler;
import javafx.scene.AccessibleAction;
import javafx.scene.AccessibleAttribute;
import javafx.scene.control.Control;
import javafx.scene.control.ScrollBar;
import javafx.scene.input.KeyCode;
import javafx.scene.input.KeyCodeCombination;
import javafx.scene.input.KeyCombination;
import javafx.scene.input.KeyEvent;
 
 
/**
 * Add key control for the scroll bar of any JavaFx control.
 * 
 * <code>
 *    TableView<!--?--> tableView = new TableView<>();
 *    
 *    // The first parameter can be any JavaFX control.
 *    // The second parameter specifies adjust type.
 *    tableView.setOnKeyPressed(new ControlScrollBarHandler(tableView, ADJUST_TYPE.UNIT));
 * 
 *    // Use default adjust type, which is AdjustType.BLOCK.
 *    tableView.setOnKeyPressed(new ControlScrollBarHandler(tableView));
 * </code>
 * 
 * @author alzhang
 */
public class ControlScrollBarHandler implements EventHandler<keyevent> {
 
  /**
   * The increment type of scroll bar.
   * 
   * @author alzhang
   */
  public enum AdjustType {
    /**
     * Same as clicking the ARROW of scroll bar.
     */
    UNIT, 
 
    /**
     * Same as clicking the empty space of scroll bar.
     */
    BLOCK;
  }
 
  /**
   * Control + LEFT Arrow 
   */
  private static final KeyCombination SCROLL_LEFT = 
      new KeyCodeCombination(KeyCode.LEFT, KeyCombination.CONTROL_DOWN);
  /**
   * Control + RIGHT Arrow 
   */
  private static final KeyCombination SCROLL_RIGHT = 
      new KeyCodeCombination(KeyCode.RIGHT, KeyCombination.CONTROL_DOWN);
  /**
   * Control + UP Arrow 
   */
  private static final KeyCombination SCROLL_UP = 
      new KeyCodeCombination(KeyCode.UP, KeyCombination.CONTROL_DOWN);
  /**
   * Control + DOWN Arrow 
   */
  private static final KeyCombination SCROLL_DOWN = 
      new KeyCodeCombination(KeyCode.DOWN, KeyCombination.CONTROL_DOWN);
 
  private final Control control;
  private final  AdjustType adjustType;
 
  private ScrollBar hScrollBar;
  private ScrollBar vScrollBar;
 
  /**
   * Construct an instance.
   *
   * @param control a control.
   */
  public ControlScrollBarHandler(Control control) {
    this(control, AdjustType.BLOCK);
  }
 
  /**
   * Construct an instance.
   *
   * @param control a control.
   * @param adjustType indicate how to adjust scroll bar.
   */
  public ControlScrollBarHandler(Control control, AdjustType adjustType) {
    super();
    this.control = control;
    this.adjustType = adjustType;
 
    // JavaFX creates scroll bar when initialize skin.
    if (control.skinProperty().get() != null) {
      initScrollBar();
    }
 
    // Update scroll bar when skin is changed.
    control.skinProperty().addListener((observable, oldSkin, newSkin) -> {
      initScrollBar();
    });
  }
 
  private void initScrollBar() {
    hScrollBar = ScrollBar.class.cast(control.queryAccessibleAttribute(AccessibleAttribute.HORIZONTAL_SCROLLBAR));
    vScrollBar = ScrollBar.class.cast(control.queryAccessibleAttribute(AccessibleAttribute.VERTICAL_SCROLLBAR));
  }
 
  @Override
  public void handle(KeyEvent keyEvent) {
    if (hScrollBar == null || vScrollBar == null) {
      return;
    }
 
    if (SCROLL_LEFT.match(keyEvent) &amp;&amp; hScrollBar != null) {
      adjustScrollBar(hScrollBar, false);
    } else if (SCROLL_RIGHT.match(keyEvent) &amp;&amp; hScrollBar != null) {
      adjustScrollBar(hScrollBar, true);
    } else if (SCROLL_UP.match(keyEvent) &amp;&amp; vScrollBar != null) {
      adjustScrollBar(vScrollBar, false);
    } else if (SCROLL_DOWN.match(keyEvent) &amp;&amp; vScrollBar != null) {
      adjustScrollBar(vScrollBar, true);
    }
  }
 
  private void adjustScrollBar(ScrollBar scrollBar, boolean isIncrement) {
    if (isIncrement) {
      if (AdjustType.UNIT.equals(adjustType)) {
        scrollBar.executeAccessibleAction(AccessibleAction.INCREMENT);
      } else {
        scrollBar.executeAccessibleAction(AccessibleAction.BLOCK_INCREMENT);
      }
    } else {
      if (AdjustType.UNIT.equals(adjustType)) {
        scrollBar.executeAccessibleAction(AccessibleAction.DECREMENT);
      } else {
        scrollBar.executeAccessibleAction(AccessibleAction.BLOCK_DECREMENT);
      }
    }
  }
 
}