2017-04-25 69 views
0

我在JavaFX中使用treeview來實現文件瀏覽器。我把Tab中的treeview設置爲conent。當我使用預定線程刷新treeview元素時,我無法滾動到以前選擇的樹項目。這裏是我的啓示的最小版本。JavaFX:刷新內容後如何記住tabcontent的滾動位置?

/** 
* 
* @author nika 
*/ 
public class TabTreeView extends Application implements Runnable { 

private TreeItem<String> root; 
private TreeView<String> tv = new TreeView<>(); 
private static ArrayList<NumberPair> rememberExpanded = new ArrayList<>(); 
private TreeItem<String> previouslySelectedTreeItem; 

@Override 
public void run() { 
    root = new TreeItem<>("Root"); 
    root.setExpanded(true); 
    for (int i = 0; i < 10; i++) { 
     TreeItem<String> sublevel = new TreeItem<>("Level : " + i + " TS: " + System.currentTimeMillis()); 
     final int level = i; 
     if (rememberExpanded.contains(new NumberPair(level, -9999))) { 
      sublevel.setExpanded(true); 
     } 
     sublevel.expandedProperty().addListener(new ChangeListener<Boolean>() { 
      @Override 
      public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 
       if (newValue) { 
        rememberExpanded.add(new NumberPair(level, -9999)); 
       } else { 
        rememberExpanded.remove(new NumberPair(level, -9999)); 
       } 
      } 
     }); 
     for (int j = 0; j < 5; j++) { 
      TreeItem<String> subsublevel = new TreeItem<>("SubLevel : " + j + " @ " + i + " TS:" + System.currentTimeMillis()); 
      final int level2 = j; 
      if (rememberExpanded.contains(new NumberPair(level, level2))) { 
       sublevel.setExpanded(true); 
      } 
      subsublevel.expandedProperty().addListener(new ChangeListener<Boolean>() { 
       @Override 
       public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) { 
        if (newValue) { 
         rememberExpanded.add(new NumberPair(level, level2)); 
        } else { 
         rememberExpanded.remove(new NumberPair(level, level2)); 
        } 
       } 
      }); 

      sublevel.getChildren().add(subsublevel); 
     } 
     root.getChildren().add(sublevel); 
    } 
    Platform.runLater(new Runnable() { 
     @Override 
     public void run() { 
      tv.setRoot(root); 
      //scroll to previously selected item 
      //i know this one is wrong 1. "as it is not working", 2. "because it will only look into children of root not grandchildren". 
      tv.scrollTo(root.getChildren().indexOf(previouslySelectedTreeItem)); 
     } 
    }); 
} 

@Override 
public void start(Stage primaryStage) throws Exception { 
    TabPane tabpane = new TabPane(); 
    tv.selectionModelProperty().addListener(new ChangeListener<MultipleSelectionModel<TreeItem<String>>>() { 
     @Override 
     public void changed(ObservableValue<? extends MultipleSelectionModel<TreeItem<String>>> observable, MultipleSelectionModel<TreeItem<String>> oldValue, MultipleSelectionModel<TreeItem<String>> newValue) { 
      previouslySelectedTreeItem = newValue.getSelectedItem(); 
     } 
    }); 
    Tab t = new Tab("Demo", tv); 
    tabpane.getTabs().add(t); 
    BorderPane bp = new BorderPane(tabpane); 
    primaryStage.setScene(new Scene(bp)); 
    primaryStage.show(); 
    ScheduledExecutorService exc = Executors.newScheduledThreadPool(1); 
    exc.scheduleAtFixedRate(this, 0, 2, TimeUnit.SECONDS); 

} 

public static void main(String[] args) throws Exception { 
    launch(TabTreeView.class, args); 

} 

private class NumberPair { 

    int first, second; 

    public NumberPair(int first, int second) { 
     this.first = first; 
     this.second = second; 
    } 

    public int getFirst() { 
     return first; 
    } 

    public void setFirst(int first) { 
     this.first = first; 
    } 

    public int getSecond() { 
     return second; 
    } 

    public void setSecond(int second) { 
     this.second = second; 
    } 

    @Override 
    public int hashCode() { 
     int hash = 3; 
     hash = 37 * hash + this.first; 
     hash = 37 * hash + this.second; 
     return hash; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) { 
      return true; 
     } 
     if (obj == null) { 
      return false; 
     } 
     if (getClass() != obj.getClass()) { 
      return false; 
     } 
     final NumberPair other = (NumberPair) obj; 
     if (this.first != other.first) { 
      return false; 
     } 
     if (this.second != other.second) { 
      return false; 
     } 
     return true; 
    } 

    @Override 
    public String toString() { 
     return "NumberPair{" + "first=" + first + ", second=" + second + '}'; 
    } 

} 

} 

有沒有辦法記住滾動tabcontent或treeview的位置?這樣我可以在刷新視圖後恢復滾動位置。

在這個實現中,我添加了一個NumberPair類來記住擴展項目。

回答

1

你可以找到styleclass的TreeView的ScrollBar引用。嘗試這個。

Platform.runLater(new Runnable() { 
    @Override 
    public void run() { 
     double position[] = new double[]{0.0d}; 
     Optional<ScrollBar> scroll = findScrollBar(tv, Orientation.VERTICAL); 
     scroll.ifPresent(s -> position[0] = s.getValue()); 
     tv.setRoot(root); 
     scroll.ifPresent(s -> s.setValue(position[0])); 
    } 
}); 

private Optional<ScrollBar> findScrollBar(TreeView<String> from, Orientation orientation) { 
    Set<Node> nodes = from.lookupAll(".scroll-bar"); 
    return nodes.stream() 
      .filter(node -> node instanceof ScrollBar && ((ScrollBar)node).getOrientation() == orientation) 
      .map(node -> ((ScrollBar)node)) 
      .findFirst(); 
} 

但請注意,試圖在TreeItem中保留相同的項目可能是一個好方法。只要您從唯一的根目錄添加和刪除項目,TreeView就會保持選擇擴展和滾動位置。