【问题标题】:JavaFX 8 TableView with ToggleButton to Show/Hide PasswordJavaFX 8 TableView 与切换按钮显示/隐藏密码
【发布时间】:2015-12-26 02:14:27
【问题描述】:

我正在尝试为每一行创建一个带有按钮的 TableView,以显示/隐藏该特定行的密码。我一直在使用this 问题来解决这个问题,但我现在真的很难过。所以我的问题是我怎样才能让它工作?我做错了什么?

这是我到目前为止所得到的。

Person.java

import javafx.beans.property.SimpleStringProperty;

public class Person {
    private final SimpleStringProperty name;
    private final SimpleStringProperty password;
    private final SimpleStringProperty maskedPassword;

    public Person(String n, String p) {
        this.name = new SimpleStringProperty(n);
        this.password = new SimpleStringProperty(p);
        this.maskedPassword = new SimpleStringProperty(maskPass(p));
    }

    private String maskPass(String password) {
        String output = "";
        for(int i = 0; i < password.length(); i++) {
            output += "\u2022";
        }
        return output;
    }

    public String getName() {
        return this.name.get();
    }

    public void setName(String n) {
        this.name.set(n);
    }

    public String getPassword() {
        return this.password.get();
    }

    public void setPassword(String p) {
        this.password.set(p);
        this.maskedPassword.set(maskPass(p));
    }

    public String getMaskedPassword() {
        return this.maskedPassword.get();
    }
}

和 Main.java

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.cell.PropertyValueFactory;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
import javafx.util.Callback;

public class Main extends Application{

    private final TableView<Person> table = new TableView<Person>();
    private final ObservableList<Person> data = FXCollections.observableArrayList(
                new Person("Moses Kiptanui", "pas$w0rd"),
                new Person("Bob Geldof", "hunter2"),
                new Person("Steve Finnan", "skadjhf"),
                new Person("Don Bradman", "CRICKET!"),
                new Person("Fetty Wap", "seventeen38")
            );

    @Override
    public void start(Stage stage) throws Exception {
        stage.setTitle("Table Experiment");

        TableColumn<Person, String> nameCol = new TableColumn<Person, String>("Name");
        nameCol.setMinWidth(100);
        nameCol.setCellValueFactory(new PropertyValueFactory<Person, String>("name"));

        TableColumn<Person, String> passwordCol = new TableColumn<Person, String>("Password");
        passwordCol.setMinWidth(100);
        passwordCol.setCellValueFactory(new PropertyValueFactory<Person, String>("maskedPassword"));

        TableColumn<Person, Person> btnCol = new TableColumn<Person, Person>("Show/Hide");
        btnCol.setCellFactory(new Callback<TableColumn<Person, Person>, TableCell<Person, Person>>() {
            @Override
            public TableCell<Person, Person> call(TableColumn<Person, Person> btnCol) {
                return new TableCell<Person, Person>() {
                    final ToggleButton btn = new ToggleButton("Show");
                    @Override
                    public void updateItem(final Person person, boolean empty) {
                        super.updateItem(person, empty);
                        if(person != null) {
                            if(btn.isSelected()) {
                                btn.setText("Hide");
                            } else {
                                btn.setText("Show");
                            }
                            setGraphic(btn);
                            btn.setOnAction(e -> {
                                if(btn.isSelected()) {
                                    passwordCol.setCellValueFactory(new PropertyValueFactory<Person, String>("password"));
                                } else {
                                    passwordCol.setCellValueFactory(new PropertyValueFactory<Person, String>("maskedPassword"));
                                }
                            });
                        }
                    }
                };
            }
        });

        table.setItems(data);
        table.getColumns().addAll(nameCol, passwordCol, btnCol);
        VBox vb = new VBox(table);
        stage.setScene(new Scene(vb));
        stage.show();
    }

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

这就是它目前的样子。我实际上已经有了它,所以按钮显示了,但它们显示在每一行中,而不仅仅是人!= null 的那些。当我确实让它们显示它们不起作用时。

【问题讨论】:

  • 太好了....在所有这些奇怪的方法之间是否存在问题?
  • 是的 - 我做错了什么?我怎样才能让它工作?
  • 您是否尝试查看 person 是否真的为空?您是否尝试在 if-else 中添加日志?

标签: java javafx javafx-8


【解决方案1】:

这是我的处理方法:

  1. 我不认为被屏蔽的密码,或者说密码是否应该被屏蔽,不应该是你Person类的一部分。这不是数据的固有部分,而是数据视图的一部分。
  2. 显示密码的TableColumn 需要使用自定义单元工厂,根据是否按下切换按钮来显示实际密码或隐藏密码。
  3. 您不能直接访问切换按钮,因为它们是虚拟化单元的一部分,因此您需要一些其他数据来跟踪显示哪些密码以及隐藏哪些密码。一种方法(我认为是最简单的)就是使用 ObservableSet,其中包含显示密码的项目。

现在您需要确保切换状态与可观察集匹配,因此您需要一个带有更新可观察集的切换按钮的侦听器,以及一个带有更新切换按钮的可观察集的侦听器。密码列的单元工厂还必须遵守ObservableSet 并相应地更新该列中的文本。

这是一个 SSCCE:

import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.beans.property.ReadOnlyObjectWrapper;
import javafx.beans.property.SimpleStringProperty;
import javafx.beans.property.StringProperty;
import javafx.collections.FXCollections;
import javafx.collections.ObservableSet;
import javafx.collections.SetChangeListener.Change;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.TableView;
import javafx.scene.control.ToggleButton;
import javafx.scene.layout.BorderPane;
import javafx.stage.Stage;

public class ShowHidePasswordExample extends Application {

    private static final Random RNG = new Random();

    @Override
    public void start(Stage primaryStage) {

        TableView<User> userTable = new TableView<>();

        // standard column stuff:
        TableColumn<User, String> userNameCol = new TableColumn<>("User Name");
        userNameCol.setCellValueFactory(cellData -> cellData.getValue().userNameProperty());

        TableColumn<User, String> passwordCol = new TableColumn<>("Password");
        passwordCol.setCellValueFactory(cellData -> cellData.getValue().passwordProperty());

        // which passwords are shown:
        ObservableSet<User> usersWithShownPasswords = FXCollections.observableSet();

        // cell factory for password column. Cells must show either the
        // real or masked password, and may 
        // need to update if usersWithShownPasswords changes:

        passwordCol.setCellFactory(c -> {

            // plain old cell:
            TableCell<User, String> cell = new TableCell<>();

            // if the cell is reused for an item from a different row, update it:
            cell.indexProperty().addListener((obs, oldIndex, newIndex) -> 
                updateCell(usersWithShownPasswords, cell));

            // if the password changes, update:

            cell.itemProperty().addListener((obs, oldItem, newItem) -> 
                updateCell(usersWithShownPasswords, cell));

            // if the set of users with shown password changes, update the cell:
            usersWithShownPasswords.addListener((Change<? extends User> change) ->
                updateCell(usersWithShownPasswords, cell));

            return cell ;
        });

        // column with show/hide buttons:
        TableColumn<User, User> showHidePasswordCol = new TableColumn<>("Show/Hide password");

        // just use whole row (User) as data for cells in this column:
        showHidePasswordCol.setCellValueFactory(cellData -> new ReadOnlyObjectWrapper<>(cellData.getValue()));

        // cell factory for toggle buttons:
        showHidePasswordCol.setCellFactory(c -> new TableCell<User, User>() {

            // create toggle button once for cell:
            private final ToggleButton button = new ToggleButton();

            // anonymous constructor:
            {
                // update toggle button state if usersWithShownPasswords changes:
                usersWithShownPasswords.addListener((Change<? extends User> change) -> {
                    button.setSelected(usersWithShownPasswords.contains(getItem()));
                });

                // update usersWithShownPasswords if toggle selection changes:
                button.selectedProperty().addListener((obs, wasSelected, isNowSelected) -> {
                    if (isNowSelected) {
                        usersWithShownPasswords.add(getItem());
                    } else {
                        usersWithShownPasswords.remove(getItem());
                    }
                });

                // keep text "Show" or "Hide" appropriately:
                button.textProperty().bind(Bindings.when(button.selectedProperty()).then("Hide").otherwise("Show"));
                setAlignment(Pos.CENTER);
            }

            // Just update graphic as needed:

            @Override
            public void updateItem(User item, boolean empty) {
                super.updateItem(item, empty);
                if (empty) {
                    setGraphic(null);
                } else {
                    button.setSelected(usersWithShownPasswords.contains(item));
                    setGraphic(button);
                }
            }
        });

        userTable.getColumns().addAll(Arrays.asList(userNameCol, passwordCol, showHidePasswordCol));

        userTable.getItems().addAll(createData());

        Scene scene = new Scene(new BorderPane(userTable), 800, 600);
        primaryStage.setScene(scene);
        primaryStage.show();
    }


    private void updateCell(ObservableSet<User> usersWithShownPasswords,
            TableCell<User, String> cell) {
        int index = cell.getIndex();
        TableView<User> table = cell.getTableView();
        if (index < 0 || index >= table.getItems().size()) {
            cell.setText("");
        } else {
            User user = table.getItems().get(index);
            if (usersWithShownPasswords.contains(user)) {
                cell.setText(user.getPassword()) ;
            } else {
                cell.setText(mask(user.getPassword()));
            }
        }
    }

    private String mask(String text) {
        char[] chars = new char[text.length()];
        Arrays.fill(chars, '*');
        return new String(chars);
    }

    private List<User> createData() {
        return IntStream.rangeClosed(1, 100)
                .mapToObj(i -> new User("User "+i, randomPassword()))
                .collect(Collectors.toList());
    }

    private String randomPassword() {
        int pwSize = 6 + RNG.nextInt(5);
        char[] chars = new char[pwSize];
        for (int i = 0 ; i < chars.length; i++) {
            chars[i] = (char)('a'+RNG.nextInt(26));
        }
        return new String(chars);
    }

    public static class User {
        private final StringProperty userName = new SimpleStringProperty();
        private final StringProperty password = new SimpleStringProperty();

        public User(String userName, String password) {
            setUserName(userName);
            setPassword(password);
        }

        public final StringProperty userNameProperty() {
            return this.userName;
        }

        public final java.lang.String getUserName() {
            return this.userNameProperty().get();
        }

        public final void setUserName(final java.lang.String userName) {
            this.userNameProperty().set(userName);
        }

        public final StringProperty passwordProperty() {
            return this.password;
        }

        public final java.lang.String getPassword() {
            return this.passwordProperty().get();
        }

        public final void setPassword(final java.lang.String password) {
            this.passwordProperty().set(password);
        }


    }

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

【讨论】:

    猜你喜欢
    • 2017-09-09
    • 2011-05-30
    • 2020-07-05
    • 2019-09-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-07-07
    相关资源
    最近更新 更多