如何使用 css 在 JavaFX 中更改 ListView 单元格的文本颜色
How to change ListView cell's text color in JavaFX using css
所以我有一个列表,里面全是我数据库中的名字。这些名字代表在公司工作或曾经在公司工作的员工。我已经看到可以更改单元格的文本颜色,但是当我应用它时,它会更改所有这些颜色。我真正想要的是改变不再工作的客户端的颜色。
我在列表中添加了 "not active",这样我就可以将 "active" 和 "not active" 客户分开。
这是我在 ArrayList 中添加员工姓名的代码:
public ArrayList<String> search(String unit, String name)
{
ArrayList<String> temp= new ArrayList<String>();
String sql="";
if(unit=="all units")
sql="SELECT * FROM employees WHERE name='"+name+"';";
else
sql="SELECT * FROM employees WHERE unit='"+unit+"' AND name='"+name+"';";
try {
ResultSet rs=bp.select(sql);
while(rs.next())
{
if(rs.getInt("active")==1)
temp.add(rs.getString("name"));
else
temp.add(rs.getString("name")+" - not active");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Collections.sort(temp);
return temp;
}
这是我在 ListView 中添加 ArrayList 的代码:
for (String item: u.search(unitCB.getValue(),nameTF.getText()))
SearchLW.getItems().add(item);
现在,我想知道是否可以使用 css 将此列表中所有不再活跃的员工的文本颜色更改为红色?如果不是,关于如何更改此特定 ListView 单元格的文本颜色的另一种解决方案将会有所帮助。
您可以使用 CSS 伪 class 来表示 "inactive" 样式并在 CSS 文件中设置样式。使用您当前的代码,它看起来像:
ListView<String> listView = new ListView<>();
PseudoClass inactive = PseudoClass.getPseudoClass("inactive");
listView.setCellFactory(lv -> new ListCell<String>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item);
pseudoClassStateChanged(inactive, item != null && item.endsWith(" - not active"));
}
});
然后在外部 css 文件中你可以定义你想要的样式:
.list-cell:inactive {
-fx-text-fill: red ;
}
仅仅在这里测试字符串值是非常脆弱的(想象一下如果您尝试将应用程序国际化,或者当您的老板告诉您更改演示文稿以便 "not active"
位于括号中,或者其他任何情况时会发生什么...)。我建议创建一个 Employee
class:
public class Employee {
private final String name ;
private final boolean active ;
public Employee(String name, boolean active) {
this.name = name ;
this.active = active ;
}
public String getName() {
return name ;
}
public boolean isActive() {
return active ;
}
}
然后你做
ListView<Employee> listView = new ListView<>();
PseudoClass inactive = PseudoClass.getPseudoClass("inactive");
listView.setCellFactory(lv -> new ListCell<Employee>() {
@Override
protected void updateItem(Employee employee, boolean empty) {
super.updateItem(employee, empty);
if (empty) {
setText(null);
pseudoClassStateChanged(inactive, false);
} else {
if (employee.isActive()) {
setText(employee.getName());
pseudoClassStateChanged(inactive, false);
} else {
setText(employee.getName() + " - not active");
pseudoClassStateChanged(inactive, true);
}
}
}
});
并对您发布的方法进行明显更新:
public ArrayList<Employee> search(String unit, String name) {
ArrayList<Employee> temp= new ArrayList<>();
String sql="SELECT * FROM employees WHERE unit like ? AND name=?";
try (PreparedStatement pStmnt = conn.prepareStatement(sql)) {
if("all units".equals(unit))
pStmnt.setString(1, "%");
else
pStmnt.setString(1, unit);
pStmnt.setString(2, name);
ResultSet rs=pStmnt.executeQuery();
while(rs.next())
{
temp.add(new Employee(rs.getString("name"), rs.getInt("active")==1);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
temp.sort(Comparator.comparing(Employee::getName));
return temp;
}
这是一个替代实现,它只使用样式 class 而不是伪 class。在这种情况下,伪class 实现可能更好。
不过,也许这个解决方案的扩展性可能更好一些,因为您可以为诸如选定与未选定或聚焦与未聚焦的单元格之类的事物指定单独的伪-class 样式,如果您使用,这可能会很困难自定义伪class(虽然我不确定)。
ActivityListViewer.java
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class ActivityListViewer extends Application {
@Override
public void start(Stage stage) throws Exception {
ListView<Activity> activityListView = new ListView<>(
FXCollections.observableArrayList(
new Activity("Fetch super suit from drycleaner", false),
new Activity("Interview governor with Jimmy", false),
new Activity("Rescue stranded kitten", true),
new Activity("Dinner date with Lois", false)
)
);
activityListView.setCellFactory(param -> new ListCell<Activity>() {
static final String ACTIVE_CLASS = "active";
@Override
protected void updateItem(Activity item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null || item.getName() == null) {
setText(null);
getStyleClass().remove(ACTIVE_CLASS);
} else {
if (item.isActive()) {
setText(item.getName() + " - active");
} else {
setText(item.getName());
}
if (item.isActive() && !getStyleClass().contains(ACTIVE_CLASS)) {
getStyleClass().add(ACTIVE_CLASS);
} else {
getStyleClass().remove(ACTIVE_CLASS);
}
}
}
});
activityListView.setPrefHeight(150);
Scene scene = new Scene(activityListView);
scene.getStylesheets().add(
this.getClass().getResource("activity.css").toExternalForm()
);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class Activity {
private final StringProperty nameProperty;
private final BooleanProperty activeProperty;
public Activity(String name, boolean active) {
this.nameProperty = new SimpleStringProperty(name);
this.activeProperty = new SimpleBooleanProperty(active);
}
public String getName() {
return nameProperty.get();
}
public StringProperty nameProperty() {
return nameProperty;
}
public boolean isActive() {
return activeProperty.getValue() != null
? activeProperty.getValue()
: false;
}
public BooleanProperty activeProperty() {
return activeProperty;
}
}
activity.css
.active {
-fx-text-fill: forestgreen;
}
.active:selected {
-fx-text-fill: lawngreen;
}
所以我有一个列表,里面全是我数据库中的名字。这些名字代表在公司工作或曾经在公司工作的员工。我已经看到可以更改单元格的文本颜色,但是当我应用它时,它会更改所有这些颜色。我真正想要的是改变不再工作的客户端的颜色。
我在列表中添加了 "not active",这样我就可以将 "active" 和 "not active" 客户分开。
这是我在 ArrayList 中添加员工姓名的代码:
public ArrayList<String> search(String unit, String name)
{
ArrayList<String> temp= new ArrayList<String>();
String sql="";
if(unit=="all units")
sql="SELECT * FROM employees WHERE name='"+name+"';";
else
sql="SELECT * FROM employees WHERE unit='"+unit+"' AND name='"+name+"';";
try {
ResultSet rs=bp.select(sql);
while(rs.next())
{
if(rs.getInt("active")==1)
temp.add(rs.getString("name"));
else
temp.add(rs.getString("name")+" - not active");
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
Collections.sort(temp);
return temp;
}
这是我在 ListView 中添加 ArrayList 的代码:
for (String item: u.search(unitCB.getValue(),nameTF.getText()))
SearchLW.getItems().add(item);
现在,我想知道是否可以使用 css 将此列表中所有不再活跃的员工的文本颜色更改为红色?如果不是,关于如何更改此特定 ListView 单元格的文本颜色的另一种解决方案将会有所帮助。
您可以使用 CSS 伪 class 来表示 "inactive" 样式并在 CSS 文件中设置样式。使用您当前的代码,它看起来像:
ListView<String> listView = new ListView<>();
PseudoClass inactive = PseudoClass.getPseudoClass("inactive");
listView.setCellFactory(lv -> new ListCell<String>() {
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
setText(empty ? null : item);
pseudoClassStateChanged(inactive, item != null && item.endsWith(" - not active"));
}
});
然后在外部 css 文件中你可以定义你想要的样式:
.list-cell:inactive {
-fx-text-fill: red ;
}
仅仅在这里测试字符串值是非常脆弱的(想象一下如果您尝试将应用程序国际化,或者当您的老板告诉您更改演示文稿以便 "not active"
位于括号中,或者其他任何情况时会发生什么...)。我建议创建一个 Employee
class:
public class Employee {
private final String name ;
private final boolean active ;
public Employee(String name, boolean active) {
this.name = name ;
this.active = active ;
}
public String getName() {
return name ;
}
public boolean isActive() {
return active ;
}
}
然后你做
ListView<Employee> listView = new ListView<>();
PseudoClass inactive = PseudoClass.getPseudoClass("inactive");
listView.setCellFactory(lv -> new ListCell<Employee>() {
@Override
protected void updateItem(Employee employee, boolean empty) {
super.updateItem(employee, empty);
if (empty) {
setText(null);
pseudoClassStateChanged(inactive, false);
} else {
if (employee.isActive()) {
setText(employee.getName());
pseudoClassStateChanged(inactive, false);
} else {
setText(employee.getName() + " - not active");
pseudoClassStateChanged(inactive, true);
}
}
}
});
并对您发布的方法进行明显更新:
public ArrayList<Employee> search(String unit, String name) {
ArrayList<Employee> temp= new ArrayList<>();
String sql="SELECT * FROM employees WHERE unit like ? AND name=?";
try (PreparedStatement pStmnt = conn.prepareStatement(sql)) {
if("all units".equals(unit))
pStmnt.setString(1, "%");
else
pStmnt.setString(1, unit);
pStmnt.setString(2, name);
ResultSet rs=pStmnt.executeQuery();
while(rs.next())
{
temp.add(new Employee(rs.getString("name"), rs.getInt("active")==1);
}
} catch (SQLException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
temp.sort(Comparator.comparing(Employee::getName));
return temp;
}
这是一个替代实现,它只使用样式 class 而不是伪 class。在这种情况下,伪class 实现可能更好。
不过,也许这个解决方案的扩展性可能更好一些,因为您可以为诸如选定与未选定或聚焦与未聚焦的单元格之类的事物指定单独的伪-class 样式,如果您使用,这可能会很困难自定义伪class(虽然我不确定)。
ActivityListViewer.java
import javafx.application.Application;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;
public class ActivityListViewer extends Application {
@Override
public void start(Stage stage) throws Exception {
ListView<Activity> activityListView = new ListView<>(
FXCollections.observableArrayList(
new Activity("Fetch super suit from drycleaner", false),
new Activity("Interview governor with Jimmy", false),
new Activity("Rescue stranded kitten", true),
new Activity("Dinner date with Lois", false)
)
);
activityListView.setCellFactory(param -> new ListCell<Activity>() {
static final String ACTIVE_CLASS = "active";
@Override
protected void updateItem(Activity item, boolean empty) {
super.updateItem(item, empty);
if (empty || item == null || item.getName() == null) {
setText(null);
getStyleClass().remove(ACTIVE_CLASS);
} else {
if (item.isActive()) {
setText(item.getName() + " - active");
} else {
setText(item.getName());
}
if (item.isActive() && !getStyleClass().contains(ACTIVE_CLASS)) {
getStyleClass().add(ACTIVE_CLASS);
} else {
getStyleClass().remove(ACTIVE_CLASS);
}
}
}
});
activityListView.setPrefHeight(150);
Scene scene = new Scene(activityListView);
scene.getStylesheets().add(
this.getClass().getResource("activity.css").toExternalForm()
);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args) {
launch(args);
}
}
class Activity {
private final StringProperty nameProperty;
private final BooleanProperty activeProperty;
public Activity(String name, boolean active) {
this.nameProperty = new SimpleStringProperty(name);
this.activeProperty = new SimpleBooleanProperty(active);
}
public String getName() {
return nameProperty.get();
}
public StringProperty nameProperty() {
return nameProperty;
}
public boolean isActive() {
return activeProperty.getValue() != null
? activeProperty.getValue()
: false;
}
public BooleanProperty activeProperty() {
return activeProperty;
}
}
activity.css
.active {
-fx-text-fill: forestgreen;
}
.active:selected {
-fx-text-fill: lawngreen;
}