1

I was wandering, is there a property, to which I can attach a listener, that represents the action of "When the list view starts to show the ScrollBar"?

The thing is, that I have an output to the ListView, and at some point the ListView's scrollBar triggers. I want to capture that event and scroll to the bottom so the next addition to the ListView object will automatically scroll to the bottom.

I do understand that I may use the listener for the item change, but I want to preserve the behavior when I decide to scroll up and it will not scroll back to the last position as soon as an item gets added.

Draaksward
  • 759
  • 7
  • 32
  • When the ScrollBar shows you are probably changing the visibility of the already existing object I'd assume? In that case you could listen on the visible flag for the ScrollBar. I have never tried this though, so I can't guarantee this works but it might be an approach worth trying. – Ben Mar 22 '18 at 10:16
  • Yes, but I having trouble finding the point where in ListView to listen to this flag. – Draaksward Mar 22 '18 at 10:21
  • There is no existing event for this flag changing, so you need to create your own. You can have a look [here](https://stackoverflow.com/questions/27416758/how-to-emit-and-handle-custom-events) for an introduction to custom events. – Ben Mar 22 '18 at 10:23
  • Thanks! Still new to JavaFX and didn't know that you can make own events. – Draaksward Mar 22 '18 at 10:27
  • But still... even firing custom events require a trigger. The question of "on what to focus" remains. – Draaksward Mar 22 '18 at 10:51

3 Answers3

2

Try this:

for (Node node : listView.lookupAll(".scroll-bar")) {
    if (node instanceof ScrollBar) {
        ScrollBar scrollBar = (ScrollBar) node;
        scrollBar.visibleProperty().addListener((observable, oldValue, newValue) -> {
            System.out.println(String.format("%s ScrollBar Visible Property Changed From: %s To: %s", scrollBar.getOrientation(), oldValue, newValue));
        });
    }
}

Output:

VERTICAL ScrollBar Visible Property Changed From: false To: true

Note: This code should be added after you set the items in your ListView to avoid NullPointerException

Miss Chanandler Bong
  • 4,081
  • 10
  • 26
  • 36
  • Thanks. The 'lookupAll' looks interesting. Will give it a try. – Draaksward Mar 23 '18 at 06:42
  • Yes, did the trick. Since my list is dynamic and isn't loaded from the beginning, attached a listener to item change in order to spot the scrollbar. Thank you. – Draaksward Mar 23 '18 at 07:09
1

Maybe this can help you:

  private boolean scroll = true;
  private ListView<String> listView = new ListView<>();

  Constructor(){
    outputListView.addEventFilter(ScrollEvent.ANY, (e) ->{ //get every scroll event
      if(e.getTextDeltaY() > 0){ //set Scroll event to false when the user scrolls up
        scroll = false;
      }
    });
  }

  public void addToListView(String s){
    listView.getItems().add(s);
    if(scroll)
        listView.scrollTo(listView.getItems().size() -1 );
  }

In this case the listView scrolls down when the scroll bar will shown but when the user scrolls up the listView doesn't scroll down anymore until the user scroll at the bottom of Listview (this is a standart listview behavior)

Morchul
  • 1,987
  • 1
  • 7
  • 21
-1

Personally, I'm not crazy about the CSS lookup approach, because it feels too much like a hack to me (I don't like relying on hard-coded property lookups).

What I like to do instead is just search through the physical layout tree for a scrollbar with the proper orientation:

    public static Optional<ScrollBar> getScrollbar(Node n, Orientation o) {
        LinkedList<Node> queue = new LinkedList<>();
        queue.add(n);
        
        while(! queue.isEmpty()) {
            n = queue.removeFirst();
            
            if (n instanceof ScrollBar) {
                ScrollBar sb = (ScrollBar)n;
                
                if (sb.getOrientation() == o) {
                    return Optional.of(sb);
                }
            }
            
            if (n instanceof Parent) {
                queue.addAll(((Parent) n).getChildrenUnmodifiable());
            }
        }
        
        return Optional.empty();
    }

For example, if I am anchoring a floating button to the upper-right of my list view, when the layout bounds property of my list view changes, I'll do:

    Optional<ScrollBar> sb = getScrollbar(listView, Orientation.VERTICAL);
        
    if (sb.isPresent() && sb.get().isVisible()) {
        StackPane.setMargin(hoverButton, new Insets(1, sb.get().getWidth(), 0, 0));
    }
    else {
        StackPane.setMargin(hoverButton, new Insets(1, 1, 0, 0));
    }

And that'll scoot my button over so it doesn't hover over the scrollbar.

captfoss
  • 9
  • 2