简体   繁体   中英

How to make a ListView selectable but not editable

so I'm writing a javafx app and I need to be able to select the cells from the list view (for copy paste purposes) but I don't want to make it editable, I mean, the content cannot be changed unless I want to (allowing it through a button, for example).

So I have the following code:

import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public class Main extends Application {

    @Override
    public void start(Stage primaryStage) throws Exception{
        List<String> contacts = new ArrayList<>(Arrays.asList("968787522","3424234234","2343234324"));
        ListView<String> contactsList = new ListView();
        contactsList.setItems(FXCollections.observableArrayList(contacts));
        //this gives me the ability to edit the row as text field but I want this text field to not be editable
        contactsList.setCellFactory(TextFieldListCell.forListView());
        StackPane pane = new StackPane();
        pane.getChildren().add(contactsList);
        primaryStage.setScene(new Scene(pane, 300, 275));
        primaryStage.show();    }


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

and if I set ' contactsList ' as not editable, I'm not able to edit, neither select.

As you can see (image bellow),I'm editing the cell, but I want to be able to select the text(not the item), but I don't want to be able to delete characters (text selectable but not editable). 在此处输入图片说明

so after breaking my head off, lots of research and API reading, I came up with a solution. This does EXACTLY what I wanted to do. Here is the demo if someone needs it ;) So the idea is, each time we want to select the content of a row we need to select the row, get the textField and set the editing to true or false, (every time). So in the demo that I made, I placed a button so you can toggle the editing to true or false to be sure that's is working, and how is working.

Cheers.

I commented some of the code for better understanding, if you have any questions about this just let me know.

package sample;

import com.sun.javafx.scene.control.skin.VirtualFlow;
import javafx.application.Application;
import javafx.application.Platform;
import javafx.collections.FXCollections;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.scene.control.cell.TextFieldListCell;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;


public class Main extends Application {
private boolean editable = false;

public static  IndexedCell getCell(final Control control, final int index) {
    return getVirtualFlow(control).getCell(index);
}

public static VirtualFlow<?> getVirtualFlow(Control control) {
    Group group = new Group();
    Scene scene = new Scene(group);
    Stage stage = new Stage();

    if(control.getScene() == null) {
        group.getChildren().setAll(control);
        stage.setScene(scene);
        stage.show();
    }

    VirtualFlow<?>flow = (VirtualFlow<?>) control.lookup("#virtual-flow");
    return flow;
}

public void setEditable(ListView contactsList){
        //this needs to be done since we need to run our code after the text field was rendered
        //so we need to invoke our code after this happens, if not it will throw a null pointer...
        Platform.runLater(() -> {
            //this is one of the most important guys because javafx api says that
            //TextFieldListCell.forListView() allows editing of the cell content when the cell is double-clicked,
            // or when {@link ListView#edit(int)} is called.
            int rowIndex = contactsList.getSelectionModel().getSelectedIndex();
            contactsList.edit(rowIndex);
            ListCell rootCell = (ListCell) getCell(contactsList, rowIndex);
            TextField textField = (TextField) rootCell.getGraphic();
            textField.setEditable(editable);
        });
}

@Override
public void start(Stage primaryStage) throws Exception{
    List<String> contacts = new ArrayList<>(Arrays.asList("968787522","3424234234","2343234324"));
    ListView<String> contactsList = new ListView();
    contactsList.setItems(FXCollections.observableArrayList(contacts));
    contactsList.setEditable(true);

    //this gives me the ability to edit the row as text field but I want this text field to not be editable
    contactsList.setCellFactory(TextFieldListCell.forListView());
    contactsList.setOnEditStart(e -> {
        setEditable(contactsList);
    });

    StackPane pane = new StackPane();
    Button editBtn = new Button("Toggle edit");
    editBtn.setOnAction(event -> {
        editable = !editable;
        editBtn.setText("Editing = " + editable);
        //to cancel any editing that might be occuring
        contactsList.getSelectionModel().clearSelection();
    });
    pane.getChildren().addAll(contactsList,editBtn);
    primaryStage.setScene(new Scene(pane, 300, 275));
    primaryStage.show();
}

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

If I understand you correctly, it is not necessary to set the listview to 'not editable', as the default behaviour should suffice for your purpose. Take a look at this code, for example:

import javafx.application.Application;
import javafx.event.EventHandler;
import javafx.scene.Scene;
import javafx.scene.control.ListView;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class NewFXMain extends Application {

    @Override
    public void start(Stage primaryStage) {
        ListView listView = new ListView();
        listView.getItems().addAll("one","two","three","four");

        listView.setOnMouseClicked(new EventHandler<MouseEvent>() {
            @Override
            public void handle(MouseEvent event) {
                System.out.println(listView.getSelectionModel().getSelectedItem());
            }
        });

        StackPane root = new StackPane();
        root.getChildren().add(listView);
        Scene scene = new Scene(root, 300, 250);

        primaryStage.setTitle("ListView Example");
        primaryStage.setScene(scene);
        primaryStage.show();
    }

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

}

I changed nothing about the editable-property of the ListView, but I can select every item, without being able to edit it (in the sense of changing its value). You can easily add an EventHandler to the ListView to perform whatever operation you want to perform. You could also add an EventHandler to every cell of the ListView by manipulating the CellFactory, as shown in this answer: How to handle ListView item clicked action?

Here's what works for me:

    TableView<DataBean> table = new TableView<>();
    table.setItems(...); // list of some DataBean objects with dataBeanField proprty
    table.setEditable(true);

    TableColumn<DataBean, String> column = new TableColumn<>("SomeData");
    column.setCellValueFactory(new PropertyValueFactory<DataBean, String>("dataBeanField"));
    column.setCellFactory(new Callback<TableColumn<DataBean, String>, TableCell<DataBean, String>>() {
        @Override
        public TableCell<DataBean, String> call(TableColumn<DataBean, String> param) {
            return new TextFieldTableCell<>(new DefaultStringConverter() {
                private String defaultValue = "";

                @Override
                public String fromString(String newValue) {
                    return super.fromString(defaultValue);
                }

                @Override
                public String toString(String value) {
                    return defaultValue = super.toString(value);
                }
            });
        }
    });

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM