2

I have a ListView with some Labels in it. The labels' width property is bound to the width property of the ListView but they seem to be slightly larger meaning that a horizontal scrollbar is shown on the list view. What I want is to fit the labels in the list view without the scrollbar on the bottom. I have looked at various padding and insets values on both the label and the list view but none I have found are the culprit (most are zero).

enter image description here

Here is an example which demonstrates the problem.

import javafx.application.Application;
import javafx.scene.Node;
import javafx.scene.Scene;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.stage.Stage;

public class ListViewScrollExample extends Application {

   private ListView<Node> listView;

   @Override
   public void start(Stage stage) throws Exception {
       listView = new ListView<>();
       addItem("Some quite long string to demonstrate the problem");

       Scene scene = new Scene(listView);
       stage.setScene(scene);
       stage.show();
    }

    public void addItem(String item) {
        Label label = new Label(item);
        label.setWrapText(true);
        label.maxWidthProperty().bind(listView.widthProperty());
        listView.getItems().add(label);
    }

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

}
Janion
  • 98
  • 7
  • If you allow wrapping of the text, how do you want the width to be calculated? I can shrink the list view until the label displayed 1 word on each line. – user1803551 Aug 04 '17 at 15:38
  • @user1803551 The problem is that the scrollbar appears at the bottom of the ListView. the wrapping works as I expect but the label area seems to extend out to the side even when the text itself doesn't – Janion Aug 04 '17 at 15:59
  • So you want to remove the scrollbar? Like [this](https://stackoverflow.com/questions/12670137/how-to-hide-the-horizontal-scrollbar-of-a-listview-in-javafx)? – user1803551 Aug 04 '17 at 16:39

2 Answers2

4

The default CSS file adds padding to a ListCell (line 2316 in the current release):

.list-cell {
    -fx-padding: 0.25em 0.583em 0.25em 0.583em; /* 3 7 3 7 */
}

It generally a bad idea to use Node instances as the data backing a ListView: you should use String in this example, and use the cell factory to create a label displaying the string that is configured as you need. The following seems to work for your example:

import javafx.application.Application;
import javafx.beans.binding.Bindings;
import javafx.scene.Scene;
import javafx.scene.control.ContentDisplay;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.stage.Stage;

public class ListViewScrollExample extends Application {

   private ListView<String> listView;

   @Override
   public void start(Stage stage) throws Exception {
       listView = new ListView<>();
       listView.getItems().add("Some quite long string to demonstrate the problem");

       listView.setCellFactory(lv -> {
           ListCell<String> cell = new ListCell<String>() {

               private Label label = new Label();
               {
                   label.setWrapText(true);
                   label.maxWidthProperty().bind(Bindings.createDoubleBinding(
                           () -> getWidth() - getPadding().getLeft() - getPadding().getRight() - 1, 
                           widthProperty(), paddingProperty()));
                   setContentDisplay(ContentDisplay.GRAPHIC_ONLY);
               }

               @Override
               protected void updateItem(String item, boolean empty) {
                   super.updateItem(item, empty);
                   if (empty) {
                       setGraphic(null);
                   } else {
                       label.setText(item);
                       setGraphic(label);
                   }
               }
           };
           return cell ;
       });

       Scene scene = new Scene(listView);
       stage.setScene(scene);
       stage.show();
    }


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

}

List view at default width List view at wider width List view at narrow width

Here I created a list cell that displays a label as its graphic, with the text of the label set to the string to be displayed. The constructor for the cell binds the label's max width to the width of the cell, less any space required for padding. The call to setContentDisplay(ContentDisplay.GRAPHIC_ONLY) appears necessary, so the cell doesn't try to allocate any space for text.

It may be possible to do this by setting the text directly on the list cell and calling setWrapText(true) on the cell (which is, after all, also a subclass of Labeled), but I couldn't get it to work this way.

James_D
  • 201,275
  • 16
  • 291
  • 322
  • That works, thanks. I wouldn't have known to use that method – Janion Aug 07 '17 at 07:57
  • I did find that the minus one in the binding, and the call to setContentDisplay(ContentDisplay.GRAPHIC_ONLY) were unnecessary though – Janion Aug 07 '17 at 08:28
-1

I couldn't replicate the problem but you can try the following instead of label.maxWidthProperty().bind(listView.widthProperty());

double i = Double.parseDouble(listView.widthProperty().toString());
label.setMaxWidth((i-2.0));

You can change the 2.0 to any pixel count you need to alter the screen by.

Mark Rees
  • 277
  • 3
  • 12
  • 2
    I am trying to avoid a hard-coded constant by which to shrink the label size. Also Why did you use Double.parseDouble(listView.widthProperty().toString()) instead of listView.widthProperty().get()? – Janion Aug 04 '17 at 16:01
  • No real reason to use double.parse instead of that but that's where my mind went first, the long way. You could try `label.setMaxWidth(listView.getPrefWidth());` instead. It all should do the same but the values can be slightly different. – Mark Rees Aug 04 '17 at 17:40
  • If you're going with a hard-coded amount by which to shrink it, just use the same binding approach: `label.maxWidthProperty().bind(listView.widthProperty().subtract(2.0));` – James_D Aug 04 '17 at 19:14
  • Also, `Double.parseDouble(listView.widthProperty().toString())` won't even work: the `toString()` method of the property returns something like `"ReadOnlyDoubleProperty [bean: ListView@27e7a3d1[styleClass=root list-view], name: width, value: 248.0]"`, which obviously cannot be parsed to a double. – James_D Aug 04 '17 at 19:42