Locked History Actions

JavaFX/MakeRowVisible

*************************************************

警告:このやり方はJava9では動作しません。エラーになります。

com.sunパッケージを使っている(使わざるをえなかった)からです。

Java9でも通用するやり方は以下にあります。

https://www.gwtcenter.com/post-75

*************************************************

TableView中の特定行を可視にする

JavaFXは複雑になっているにもかかわらずこんな機能も無い。つまり、テーブル中のある行が可視領域から外れてしまったときに、テーブルをスクロールしてその行が可視領域にきてもらいたい。 scrollTo(int index)という機能もあるが、これでは常に先頭行になってしまうため不自然。

There's no functionality of making the specified row visible in TableView. The following code does it. Of course there's builtin scrollTo(int) method, but that behavior is unnatural. I don't want to use it.

import com.sun.javafx.scene.control.skin.*;

import javafx.scene.control.*;

public class TableRowVisible {

  private TableView<?> tableView;
  
  public TableRowVisible(TableView<?> tableView) {
    this.tableView = tableView;
  }

  @SuppressWarnings("restriction")
  public VisibleRange getVisibleRange() {
    TableViewSkin<?> skin = (TableViewSkin<?>)tableView.getSkin();
    if (skin == null) {
      return new VisibleRange(0, 0);
    }
    VirtualFlow<?> flow = (VirtualFlow<?>)skin.getChildren().get(1);
    int indexFirst;
    int indexLast;
    if (flow != null && flow.getFirstVisibleCellWithinViewPort() != null
        && flow.getLastVisibleCellWithinViewPort() != null) {
      indexFirst = flow.getFirstVisibleCellWithinViewPort().getIndex();
      if (indexFirst >= tableView.getItems().size())
        indexFirst = tableView.getItems().size() - 1;
      indexLast = flow.getLastVisibleCellWithinViewPort().getIndex();
      if (indexLast >= tableView.getItems().size())
        indexLast = tableView.getItems().size() - 1;
    } else {
      indexFirst = 0;
      indexLast = 0;
    }
    return new VisibleRange(indexFirst, indexLast - 1); // indexLast may be "half" visible
  }

  /**
   * Make specified row visible.
   * @param index
   */
  public void makeVisible(int index) {
    VisibleRange range = getVisibleRange();
    if (range.contains(index))
      return;
    if (index < range.start) {
      tableView.scrollTo(index);
      return;
    }

    int newStart = index - range.count();
    tableView.scrollTo(newStart);
  }
  
  static class VisibleRange {
    final int start;
    final int end;

    VisibleRange(int start, int end) {
      this.start = start;
      this.end = end;
    }

    int count() {
      return end - start + 1;
    }

    boolean contains(int index) {
      return start <= index && index <= end;
    }

    @Override
    public String toString() {
      return start + "," + end + "," + count();
    }
  }
}