Как установить цвет фона TableRow в зависимости от того, выбран ли он и / или значение в модели данных, в JavaFX8 TableView? - PullRequest
0 голосов
/ 19 октября 2018

Относительный вопрос новичка в Java.

Я пытаюсь установить цвет фона TableRow в зависимости от того, выбран он или нет и / или истинно ли логическое значение в модели данных.

Я нашел способы сделать каждый, но не оба вместе в одном и том же setRowFactory.

В итоге я хотел бы получить следующее (хотя и с ужасными цветами для примератолько!):

enter image description here

Как мне добиться этого?

Это то, что я нашел, не меняя цвет строки на основевыбор.Это адаптировано из ответа пользователя James_D здесь https://community.oracle.com/thread/3528543.

final ObservableSet<Integer> selectedRowIndexes = FXCollections.observableSet();

table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> change) -> {  
    selectedRowIndexes.clear();
    selectedRowIndexes.add( (table.getSelectionModel().getSelectedCells().get(0)).getRow() );
});

table.setRowFactory(tv -> {
    TableRow<TestModel> row = new TableRow<>();
    BooleanBinding selected = Bindings.createBooleanBinding(() ->  
        selectedRowIndexes.contains(new Integer(row.getIndex())), row.indexProperty(), selectedRowIndexes);
    row.styleProperty().bind(Bindings.when(selected)
        .then("-fx-background-color:  green;")
        .otherwise(""));
    return row;
});

И это то, что я нашел при изменении цвета строки на основе значения ячейки.Это адаптировано из ответа пользователя kleopatra здесь TreeTableView: установка строки недоступна для редактирования .

table.setRowFactory(tv -> {
    TableRow<TestModel> row = new TableRow<TestModel>() {
        @Override
        public void updateItem(TestModel testmodel, boolean empty) {
            super.updateItem(testmodel, empty);
            boolean locked = false;
            if ( getItem() != null ) {
                locked = getItem().lockedProperty().get();
                setEditable( ! locked);
            }
            if (!isEmpty() && locked ) {
                setStyle("-fx-background-color: red;");
            }else{
                setStyle(null);
            }
        }
    };
    return row;
});

Однако я получил две фабрики строк и не смог выяснитькак объединить их в один.

Если это поможет, вот MVCE, с которым я играл.У этого есть две фабрики ряда.Я не включил свои (многие!) Попытки объединить их, поскольку ни одна из них не сработала.

Я использую JavaFX8 (JDK1.8.0_181), NetBeans 8.2 и Scene Builder 8.3.

package test31;

import java.util.Arrays;
import javafx.application.Application;
import static javafx.application.Application.launch;
import javafx.beans.Observable;
import javafx.beans.binding.Bindings;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ListChangeListener.Change;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TablePosition;
import javafx.scene.control.TableRow;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.TextFieldTableCell;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;
import javafx.util.converter.BooleanStringConverter;

public class Test31 extends Application {

    private Parent createContent() {

        TableView<TestModel> table = new TableView<>();

        ObservableList<TestModel> olTestModel = FXCollections.observableArrayList(testmodel -> new Observable[] {});
        olTestModel.add(new TestModel("1", true));
        olTestModel.add(new TestModel("2", false));
        olTestModel.add(new TestModel("3", false));
        olTestModel.add(new TestModel("4", true));
        olTestModel.add(new TestModel("5", false));

        TableColumn<TestModel, String> colText = new TableColumn<>("textfield");
        colText.setCellValueFactory(cb -> cb.getValue().textFieldProperty());
        colText.setCellFactory(TextFieldTableCell.forTableColumn());

        TableColumn<TestModel, Boolean> colBoolean = new TableColumn<>("locked");
        colBoolean.setCellValueFactory(cb -> cb.getValue().lockedProperty());
        colBoolean.setCellFactory(TextFieldTableCell.forTableColumn(new BooleanStringConverter()));

        table.getSelectionModel().setCellSelectionEnabled(true);
        table.getSelectionModel().setSelectionMode(SelectionMode.SINGLE);
        table.setEditable(true);
        table.getColumns().addAll(Arrays.asList(colText, colBoolean));
        table.setItems(olTestModel);

        //****************************************************************************************
        //First row factory:  Set background colour based on whether or not the row is selected
        final ObservableSet<Integer> selectedRowIndexes = FXCollections.observableSet();

        table.getSelectionModel().getSelectedCells().addListener((Change<? extends TablePosition> change) -> {  
            selectedRowIndexes.clear();
            selectedRowIndexes.add( (table.getSelectionModel().getSelectedCells().get(0)).getRow() );
        });

        table.setRowFactory(tv -> {
            TableRow<TestModel> row = new TableRow<>();
            BooleanBinding selected = Bindings.createBooleanBinding(() ->  
                selectedRowIndexes.contains(new Integer(row.getIndex())), row.indexProperty(), selectedRowIndexes);
            row.styleProperty().bind(Bindings.when(selected)
                .then("-fx-background-color:  green;")
                .otherwise(""));
            return row;
        });

        //****************************************************************************************
        //Second row factory:  Set background colour based on the value of a boolean property
        table.setRowFactory(tv -> {
            TableRow<TestModel> row = new TableRow<TestModel>() {
                @Override
                public void updateItem(TestModel testmodel, boolean empty) {
                    super.updateItem(testmodel, empty);
                    boolean locked = false;
                    if ( getItem() != null ) {
                        locked = getItem().lockedProperty().get();
                        setEditable( ! locked);
                    }
                    if (!isEmpty() && locked ) {
                        setStyle("-fx-background-color: red;");
                    }else{
                        setStyle(null);
                    }
                }
            };
            return row;
        });

        BorderPane content = new BorderPane(table);

        return content;

    }

    public class TestModel {

        private StringProperty textField;
        private BooleanProperty locked;

        public TestModel() {
            this("", false);
        }

        public TestModel(
            String textField,
            boolean locked
        ) {
            this.textField = new SimpleStringProperty(textField);
            this.locked = new SimpleBooleanProperty(locked);
        }

        public String getTextField() {
            return textField.get().trim();
        }

        public void setTextField(String textField) {
            this.textField.set(textField);
        }

        public StringProperty textFieldProperty() {
            return textField;
        }

        public boolean getLocked() {
            return locked.get();
        }

        public void setLocked(boolean locked) {
            this.locked.set(locked);
        }

        public BooleanProperty lockedProperty() {
            return locked;
        }

    }

    @Override
    public void start(Stage stage) throws Exception {
        stage.setScene(new Scene(createContent()));
        stage.setTitle("Test");
        stage.setWidth(500);
        stage.show();
    }

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

}

1 Ответ

0 голосов
/ 19 октября 2018

Есть несколько способов сделать это.Вот пример использования внешних CSS и псевдоклассов:

Main.java

import java.util.stream.Collectors;
import java.util.stream.IntStream;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.SelectionMode;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class Main extends Application {

  @Override
  public void start(Stage primaryStage) {
    TableView<Item> table = new TableView<>(createDummyData(100));
    table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
    table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE);

    table.setRowFactory(t -> new ItemTableRow());

    TableColumn<Item, String> nameCol = new TableColumn<>("Name");
    nameCol.setCellValueFactory(features -> features.getValue().nameProperty());
    table.getColumns().add(nameCol);

    TableColumn<Item, Boolean> validCol = new TableColumn<>("Valid");
    validCol.setCellValueFactory(features -> features.getValue().validProperty());
    table.getColumns().add(validCol);

    primaryStage.setScene(new Scene(new StackPane(table), 800, 600));
    primaryStage.getScene().getStylesheets().add(getClass().getResource("Main.css").toExternalForm());
    primaryStage.setTitle("JavaFX Application");
    primaryStage.show();
  }


  private ObservableList<Item> createDummyData(int count) {
    return IntStream.rangeClosed(1, count)
        .mapToObj(i -> "Item #" + i)
        .map(name -> new Item(name, Math.random() >= 0.5))
        .collect(Collectors.toCollection(FXCollections::observableArrayList));
  }

}

ItemTableRow.java

import javafx.beans.value.ChangeListener;
import javafx.beans.value.WeakChangeListener;
import javafx.css.PseudoClass;
import javafx.scene.control.TableRow;

public class ItemTableRow extends TableRow<Item> {

  private static final PseudoClass VALID = PseudoClass.getPseudoClass("valid");

  private final ChangeListener<Boolean> listener = (obs, oldVal, newVal) -> updateValidPseudoClass(newVal);
  private final WeakChangeListener<Boolean> weakListener = new WeakChangeListener<>(listener);

  public ItemTableRow() {
    getStyleClass().add("item-table-row");
  }

  @Override
  protected void updateItem(Item item, boolean empty) {
    Item oldItem = getItem();
    if (oldItem != null) {
      oldItem.validProperty().removeListener(weakListener);
    }
    super.updateItem(item, empty);
    if (empty || item == null) {
      updateValidPseudoClass(false);
    } else {
      item.validProperty().addListener(weakListener);
      updateValidPseudoClass(item.isValid());
    }
  }

  private void updateValidPseudoClass(boolean active) {
    pseudoClassStateChanged(VALID, active);
  }

}

Item.java

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;

public class Item {

  private final StringProperty name = new SimpleStringProperty(this, "name");
  public final void setName(String name) { this.name.set(name); }
  public final String getName() { return name.get(); }
  public final StringProperty nameProperty() { return name; }

  private final BooleanProperty valid = new SimpleBooleanProperty(this, "valid");
  public final void setValid(boolean valid) { this.valid.set(valid); }
  public final boolean isValid() { return valid.get(); }
  public final BooleanProperty validProperty() { return valid; }

  public Item() {}

  public Item(String name, boolean valid) {
    setName(name);
    setValid(valid);
  }

}

Main.css

.item-table-row:selected {
    -fx-background-color: -fx-control-inner-background, green;
}

.item-table-row:valid {
    -fx-background-color: -fx-control-inner-background, yellow;
}

.item-table-row:valid:selected {
    -fx-background-color: -fx-control-inner-background, red;
}

Если вы предпочитаете использовать только код,замените ItemTableRow на это (и удалите getStylesheets().add(...) из Main):

import javafx.beans.InvalidationListener;
import javafx.beans.WeakInvalidationListener;
import javafx.scene.control.TableRow;

public class ItemTableRow extends TableRow<Item> {

  private final InvalidationListener listener = observable -> updateStyle();
  private final WeakInvalidationListener weakListener = new WeakInvalidationListener(listener);

  public ItemTableRow() {
    getStyleClass().add("item-table-row");
    selectedProperty().addListener(listener); // could also override updateSelected
  }

  @Override
  protected void updateItem(Item item, boolean empty) {
    Item oldItem = getItem();
    if (oldItem != null) {
      oldItem.validProperty().removeListener(weakListener);
    }
    super.updateItem(item, empty);
    if (item != null) {
      item.validProperty().addListener(weakListener);
    }
    updateStyle();
  }

  private void updateStyle() {
    final Item item = getItem();
    if (item == null || (!isSelected() && !item.isValid())) {
      setStyle(null);
    } else if (isSelected() && item.isValid()) {
      setStyle("-fx-background-color: -fx-control-inner-background, red;");
    } else if (isSelected()) {
      setStyle("-fx-background-color: -fx-control-inner-background, green;");
    } else if (item.isValid()) {
      setStyle("-fx-background-color: -fx-control-inner-background, yellow;");
    } else {
      // I don't think this branch is possible, but not 100% sure
      throw new AssertionError("Shouldn't be here?");
    }
  }

}

Значение -fx-control-inner-background определено в modena.css (таблица стилей по умолчанию для JavaFX 8+),Это дает TableRow немного цвета.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...