2016-09-10 14 views
7

私は2年前からJavaFXを使用しています。今、私は、JavaFXを使用してコントロールのようなスプレッドシートを作成しています。コントロールを作成しています。私は、スクロールテーブル、最初のテーブルをスクロールするときに、スクロールペインの行は同期してスクロールしますが、スクロールした後は開始の不一致があります。同じスクリーンショットを添付しました。JavaFX TableviewとScrollPaneスクロールの問題

1)初期画面スクロールなし

enter image description here

2)画面の一部のスクロールの後に続いて

enter image description here

は、私はペーストビンに貼り付けられている。この制御のためのコードスニペットです。

1)Cells.java

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.stage.Stage; 

public class Cells extends Application { 

    public void start(Stage stage) { 
     stage.setScene(new Scene(new SpreadSheet(100, 26))); 
     stage.setTitle("Cells"); 
     stage.setWidth(400); 
     stage.setHeight(400); 
     stage.show(); 
    } 

    public static void main(String[] args) { 
     launch(args); 
    } 

} 

2)SpreadSheet.java

import javafx.collections.ListChangeListener; 
import javafx.collections.ObservableList; 
import javafx.event.EventHandler; 
import javafx.geometry.Insets; 
import javafx.scene.Node; 
import javafx.scene.control.*; 
import javafx.scene.control.cell.TextFieldTableCell; 
import javafx.scene.input.ScrollEvent; 
import javafx.scene.layout.HBox; 


public class SpreadSheet extends HBox { 

    public SpreadSheet(int height, int width) { 
     super(); 
     Model model = new Model(height, width); 

     TableView<ObservableList<Model.Cell>> table = new TableView<>(); 
     table.setEditable(true); 
     table.setItems(model.getCellsAsObservableList()); 

     for (char w = 'A'; w < 'A'+width; w++) { 
      TableColumn<ObservableList<Model.Cell>, String> column = new TableColumn<>(w+""); 
      column.setSortable(false); 
      column.setMinWidth(50); 
      column.setCellFactory(TextFieldTableCell.forTableColumn()); 
      final char w0 = w; 
      column.setCellValueFactory(param -> param.getValue().get(w0-'A').text); 
      column.setOnEditStart(event -> { 
       int row = event.getTablePosition().getRow(); 
       int col = event.getTablePosition().getColumn(); 
       Model.Cell c = model.getCells()[row][col]; 
       c.setShowUserData(true); 
      }); 

      column.setOnEditCommit(event -> { 
       int row = event.getTablePosition().getRow(); 
       int col = event.getTablePosition().getColumn(); 
       Model.Cell c = model.getCells()[row][col]; 
       System.out.println("Hello"); 
       c.userData.set(event.getNewValue()); 
       c.setShowUserData(false); 
      }); 
      table.getColumns().add(column); 
     } 

     ListView<String> rowHeaders = new ListView<>(); 
     rowHeaders.getItems().add(""); 
     for (int i = 0; i < height; i++) { 
      rowHeaders.getItems().add(i+""); 
      } 
     ScrollPane scrolledRowHeaders = new ScrollPane(rowHeaders); 
     scrolledRowHeaders.setHbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); 
     scrolledRowHeaders.setVbarPolicy(ScrollPane.ScrollBarPolicy.NEVER); 


     table.getChildrenUnmodifiable().addListener((ListChangeListener<Node>) c -> { 
      ScrollBar vbarTable = (ScrollBar) table.lookup(".scroll-bar:vertical"); 
      ScrollBar vbarRowHeaders = (ScrollBar) scrolledRowHeaders.lookup(".scroll-bar:vertical"); 

      if (vbarRowHeaders != null && vbarTable != null) 
       vbarTable.valueProperty().bindBidirectional(vbarRowHeaders.valueProperty()); 


     }); 

     getChildren().addAll(scrolledRowHeaders, table); 

    } 
} 

3)Model.java

import java.util.List; 

import javafx.beans.binding.Binding; 
import javafx.beans.binding.Bindings; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 
import javafx.beans.value.ObservableValue; 
import javafx.collections.FXCollections; 
import javafx.collections.ObservableList; 

class Model { 

    private Cell[][] cells; 

    Model(int height, int width) { 
     cells = new Cell[height][width]; 
     for (int i = 0; i < height; i++) { 
      for (int j = 0; j < width; j++) { 
       cells[i][j] = new Cell(); 
      } 
     } 
    } 

    public Cell[][] getCells() { 
     return cells; 
    } 

    public ObservableList<ObservableList<Cell>> getCellsAsObservableList() { 
     ObservableList<ObservableList<Cell>> cs = FXCollections.observableArrayList(); 
     for (int i = 0; i < cells.length; i++) { 
      cs.add(FXCollections.observableArrayList()); 
      for (int j = 0; j < cells[i].length; j++) { 
       cs.get(i).add(cells[i][j]); 
      } 
     } 
     return cs; 
    } 

    class Cell { 

     public final StringProperty userData = new SimpleStringProperty(""); 
     public final StringProperty text = new SimpleStringProperty(""); 
     ObservableValue<Double>[] toArray(List<ObservableValue<Double>> l) { 
       return l.toArray(new ObservableValue[l.size()]); 
     } 
     // Has same problem 
//  public ObservableValue<Double> value = EasyBind.map(userData, Parser::parse) 
//    .flatMap(f -> Bindings.createObjectBinding(() -> f.eval(Model.this), toArray(f.getReferences(Model.this)))); 
     // Has same problem 
     public ObservableValue<Double> value = 
       Bindings.createObjectBinding(() -> { 
        System.out.println(System.currentTimeMillis()); 
        Formula f = Parser.parse(userData.get()); 
        ObservableValue<Double>[] fs = toArray(f.getReferences(Model.this)); 
        Binding<Double> d = Bindings.createObjectBinding(() -> { 
         double v = f.eval(Model.this); 
//      text.set(String.valueOf(v)); 
         return v; 
        }, fs); 
        d.addListener((v, o, n) -> { 
         // ??? 
        }); 
        return d.getValue(); 
       }, userData); 


     public void setShowUserData(Boolean b) { 
      if (b) text.setValue(userData.get()); 
      else text.setValue(String.valueOf(value.getValue())); 
     } 

    } 
} 

4)Parser.java

import java.util.ArrayList; 
import java.util.LinkedList; 
import java.util.List; 
import java.util.regex.Matcher; 
import java.util.regex.Pattern; 

class Parser { 

    private static Parser instance = new Parser(); 
    private static Tokenizer tokenizer; 
    static { 
     tokenizer = new Tokenizer(); 
     tokenizer.add("[a-zA-Z_]\\d+", Token.CELL); 
     tokenizer.add("[a-zA-Z_]\\w*", Token.IDENT); 
     tokenizer.add("-?\\d+(\\.\\d*)?", Token.DECIMAL); 
     tokenizer.add("=", Token.EQUALS); 
     tokenizer.add(",", Token.COMMA); 
     tokenizer.add(":", Token.COLON); 
     tokenizer.add("\\(", Token.OPEN_BRACKET); 
     tokenizer.add("\\)", Token.CLOSE_BRACKET); 
    } 

    public static Formula parse(String formulaString) { 
     return instance.parseFormula(formulaString); 
    } 


    String formulaString; 
    LinkedList<Token> tokens; 
    Token lookahead; 

    private Parser() {} 

    private Formula parseFormula(String formulaString) { 
     this.formulaString = formulaString; 

     try { 
      tokenizer.tokenize(formulaString.replaceAll("\\s+","")); 
     } catch (ParseError e) { 
      System.out.println(e.getMessage()); 
     } 
     this.tokens = tokenizer.getTokens(); 
     if (tokens.isEmpty()) return Formula.Empty; 
     lookahead = this.tokens.getFirst(); 

     return formula(); 
    } 

    private Formula formula() { 
     switch(lookahead.token) { 
      case Token.DECIMAL: 
       String n = lookahead.sequence; 
       nextToken(); 
       return new Number(Double.parseDouble(n)); 
      case Token.EQUALS: 
       nextToken(); 
       return expression(); 
      case Token.EPSILON: 
       return Formula.Empty; 
      default: 
       return new Textual(formulaString); 
     } 
    } 

    private Formula expression() { 
     switch(lookahead.token) { 
      case Token.CELL: 
       int c = lookahead.sequence.charAt(0) - 'A'; 
       int r = Integer.parseInt(lookahead.sequence.substring(1)); 
       nextToken(); 
       if (lookahead.token == Token.COLON) { // Range 
        nextToken(); 
        if (lookahead.token == Token.CELL) { 
         int c2 = lookahead.sequence.charAt(0) - 'A'; 
         int r2 = Integer.parseInt(lookahead.sequence.substring(1)); 
         nextToken(); 
         return new Range(new Coord(r, c), new Coord(r2, c2)); 
        } else { 
         throw new ParseError("Incorrect Range: " + lookahead.sequence); 
        } 
       } else { 
        return new Coord(r, c); 
       } 
      case Token.DECIMAL: 
       Double d = Double.parseDouble(lookahead.sequence); 
       nextToken(); 
       return new Number(d); 
      case Token.IDENT: 
       return application(); 
      default: 
       throw new ParseError("Incorrect Expression: " + lookahead.sequence); 
     } 
    } 

    private Formula application() { 
     String opName = lookahead.sequence; 
     nextToken(); 
     if (lookahead.token != Token.OPEN_BRACKET) 
      throw new ParseError("No opening bracket: " + opName); 
     nextToken(); 
     List<Formula> args = new ArrayList<Formula>(); 
     while (true) { 
      if (lookahead.token == Token.EPSILON) 
       throw new ParseError("No closing bracket"); 
      args.add(expression()); 
      if (lookahead.token == Token.COMMA) nextToken(); 
      if (lookahead.token == Token.CLOSE_BRACKET) 
       return new Application(opName, args); 
     } 
    } 

    private void nextToken() { 
     tokens.pop(); 
     if (tokens.isEmpty()) lookahead = new Token(Token.EPSILON, ""); 
     else lookahead = tokens.getFirst(); 
    } 

} 

class ParseError extends RuntimeException { 

    ParseError(String message) { 
     super(message); 
    } 

} 

class Token { 

    public static final int EPSILON = 0; 
    public static final int EQUALS = 1; 
    public static final int IDENT = 2; 
    public static final int DECIMAL = 3; 
    public static final int OPEN_BRACKET = 4; 
    public static final int CLOSE_BRACKET = 5; 
    public static final int COMMA = 6; 
    public static final int COLON = 7; 
    public static final int CELL = 8; 

    public final int token; 
    public final String sequence; 

    public Token(int token, String sequence) { 
     this.token = token; 
     this.sequence = sequence; 
    } 

} 

class Tokenizer { 

    private LinkedList<TokenInfo> tokenInfos; 
    private LinkedList<Token> tokens; 

    public Tokenizer() { 
     tokenInfos = new LinkedList<TokenInfo>(); 
     tokens = new LinkedList<Token>(); 
    } 

    public void add(String regex, int token) { 
     tokenInfos.add(new TokenInfo(Pattern.compile("^("+regex+")"), token)); 
    } 

    public void tokenize(String s) { 
     tokens.clear(); 
     while (!s.equals("")) { 
      boolean match = false; 
      for (TokenInfo info : tokenInfos) { 
       Matcher m = info.regex.matcher(s); 
       if (m.find()) { 
        match = true; 
        String tok = m.group().trim(); 
        tokens.add(new Token(info.token, tok)); 
        s = m.replaceFirst(""); 
        break; 
       } 
      } 
      if (!match) throw new ParseError("Unexpected char in input: " + s); 
     } 
    } 

    public LinkedList<Token> getTokens() { 
     return tokens; 
    } 

    private static class TokenInfo { 

     public final Pattern regex; 
     public final int token; 

     public TokenInfo(Pattern regex, int token) { 
      super(); 
      this.regex = regex; 
      this.token = token; 
     } 

    } 

} 

5)Formula.java

import javafx.beans.value.ObservableValue; 
import javafx.scene.control.Cell; 

import java.util.*; 

abstract class Formula { 
    public static final Formula Empty = new Textual(""); 
    public double eval(Model env) { return 0.0; } 
    public List<ObservableValue<Double>> getReferences(Model env) { return Collections.emptyList(); } 
} 

class Textual extends Formula { 
    String value; 

    public Textual(String value) { 
     this.value = value; 
    } 

    public String toString() { 
     return value; 
    } 
} 

class Number extends Formula { 
    double value; 

    public Number(double value) { 
     this.value = value; 
    } 

    public String toString() { 
     return String.valueOf(value); 
    } 

    public double eval(Model env) { 
     return value; 
    } 
} 

class Coord extends Formula { 
    int row, column; 

    public Coord(int row, int column) { 
     this.row = row; 
     this.column = column; 
    } 

    public String toString() { 
     return ((char)('A'+column))+""+row; 
    } 

    public double eval(Model env) { 
     return env.getCells()[row][column].value.getValue(); 
    } 

    public List<ObservableValue<Double>> getReferences(Model env) { 
     List<ObservableValue<Double>> result = new ArrayList<>(1); 
     result.add(env.getCells()[row][column].value); 
     return result; 
    } 
} 

class Range extends Formula { 
    Coord coord1, coord2; 

    public Range(Coord coord1, Coord coord2) { 
     this.coord1 = coord1; this.coord2 = coord2; 
    } 

    public String toString() { 
     return String.valueOf(coord1)+":"+String.valueOf(coord2); 
    } 

    public double eval(Model env) { 
     throw new RuntimeException("Range cannot be evaluated!"); 
    } 

    public List<ObservableValue<Double>> getReferences(Model env) { 
     List<ObservableValue<Double>> result = new ArrayList<>(); 
     for (int r = coord1.row; r <= coord2.row; r++) { 
      for (int c = coord1.column; c <= coord2.column; c++) { 
       result.add(env.getCells()[r][c].value); 
      } 
     } 
     return result; 
    } 
} 

class Application extends Formula { 
    String function; 
    List<Formula> arguments; 

    public Application(String function, List<Formula> arguments) { 
     this.function = function; 
     this.arguments = arguments; 
    } 

    public String toString() { 
     StringBuilder t = new StringBuilder(); 
     t.append(function); 
     t.append("("); 
     for (int i = 0; i < arguments.size()-1; i ++) { 
      t.append(arguments.get(i).toString()); 
      t.append(", "); 
     } 
     if (!arguments.isEmpty()) t.append(arguments.get(arguments.size()-1).toString()); 
     t.append(")"); 
     return t.toString(); 
    } 

    public double eval(Model env) { 
     try { 
      List<Double> argvals = evalList(arguments, env); 
      return opTable.get(function).eval(argvals); 
     } catch(Exception e) { 
      return Double.NaN; 
     } 
    } 

    public List<ObservableValue<Double>> getReferences(Model env) { 
     List<ObservableValue<Double>> result = new ArrayList<>(); 
     for (Formula argument : arguments) { 
      result.addAll(argument.getReferences(env)); 
     } 
     return result; 
    } 


    private static List<Double> evalList(List<Formula> args, Model env) { 
     List<Double> result = new ArrayList<>(); 
     for (Formula f : args) { 
      if (f instanceof Range) { 
       for (ObservableValue<Double> c : f.getReferences(env)) { 
        result.add(c.getValue()); 
       } 
      } else { 
       result.add(f.eval(env)); 
      } 
     } 
     return result; 
    } 

    private static Map<String, Op> opTable = new HashMap<>(); 
    static { 
     opTable.put("add", vals -> vals.get(0) + vals.get(1)); 
     opTable.put("sub", vals -> vals.get(0) - vals.get(1)); 
     opTable.put("div", vals -> vals.get(0)/vals.get(1)); 
     opTable.put("mul", vals -> vals.get(0) * vals.get(1)); 
     opTable.put("mod", vals -> vals.get(0) % vals.get(1)); 
     opTable.put("sum", vals -> { 
      double accum = 0; 
      for (Double i : vals) { 
       accum += i; 
      } 
      return accum; 
     }); 
     opTable.put("prod", vals -> { 
      double accum = 1; 
      for (Double i : vals) { 
       accum *= i; 
      } 
      return accum; 
     }); 
    } 

    private static interface Op { 
     public double eval(List<Double> vals); 
    } 

} 
+0

を行い、その明快ホープと

EDIT

enter image description here [enter image description here] [enter image description here] 3

に役立つ安全であるとのコードを記載してくださいとにかく私の場所からブロックされているpastebinではなく質問ですか?ファイルが大きすぎる場合は、SSCCEに簡略化してください。http://sscce.org/ – Adam

+0

ControlsFXのSpreadsheetViewを試しましたか?http:// fxexperience。co.jp/controlsfx/features /#spreadsheetview? – eckig

+0

テーブルの列ラベルの行と下のスクロールバーがテーブルのスクロールペインに含まれていないため、コンテンツが最初から不一致です。 – Alexiy

答えて

0

はあなたListViewあなたVBarを取得し、あなたのTableViewVBarとそれを結合して、あなたのアライメントはそのままになりますについては、この

を試してみてください。あなたが持っているので

は今 TableView VBar

VirtualFlow tvVF = (VirtualFlow) TableView.lookup("#virtual-flow"); 
for (Node n : tvVF.getChildrenUnmodifiable()) { 
    if (n.getClass().isAssignableFrom(VirtualScrollBar.class)) { 
     VirtualScrollBar table_vsb = (VirtualScrollBar) n; 
     if (tvVF.getWidth() - table_vsb.getWidth() > tvVF.getWidth()/2) { 
       //table_vsb is your Vertical bar for the TableView 
    ....//close braces 
VirtualFlow lvVF = (VirtualFlow) ListView.lookup("#virtual-flow"); 
// we do the same for listview 
for (Node c : lvVF.getChildrenUnmodifiable()) { 
     if (c.getClass().isAssignableFrom(VirtualScrollBar.class)) { 
      VirtualScrollBar list_vsb = (VirtualScrollBar) c; 
      if (tvVF.getWidth() - vsb.getWidth() > tvVF.getWidth()/2) { 
       //list_vsb is your vbar for the listview 

のためにあなたのListViewVBar実行同じこのコードを取得するには、あなたの2 VBarListViewバインド 'はこの

list_vsb.valueProperty().bind(table_vsb.valueProperty()); 

ようTableView秒をしてください完全なレイアウトの後、またはStage.show();これらのコードを呼び出す必要があることに注意してください。呼ばれている -

が、それは私のために仕事:)

+0

動作していません。 tvVFオブジェクトはnullです。 –

+0

それはあなたがすぐに呼び出すときにnullだけになります、あなたはあなたのデータとノードが最初にレイアウトされている必要があります、tableview/listviewは、バーチャルフローが存在する前に、スニペットのリンクを確認してください@ ZaheerKhorajiya – Elltz

+0

上記のソリューションは動作しませんこのhttps://s3.postimg.org/5k77mks83/screen_png.pngを参照してください) –