2015-06-12 3 views
1

У меня есть TableView с несколькими TableColumns, и я хочу разместить узел ниже определенной TableColumn. Как получить точную позицию (x, y-координаты) TableColumn, чтобы я мог привязать свойства перевода моего узла?position of TableColumn in Scene

Вот фрагмент того, как я поставил кнопку в правом верхнем углу моего TableView:

button.translateXProperty().unbind(); 
button.translateXProperty().bind(tableView.widthProperty().divide(2.0).subtract(button.getWidth()/2.0 + 2.0) + tableView.localToScene(0.0, 0.0).getX()); 

Это работает отлично, но, очевидно, только для TableView. В TableColumns нет этих свойств перевода или методов localToScene, поэтому я не могу напрямую получить позицию, к которой я хотел бы привязать свой узел.

Мое текущее решение (на самом деле это не так хорошо) заключается в следующем: Я прочитал позицию своего TableView в Scene (PointA), а затем просмотрю список всех столбцов (tableView. getColumns()) и проверьте, видна ли каждая из них, и если да, добавьте их ширину в значение X PointA. Я делаю это, пока не найду фактический столбец, который я хочу разместить ниже. Теперь проблема в том, что я не могу просто привязать позицию Nodes к этой точке, потому что, когда я изменяю порядок столбцов или делаю одно из них невидимым, мой столбец меняет положение на экране. Я должен был бы добавить слушателя к порядку столбца и видимости ...

Есть ли более эффективный способ делать то, что я хочу? : D

ответ

1

Если вам разрешено использовать непубличной API, вы могли бы рассмотреть, чтобы получить доступ к TableColumnHeader через его кожу, при условии, что это типа TableViewSkinBase: он имеет API для доступа к TableRowHeader, который контейнер всех TableColumnHeaders и api, чтобы найти заголовок для любого содержащегося в нем столбца.

Фрагмента коды (ширина/место связывания копируется из примера Джеймса, только в заголовок вместо метки)

private void buttonsPerHeader(TableView<Person> table, Pane root) { 
    if (!(table.getSkin() instanceof TableViewSkinBase)) return; 
    TableViewSkinBase skin = (TableViewSkinBase) table.getSkin(); 
    TableHeaderRow headerRow = skin.getTableHeaderRow(); 
    for (TableColumn col : table.getColumns()) { 
     TableColumnHeader header = headerRow.getColumnHeaderFor(col); 
     Button button = new Button(col.getText()); 

     button.prefWidthProperty().bind(Bindings.createDoubleBinding(() -> 
      header.getBoundsInLocal().getWidth(), header.boundsInLocalProperty())); 
     button.minWidthProperty().bind(button.prefWidthProperty()); 
     button.maxWidthProperty().bind(button.prefWidthProperty()); 

     button.layoutXProperty().bind(Bindings.createDoubleBinding(() -> 
      header.getLocalToSceneTransform().transform(header.getBoundsInLocal()).getMinX(), 
      header.boundsInLocalProperty(), header.localToSceneTransformProperty())); 

     button.layoutYProperty().bind(Bindings.createDoubleBinding(() -> 
      table.getBoundsInParent().getMaxY() ,table.boundsInParentProperty())); 

     root.getChildren().add(button); 
    } 

} 
+0

Я собираюсь попробовать это решение, так как оно не зависит от поиска или опций. Я выберу это как лучший ответ на данный момент, но я все еще надеюсь на более простой способ сделать это, может быть, в обновленной версии JavaFX: D – user2336377

+0

@ user2336377 нам может повезти в jdk9: из-за модуляции, skin api находится под обследованием, чтобы быть обнародованным ... или полностью потерянным: из-за модуляции внутренний api не будет доступен никоим образом (оставив поиск Джеймса единственным вариантом) – kleopatra

+0

Я только что реализовал это в своей программе, и он не работает как есть, потому что я получаю следующую ошибку при попытке привязать метку к позиции столбца: «Label.layoutX: связанное значение не может быть установлено». Но когда я меняю привязки из '.layoutXProperty(). Bind (...)' to '.translateXProperty(). Bind (...)' все работает так, как я этого хочу :) – user2336377

1

Мне обычно не нравится использование поисковых запросов, но вы можете получить метку, которая используется для отображения заголовка столбца с помощью поиска .table-view .column-header .label, а затем привязать свойства макета кнопки с помощью границ этой метки.

Пример:

import java.util.Optional; 
import java.util.function.Function; 

import javafx.application.Application; 
import javafx.beans.binding.Bindings; 
import javafx.beans.property.SimpleStringProperty; 
import javafx.beans.property.StringProperty; 
import javafx.beans.value.ObservableValue; 
import javafx.scene.Parent; 
import javafx.scene.Scene; 
import javafx.scene.control.Button; 
import javafx.scene.control.Label; 
import javafx.scene.control.TableColumn; 
import javafx.scene.control.TableView; 
import javafx.scene.layout.Pane; 
import javafx.stage.Stage; 

public class TableColumnLocationExample extends Application { 

    @Override 
    public void start(Stage primaryStage) { 
     TableView<Person> table = new TableView<>(); 
     table.getColumns().add(column("First Name", Person::firstNameProperty, 120)); 
     table.getColumns().add(column("Last Name", Person::lastNameProperty, 120)); 
     table.getColumns().add(column("Email", Person::emailProperty, 250)); 

     table.getItems().addAll(
       new Person("Jacob", "Smith", "[email protected]"), 
       new Person("Isabella", "Johnson", "[email protected]"), 
       new Person("Ethan", "Williams", "[email protected]"), 
       new Person("Emma", "Jones", "[email protected]"), 
       new Person("Michael", "Brown", "[email protected]")   
     ); 

     Pane root = new Pane(table); 
     Scene scene = new Scene(root, 600, 600); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 


     for (TableColumn<Person, ?> col : table.getColumns()) { 
      Optional<Label> header = findLabelForTableColumnHeader(col.getText(), root); 
      header.ifPresent(label -> { 
       Button button = new Button(col.getText()); 

       button.prefWidthProperty().bind(Bindings.createDoubleBinding(() -> 
        label.getBoundsInLocal().getWidth(), label.boundsInLocalProperty())); 
       button.minWidthProperty().bind(button.prefWidthProperty()); 
       button.maxWidthProperty().bind(button.prefWidthProperty()); 

       button.layoutXProperty().bind(Bindings.createDoubleBinding(() -> 
        label.getLocalToSceneTransform().transform(label.getBoundsInLocal()).getMinX(), 
        label.boundsInLocalProperty(), label.localToSceneTransformProperty())); 

       button.layoutYProperty().bind(Bindings.createDoubleBinding(() -> 
        table.getBoundsInParent().getMaxY() ,table.boundsInParentProperty())); 

       root.getChildren().add(button); 

      }); 
     } 


    } 

    private Optional<Label> findLabelForTableColumnHeader(String text, Parent root) { 
     return root.lookupAll(".table-view .column-header .label") 
       .stream() 
       .map(Label.class::cast) 
       .filter(label -> label.getText().equals(text)) 
       .findAny(); // assumes all columns have unique text... 
    } 



    private <S,T> TableColumn<S,T> column(String title, Function<S,ObservableValue<T>> property, double width) { 
     TableColumn<S,T> col = new TableColumn<>(title); 
     col.setCellValueFactory(cellData -> property.apply(cellData.getValue())); 
     col.setPrefWidth(width); 
     return col ; 
    } 

    public static class Person { 
     private StringProperty firstName = new SimpleStringProperty(); 
     private StringProperty lastName = new SimpleStringProperty(); 
     private StringProperty email = new SimpleStringProperty(); 

     public Person(String firstName, String lastName, String email) { 
      setFirstName(firstName); 
      setLastName(lastName); 
      setEmail(email); 
     } 

     public final StringProperty firstNameProperty() { 
      return this.firstName; 
     } 

     public final String getFirstName() { 
      return this.firstNameProperty().get(); 
     } 

     public final void setFirstName(final String firstName) { 
      this.firstNameProperty().set(firstName); 
     } 

     public final StringProperty lastNameProperty() { 
      return this.lastName; 
     } 

     public final String getLastName() { 
      return this.lastNameProperty().get(); 
     } 

     public final void setLastName(final String lastName) { 
      this.lastNameProperty().set(lastName); 
     } 

     public final StringProperty emailProperty() { 
      return this.email; 
     } 

     public final String getEmail() { 
      return this.emailProperty().get(); 
     } 

     public final void setEmail(final String email) { 
      this.emailProperty().set(email); 
     } 


    } 

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

А я думаю, что будет работать, и лишь немного лучше, чем мой " обходной путь ": D – user2336377

+1

, вы, вероятно, хотите привязать ширину к родительскому ярлыку - в противном случае он слегка отключается, если отображается значок сортировки. – kleopatra