【问题标题】:JavaFX TableView custom cell rendering split menu buttonJavaFX TableView 自定义单元格渲染拆分菜单按钮
【发布时间】:2017-11-18 12:42:44
【问题描述】:

我在 java fx 表视图组件中的自定义单元格渲染存在问题。我可以渲染拆分菜单按钮,但只能从表格的第二行渲染。

下面是我为生成该图像而创建的代码。

SplitMenuButtonApp.java

package com.example.splimenubtn;

import java.util.ArrayList;
import java.util.List;
import javafx.application.Application;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.SplitMenuButton;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.AnchorPane;
import javafx.stage.Stage;

public class SplitMenuButtonApp extends Application {

 private class Contact {

 private StringProperty firstName;
 private StringProperty lastName;

 public Contact() {}

 public Contact(String fName, String lName) {
  firstName = new SimpleStringProperty(fName);
  lastName = new SimpleStringProperty(lName);
 }

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

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

 public StringProperty firstName() {
  return firstName;
 }

 public String getLastName() {
  return lastName.get();
 }

 public void setLastName(String lName) {
  lastName.set(lName);
 }

 public StringProperty lastName() {
  return lastName;
 }
}

private ObservableList<Contact> data;
protected List<MenuItemFactory<Contact>> menuItemsList;
private TableView<Contact> table;

@Override
public void start(Stage primaryStage) throws Exception {
 // Init data list
 data = FXCollections.observableArrayList();
 data.add(new Contact("Mickey", "Mouse"));
 data.add(new Contact("Donald", "Duck"));
 data.add(new Contact("Fantasy", "Name"));
 initMenuButton();
 SplitMenuButtonFactory<Contact> sMBtn = new SplitMenuButtonFactory<>();
 sMBtn.setMenuItems(menuItemsList);
 SplitMenuButton actions = sMBtn.buildButton();
 // Build the list
 table = new TableView<>();
 TableColumn<Contact, String> col = new TableColumn<>("First Name");
 col.setCellValueFactory(c -> c.getValue().firstName);
 table.getColumns().add(col);
 col = new TableColumn<>("Last Name");
 col.setCellValueFactory(c -> c.getValue().lastName);
 table.getColumns().add(col);
 TableColumn<Contact, SplitMenuButton> aCol = new TableColumn<>("Action");
 aCol.setCellValueFactory(new PropertyValueFactory<>(""));
 aCol.setCellFactory(new ButtonCellFactory<>(actions));
 table.getColumns().add(aCol);
 table.setItems(data);
 AnchorPane root = new AnchorPane();
 AnchorPane.setTopAnchor(table, 5.0);
 AnchorPane.setRightAnchor(table, 5.0);
 AnchorPane.setBottomAnchor(table, 5.0);
 AnchorPane.setLeftAnchor(table, 5.0);
 root.getChildren().add(table);
 Scene s = new Scene(root, 600d, 300d);
 primaryStage.setScene(s);
 primaryStage.setTitle("Split menu button on table row");
 primaryStage.show();
}

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

private void initMenuButton() {
 if (menuItemsList == null) {
  menuItemsList = new ArrayList<>();
  menuItemsList.add(new MenuItemFactory<Contact>(MenuItemActions.EDIT, "Edit", true).setDataList(table));
menuItemsList.add(new MenuItemFactory<Contact>(MenuItemActions.DELETE, "Delete", false).setDataList(table));
 }
}

}

MenuItemActions.java 包 com.example.splimenubtn;

public enum MenuItemActions {
/**
 * Detail item
 */
 DETAILS,
/**
 * Edit/Update item
 */
EDIT,
/**
 * Delete item
 */
 DELETE;
}

MenuItemFactory.java

package com.example.splimenubtn;

import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TableView;

public class MenuItemFactory<S> {
 private MenuItemActions itemType;
 private String itemLbl;
 private TableView<S> table;
 private boolean defaultAction;

 public MenuItemFactory() {}

 public MenuItemFactory(MenuItemActions itemType, String itemLabel, boolean dA) {
  this.itemType = itemType;
  itemLbl = itemLabel;
  defaultAction = dA;
 }

 public MenuItemFactory<S> setDataList(TableView<S> t) {
  table = t;
  return this;
 }

 public boolean isDefault() {
  return defaultAction;
 }

 public MenuItem buildMenuItem() {
  MenuItem mI = new MenuItem();
  switch (itemType) {
   case DETAILS:
    mI.setText(itemLbl);
    mI.setOnAction(handleDetails());
   break;
   case EDIT:
    mI.setText(itemLbl);
    mI.setOnAction(handleEdit());
   break;
   case DELETE:
    mI.setText(itemLbl);
    mI.setOnAction(handleDelete());
   break;
   default:
   break;
  }
  return mI;
 }

 private EventHandler<ActionEvent> handleDetails() {
  return new EventHandler<ActionEvent>() {
   @Override
   public void handle(ActionEvent aE) {
    System.out.println("*** DETAIL REQUESTED ***");
   }
  };
 }

 private EventHandler<ActionEvent> handleEdit() {
  return new EventHandler<ActionEvent>() {
   @Override
   public void handle(ActionEvent aE) {
    System.out.println("*** EDIT REQUESTED ***");
   }
  };
 }

 private EventHandler<ActionEvent> handleDelete() {
  return new EventHandler<ActionEvent>() {
   @Override
   public void handle(ActionEvent aE) {
    System.out.println("*** DELETE REQUESTED ***");
   }
  };
 }
}

ButtonCellFactory.java

package com.example.splimenubtn;

import javafx.scene.control.ContentDisplay;
import javafx.scene.control.SplitMenuButton;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.util.Callback;

 public class ButtonCellFactory<S, T> implements Callback<TableColumn<S, T>, TableCell<S, T>> {

 private SplitMenuButton btn;

 public ButtonCellFactory() {}

 public ButtonCellFactory(SplitMenuButton b) {
  btn = b;
 }

 @Override
 public TableCell<S, T> call(TableColumn<S, T> param) {
  return new TableCell<S, T>() {
   @Override
   public void updateItem(T item, boolean empty) {
    super.updateItem(item, empty);
    if (empty) {
     setGraphic(null);
     setText(null);
    } else {
     setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
     setGraphic(btn);
    }
   }
  };
 }
}

SpliMenuButtonFactory.java

package com.example.splimenubtn;

import java.util.List;
import javafx.scene.control.MenuItem;
import javafx.scene.control.SplitMenuButton;

public class SplitMenuButtonFactory<T> {

private List<MenuItemFactory<T>> menuItems;

public SplitMenuButtonFactory() {}

public SplitMenuButtonFactory<T> setMenuItems(List<MenuItemFactory<T>> items) {
  menuItems = items;
  return this;
 }

 public SplitMenuButton buildButton() {
  SplitMenuButton menuBtn = new SplitMenuButton();
  for (MenuItemFactory<?> mIF : menuItems) {
   MenuItem btn = mIF.buildMenuItem();
   if (mIF.isDefault()) {
    menuBtn.setText(btn.getText());
    menuBtn.setOnAction(btn.getOnAction());
   }
   menuBtn.getItems().add(btn);
  }
  return menuBtn;
 }
}

正如您在图片中看到的,使用此代码,我可以创建 spli 菜单按钮并将其添加到 ta 表中,但仅在最后一行呈现。

我需要建议在另一行呈现拆分菜单按钮,感谢任何帮助。

【问题讨论】:

  • 您尝试对每个单元格使用相同的按钮,这是您无法做到的,因为任何节点只能在场景图中出现一次。不要将按钮设为工厂的属性:将其设为单元格的属性(并为每个单元格实例化一个新按钮)。
  • @James_D,需要将按钮构造函数作为单元格值吗?

标签: javafx tableview javafx-8


【解决方案1】:

因为您在每个单元格中都使用了相同的按钮,所以它只在单元格值的最后设置了一个按钮。

在SplitMenuButtonApp类中去掉这一行

 SplitMenuButton actions = sMBtn.buildButton();

并替换这一行

aCol.setCellFactory(new ButtonCellFactory&lt;&gt;(actions));

到下面的代码

Callback<TableColumn<Contact, SplitMenuButton>, TableCell<Contact, SplitMenuButton>> actionsCol = new Callback<TableColumn<Contact, SplitMenuButton>, TableCell<Contact, SplitMenuButton>>() {
                @Override
                public TableCell call(final TableColumn<Contact, SplitMenuButton> param) {
                    final TableCell<Contact, SplitMenuButton> cell = new TableCell<Contact, SplitMenuButton>() {
                        SplitMenuButton actions = sMBtn.buildButton();
                        @Override
                        public void updateItem(SplitMenuButton item, boolean empty) {
                            super.updateItem(item, empty);
                            if (empty) {
                                setGraphic(null);
                                setText(null);
                            } else {
                                setGraphic(actions);
                                setText(null);
                            }
                        }
                    };
                    return cell;
            }
 };

aCol.setCellFactory(actionsCol);

我希望这段代码对你有用:)

【讨论】:

  • 感谢您的回复 Keyur Bhanderi,我选择了类似的解决方案,使用外部工厂并将菜单项列表传递给它。在updateItem override 中移动构建按钮调用
  • @PatrickT 如果相同的解决方案那么别忘了接受答案!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-11-19
  • 2012-06-08
  • 2015-09-17
  • 1970-01-01
  • 1970-01-01
  • 2019-04-22
  • 1970-01-01
相关资源
最近更新 更多