Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Javafx: How to limit cell width to the width of the ListView

Tags:

javafx-8

Have been struggling with this one.

I'd like the lists cells to be limited to the width of the parent ListView, and for the containing text to show ellipsis dots if the text is clipped/overrunning.

But no-matter what I do, the width of all the cells grows to the length of the longest text in those cells. And the text in the overrunning cell(s) is 'clipped' rather than what I want which is the ellipsis dots.

My ListView is defined in fxml:

<BorderPane fx:id="listViewResultsParentPane">
    <center>
        <ListView fx:id="listViewResults"/>
    </center>
</BorderPane>

And I have a list cell class:

public class ResultsListCell extends ListCell<ResultsListItem> {
    @Override
    public void updateItem(final ResultsListItem item, final boolean empty) {
        super.updateItem(item, empty);
        if(empty || item == null){
            setText("");
        } else {
            setText(item.title);
            setTextOverrun(OverrunStyle.ELLIPSIS);
            setEllipsisString("...");
        }
    }
}

And to get rid of the horizontal scrollbar, I'm using this css:

.list-view#listViewResults .scroll-bar:horizontal .increment-arrow,
.list-view#listViewResults .scroll-bar:horizontal .decrement-arrow,
.list-view#listViewResults .scroll-bar:horizontal .increment-button,
.list-view#listViewResults .scroll-bar:horizontal .decrement-button {
    -fx-padding:0;
}

To set/limit the width of the cells I have tried:

  • Setting the width of each cell to the width of the parent, in the updateCell call.
  • Binding the prefHeight and maxHeight properties of each cell to that of the parent
  • Setting the maxHeight and prefHeight of the cell css property to 100%

Any help would be appreciated.

like image 938
Ben Avatar asked May 10 '16 05:05

Ben


2 Answers

Solution

Bind the width of each cell to the preferred width of the list, less a couple of pixels to allow for the list borders. And set the max width of each cell to its preferred size:

prefWidthProperty().bind(list.widthProperty().subtract(2));
setMaxWidth(Control.USE_PREF_SIZE);

You don't need any custom CSS to remove the horizontal scroll bar. The horizontal scroll bar will only be shown if a displayed list cell exceeds the width of the ListView and the constraints specified above ensure that will never occur. You also don't need to set the text overrun style and the ellipsis string for the cell, as those will already be set to values you want by default.

sample app

Complete Sample Program

import javafx.application.Application;
import javafx.collections.*;
import javafx.scene.Scene;
import javafx.scene.control.*;
import javafx.stage.Stage;

public class ElidedListView extends Application {
    @Override
    public void start(Stage stage) {
        ListView<String> list = new ListView<>();
        ObservableList<String> items = FXCollections.observableArrayList(
                "Lorem ipsum dolor sit amet, consectetur adipiscing elit.",
                "Aenean nibh ipsum, semper nec ipsum quis, dignissim gravida arcu.",
                "Sed posuere auctor magna vel suscipit.",
                "Aenean eu diam at dolor auctor porta.",
                "Integer tincidunt ex metus, in euismod velit facilisis in.",
                "Praesent purus mi, mattis rutrum egestas vitae, elementum vel dolo"
        );
        list.setItems(items);
        list.setCellFactory(param -> new ListCell<String>() {
            {
                prefWidthProperty().bind(list.widthProperty().subtract(2));
                setMaxWidth(Control.USE_PREF_SIZE);
            }
            @Override
            protected void updateItem(String item, boolean empty) {
                super.updateItem(item, empty);

                if (item != null && !empty) {
                    setText(item);
                } else {
                    setText(null);
                }
            }
        });

        stage.setScene(new Scene(list));
        stage.show();
    }

    public static void main(String[] args) {
        launch(args);
    }
}
like image 178
jewelsea Avatar answered Oct 15 '22 06:10

jewelsea


A way simpler and better solution is it to set the preferred width of the list cell to 0. The list cell will be resized to viewport width of the list anyway, but it won't be wider than the available space. This effectively hides the horizontal scrollbar, reacts correctly with/without vertical scrollbar and does not need any bindings. (Only exception is a list cell that has a computed minimal width higher than the listview width)

list.setCellFactory(l -> new ListCell<String>() {
    { // constructor
        setPrefWidth(0); // avoids the issues
    }

    @Override
    protected void updateItem(String item, boolean empty) {
        super.updateItem(item, empty);
        // do whatever you like
    }
});
like image 22
tobain Avatar answered Oct 15 '22 08:10

tobain