在JavaFX中实现带有两种背景颜色的背景?

9
在JavaFX 2中,使用CSS是否可以创建具有两种颜色的背景?例如,一个高度为10像素的TableCell。我想要前面2个像素(垂直方向)是红色的,其余8个像素(垂直方向)保持默认的背景颜色。在JavaFX 2中,使用CSS是否可以实现这个效果?怎么做? 示例: 原始背景: enter image description here 期望结果: enter image description here(上部的2个像素被替换为红色)
谢谢任何有关此问题的提示!
2个回答

15

我使用了一层简单的背景颜色来制造一个红色的突出显示(类似于 Stefan 建议的解决方案)。

/**
 * file: table.css
 *   Place in same directory as TableViewPropertyEditorWithCSS.java.
 *   Have your build system copy this file to your build output directory.
 **/

.highlighted-cell {
  -fx-text-fill: -fx-text-inner-color;
  -fx-background-color: firebrick, gainsboro;
  -fx-background-insets: 0, 2 0 0 0;
}

对于像stackpane这样的标准区域,您只需要应用上述CSS(减去-fx-text-fill)即可获得所需的结果。


这是另一种使用渐变定义颜色的棘手方法:
-fx-background-color: 
  linear-gradient(
    from 0px 0px to 0px 2px, 
      firebrick, firebrick 99%, 
    gainsboro
  );

在下面的截图中,如果单元格的值为false,则会突出显示该单元格(通过应用highlighted-cell CSS类)。

highlightedcells

突出显示单元格样式类切换逻辑:

public void updateItem(Object item, boolean empty) {
  super.updateItem(item, empty);
  if (empty) {
    ....
    getStyleClass().remove("highlighted-cell");
  } else {
    if (getItem() instanceof Boolean && (Boolean.FALSE.equals((Boolean) getItem()))) {
      getStyleClass().add("highlighted-cell");
    } else {
      getStyleClass().remove("highlighted-cell");
    }
    ...
  }
}

当在自定义单元格的updateItem调用期间应用于标准表格单元格的highlighted-cell样式类时,它看起来很好,但确实有一些缺点。表格着色方案非常微妙和复杂。它具有奇偶值的高亮显示,选定行的高亮显示,选定悬停行的高亮显示,聚焦行和单元格的高亮显示等等。此外,它还有各种组合。仅直接在highlight-cell类中设置background-color是一种粗暴的方法,因为它没有考虑所有这些其他微妙之处,而只是覆盖了它们,因此使用此样式突出显示的单元格始终看起来相同,无论应用了什么临时的css伪类状态。

这个方案很好,但更好的解决方案是根据伪类状态以不同颜色突出显示单元格。然而,实现这个功能相当棘手,您可能会花费很多时间尝试各种状态和CSS选择器组合以获得漂亮的变化高亮显示效果。总的来说,在这个例子中,对我来说不值得付出额外的努力,但对您可能有用。


测试程序(对于这个程序的长度和复杂性,我很抱歉,但是将样式高亮逻辑集成到现有程序中更容易):

import java.lang.reflect.*;
import java.util.logging.*;
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.beans.value.*;
import javafx.collections.*;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.TableColumn.CellEditEvent;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.*;
import javafx.stage.Stage;
import javafx.util.Callback;
// click in the value column (a couple of times) to edit the value in the column.
// property editors are defined only for String and Boolean properties.
// change focus to something else to commit the edit.
public class TableViewPropertyEditorWithCSS extends Application {

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

  @Override
  public void start(Stage stage) {
    final Person aPerson = new Person("Fred", false, false, "Much Ado About Nothing");
    final Label currentObjectValue = new Label(aPerson.toString());
    TableView<NamedProperty> table = new TableView();
    table.setEditable(true);
    table.setItems(createNamedProperties(aPerson));
    TableColumn<NamedProperty, String> nameCol = new TableColumn("Name");
    nameCol.setCellValueFactory(new PropertyValueFactory<NamedProperty, String>("name"));
    TableColumn<NamedProperty, Object> valueCol = new TableColumn("Value");
    valueCol.setCellValueFactory(new PropertyValueFactory<NamedProperty, Object>("value"));
    valueCol.setCellFactory(new Callback<TableColumn<NamedProperty, Object>, TableCell<NamedProperty, Object>>() {
      @Override
      public TableCell<NamedProperty, Object> call(TableColumn<NamedProperty, Object> param) {
        return new EditingCell();
      }
    });
    valueCol.setOnEditCommit(
            new EventHandler<CellEditEvent<NamedProperty, Object>>() {
      @Override
      public void handle(CellEditEvent<NamedProperty, Object> t) {
        int row = t.getTablePosition().getRow();
        NamedProperty property = (NamedProperty) t.getTableView().getItems().get(row);
        property.setValue(t.getNewValue());
        currentObjectValue.setText(aPerson.toString());
      }
    });
    table.getColumns().setAll(nameCol, valueCol);
    table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
    VBox layout = new VBox(10);
    layout.setStyle("-fx-background-color: cornsilk; -fx-padding: 10;");
    layout.getChildren().setAll(
            currentObjectValue,
            table);
    VBox.setVgrow(table, Priority.ALWAYS);

    Scene scene = new Scene(layout, 650, 600);
    scene.getStylesheets().add(getClass().getResource("table.css").toExternalForm());
    stage.setScene(scene);
    stage.show();
  }

  private ObservableList<NamedProperty> createNamedProperties(Object object) {
    ObservableList<NamedProperty> properties = FXCollections.observableArrayList();
    for (Method method : object.getClass().getMethods()) {
      String name = method.getName();
      Class type = method.getReturnType();
      if (type.getName().endsWith("Property")) {
        try {
          properties.add(new NamedProperty(name, (Property) method.invoke(object)));
        } catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException ex) {
          Logger.getLogger(TableViewPropertyEditorWithCSS.class.getName()).log(Level.SEVERE, null, ex);
        }
      }
    }
    return properties;
  }

  public class NamedProperty {

    public NamedProperty(String name, Property value) {
      nameProperty.set(name);
      valueProperty = value;
    }
    private StringProperty nameProperty = new SimpleStringProperty();

    public StringProperty nameProperty() {
      return nameProperty;
    }

    public StringProperty getName() {
      return nameProperty;
    }

    public void setName(String name) {
      nameProperty.set(name);
    }
    private Property valueProperty;

    public Property valueProperty() {
      return valueProperty;
    }

    public Object getValue() {
      return valueProperty.getValue();
    }

    public void setValue(Object value) {
      valueProperty.setValue(value);
    }
  }

  public class Person {

    private final SimpleStringProperty firstName;
    private final SimpleBooleanProperty married;
    private final SimpleBooleanProperty hasChildren;
    private final SimpleStringProperty favoriteMovie;

    private Person(String firstName, Boolean isMarried, Boolean hasChildren, String favoriteMovie) {
      this.firstName = new SimpleStringProperty(firstName);
      this.married = new SimpleBooleanProperty(isMarried);
      this.hasChildren = new SimpleBooleanProperty(hasChildren);
      this.favoriteMovie = new SimpleStringProperty(favoriteMovie);
    }

    public SimpleStringProperty firstNameProperty() {
      return firstName;
    }

    public SimpleBooleanProperty marriedProperty() {
      return married;
    }

    public SimpleBooleanProperty hasChildrenProperty() {
      return hasChildren;
    }

    public SimpleStringProperty favoriteMovieProperty() {
      return favoriteMovie;
    }

    public String getFirstName() {
      return firstName.get();
    }

    public void setFirstName(String fName) {
      firstName.set(fName);
    }

    public Boolean getMarried() {
      return married.get();
    }

    public void setMarried(Boolean isMarried) {
      married.set(isMarried);
    }

    public Boolean getHasChildren() {
      return hasChildren.get();
    }

    public void setHasChildren(Boolean hasChildren) {
      this.hasChildren.set(hasChildren);
    }

    public String getFavoriteMovie() {
      return favoriteMovie.get();
    }

    public void setFavoriteMovie(String movie) {
      favoriteMovie.set(movie);
    }

    @Override
    public String toString() {
      return firstName.getValue() + ", isMarried? " + married.getValue() + ", hasChildren? " + hasChildren.getValue() + ", favoriteMovie: " + favoriteMovie.get();
    }
  }

  class EditingCell extends TableCell<NamedProperty, Object> {

    private TextField textField;
    private CheckBox checkBox;

    public EditingCell() {
    }

    @Override
    public void startEdit() {
      if (!isEmpty()) {
        super.startEdit();
        if (getItem() instanceof Boolean) {
          createCheckBox();
          setText(null);
          setGraphic(checkBox);
        } else {
          createTextField();
          setText(null);
          setGraphic(textField);
          textField.selectAll();
        }
      }
    }

    @Override
    public void cancelEdit() {
      super.cancelEdit();
      if (getItem() instanceof Boolean) {
        setText(getItem().toString());
      } else {
        setText((String) getItem());
      }
      setGraphic(null);
    }

    @Override
    public void updateItem(Object item, boolean empty) {
      super.updateItem(item, empty);
      if (empty) {
        setText(null);
        setGraphic(null);
        getStyleClass().remove("highlighted-cell");
      } else {
        if (getItem() instanceof Boolean && (Boolean.FALSE.equals((Boolean) getItem()))) {
          getStyleClass().add("highlighted-cell");
        } else {
          getStyleClass().remove("highlighted-cell");
        }
        if (isEditing()) {
          if (getItem() instanceof Boolean) {
            if (checkBox != null) {
              checkBox.setSelected(getBoolean());
            }
            setText(null);
            setGraphic(checkBox);
          } else {
            if (textField != null) {
              textField.setText(getString());
            }
            setText(null);
            setGraphic(textField);
          }
        } else {
          setText(getString());
          setGraphic(null);
        }
      }
    }

    private void createTextField() {
      textField = new TextField(getString());
      textField.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
      textField.focusedProperty().addListener(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
          if (!newValue) {
            commitEdit(textField.getText());
          }
        }
      });
    }

    private void createCheckBox() {
      checkBox = new CheckBox();
      checkBox.setSelected(getBoolean());
      checkBox.setMinWidth(this.getWidth() - this.getGraphicTextGap() * 2);
      checkBox.focusedProperty().addListener(new ChangeListener<Boolean>() {
        @Override
        public void changed(ObservableValue<? extends Boolean> observable, Boolean oldValue, Boolean newValue) {
          if (!newValue) {
            commitEdit(checkBox.isSelected());
          }
        }
      });
    }

    private String getString() {
      return getItem() == null ? "" : getItem().toString();
    }

    private Boolean getBoolean() {
      return getItem() == null ? false : (Boolean) getItem();
    }
  }
}

你能看一下这个JavaFX问题吗?https://stackoverflow.com/questions/51196610/javafx-slider-2-different-colors-for-example-green-for-selected-area-and-red-f - GOXR3PLUS

1

看,如何理解CSSRef:

http://docs.oracle.com/javafx/2/api/javafx/scene/doc-files/cssref.html

看这里:

-fx-background-image:

uri [ , uri ]*

一系列由逗号隔开的图像URI。

看这里:

-fx-background-repeat

repeat-style [ , repeat-style ]*

其中repeat-style = repeat-x | repeat-y | [repeat | space | round | stretch | no-repeat]{1,2}

一系列由逗号隔开的值。系列中的每个repeat-style项应用于background-image系列中对应的图像。

看这里:

-fx-background-position

bg-position [ , bg-position ]* where = [ [ [ size | left | center | right ] [ size | top | center | bottom ]? ] | [ [ center | [ left | right ] size? ] || [ center | [ top | bottom ] size? ] ]

一系列由逗号隔开的值。系列中的每个bg-position项应用于background-image系列中对应的图像。

那么,您可以看到:您应该描述两个图像(每个图像为2x2像素 - 一个红色和一个灰色),每个图像都有两个对应的背景位置和重复样式。

如何做到?

例如:

{
-fx-backdround-image : "path_to_red", "path_to_grey";
-fx-background-repeat : repeat-x, stretch;
-fx-background-position : 0px 0px, 0px 2px;
}

我不能保证代码的可行性,但这个想法似乎是正确的。

当使用插图时,可能只需要颜色而不是图片。以下是来自原始JavaFX CSS的示例:

.table-row-cell:odd {
  -fx-background-color: -fx-table-cell-border-color, derive(-fx-control-inner-background,-5%);
  -fx-background-insets: 0, 0 0 1 0;
}

[6个字符...]


啊,我不知道可以直接指定多个图像,但如果我没错的话,甚至不需要图像 :-) 感谢提示!虽然还没有测试过,因为这需要一些更多的调整(.table-row-cell实际上没有边框,但通过背景模拟边框 - 使事情变得有点复杂),但我也认为它会起作用。更新将会跟进。 - stefan.at.kotlin
如果你认为可以使用背景颜色代替图片,那么似乎你是错的,因为颜色没有大小,它只是一种颜色,但是图片有大小,所以它可以被重复等等...否则,你将不得不指定有色物体的大小。 - Alexander Kirov
Alexander,也许仍然可以使用插入来实现,可以看一下你的帖子/回答(我已经编辑过了)。你对此有何看法? - stefan.at.kotlin
我明白了,你的答案比我的更正确。我的答案是错误的,因为它是针对区域的,但单元格不是一个区域。所以如果你的答案可行,就把它作为答案发布吧 =) - Alexander Kirov
CSS是一个强大的工具。Modena是JavaFX的新外观和感觉。它与Caspian完全不同。你可以看到,CSS的使用可以完全改变外观和感觉... - Alexander Kirov

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接