带有触摸事件的JavaFX ListView,用于向上和向下滚动

时间:2014-10-23 21:11:07

标签: listview javafx gestures

我想用手势实现一个可滚动的ListView,就像在手机和平​​板电脑中一样,用手指向上和向下滚动我的列表。但是,当我单击列表时,我的当前列表会选择一个项目。我怎样才能做到这一点?我在Oracle教程中找不到任何示例。

private ObservableList<Document> items = FXCollections.observableArrayList();
@FXML ListView<Document> listView;

{
    ...

    listView.setItems(items);
    listView.getStylesheets().add("style/listview.css");
    listView.setStyle("-fx-background-insets: 0 ;"); // remove 1px border of listview container

    listView.setCellFactory(new Callback<ListView<Document>, ListCell<Document>>() {
        @Override
        public ListCell<Document> call(ListView<Document> listView) {
            return new DocumentArrayAdapter();
        }
    });  

    ...
}

public void loadListView(List<Document> ldoc){
    if (!ldoc.isEmpty()){
        items.addAll(ldoc);
    }
}

4 个答案:

答案 0 :(得分:4)

这就是我所做的

public class CustomListCell extends ListCell<Document>{

private double lastYposition = 0;

public CustomListCell(){

    setOnMousePressed(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            lastYposition = event.getSceneY();
        }
    });

    setOnMouseDragged(new EventHandler<MouseEvent>() {
        @Override
        public void handle(MouseEvent event) {
            double newYposition = event.getSceneY();
            double diff = newYposition - lastYposition;
            lastYposition = newYposition;
            CustomScrollEvent cse = new CustomScrollEvent();
            cse.fireVerticalScroll((int)diff, DocumentArrayAdapter.this, (EventTarget) event.getSource());
        }
    });  

package myproject.utils;

import javafx.event.Event;
import javafx.event.EventTarget;
import javafx.scene.input.ScrollEvent;

public class CustomScrollEvent {

public void fireVerticalScroll(int deltaY, Object source, EventTarget target){

    ScrollEvent newScrollEvent = null;
    newScrollEvent = new ScrollEvent(source,
               target,
               ScrollEvent.SCROLL,
               0,
               0,
               0,
               0,
               false,
               false,
               false,
               false,
               false,
               false,
               0,
               deltaY,
               0,
               0,
               ScrollEvent.HorizontalTextScrollUnits.CHARACTERS,
               0,
               ScrollEvent.VerticalTextScrollUnits.NONE,
               deltaY,
               0,
               null);

        Event.fireEvent(target, newScrollEvent);
}
}

虽然我已经在自己的ListCell中实现了监听器,但我想它也可以直接在ListView上实现监听器,使用listView.setOnMousePressed和listView.setOnMouseDragged

答案 1 :(得分:0)

我找到了解决此问题的不同方法:

public class ScrollList {

protected double lastYposition = 0;
private double last_value = 0;
protected double firstYposition = 0;
protected ScrollBar scrollbar = null;
private int autoValue = 0;
private int maxScroll = 0;

public ScrollList(ListView list) {

list.setOnMousePressed(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {

        lastYposition = event.getSceneY();
        //Config.prints(lastYposition + "");

        firstYposition = lastYposition;

    }
});

Platform.runLater(new Runnable() {
    @Override
    public void run() {
        if (scrollbar == null) {
            for (Node node : list.lookupAll(".scroll-bar")) {
                if (node instanceof ScrollBar) {
                    ScrollBar bar = (ScrollBar) node;
                    if (bar.getOrientation().equals(Orientation.VERTICAL)) {
                        scrollbar = bar;
                    }
                }
            }

            scrollbar.setOnMouseReleased(new EventHandler<MouseEvent>() {
                @Override
                public void handle(MouseEvent event) {

                    lastYposition = event.getSceneY();

                    last_value = autoValue;

                }
            });

            /* //in case the list has already a scroll value, like when 
            //return to fontes list, it should has the last choosed fontes selected 
            IndexedCell first = flow.getFirstVisibleCellWithinViewPort();
            autoValue = first.getIndex();*/
            VirtualFlow flow = (VirtualFlow) list.lookup(".virtual-flow");
            last_value = autoValue;
            maxScroll = flow.getCellCount();

            flow.addEventFilter(Event.ANY, new EventHandler<Event>() {
                @Override
                public void handle(Event eventus) {
                    IndexedCell first = flow.getFirstVisibleCellWithinViewPort();
                    autoValue = first.getIndex();
                    Config.prints(last_value + "");
                }
            });
        }
    }
});

list.setOnMouseDragged(new EventHandler<MouseEvent>() {
    @Override
    public void handle(MouseEvent event) {

        double newYposition = event.getSceneY();

        //Config.prints(newYposition + "");
        double diff = newYposition - lastYposition;
        lastYposition = newYposition;

        if ((firstYposition - lastYposition) > Config.TOUCH_SCREEN_NOISE && (firstYposition - lastYposition) > 0 || (firstYposition - lastYposition) < (Config.TOUCH_SCREEN_NOISE * -1) && (firstYposition - lastYposition) < 0) {
            list.getSelectionModel().clearSelection();
        }

        if (diff > 0 && diff > Config.MIN_SCROLL_VELOCITY) {
            diff = Config.MIN_SCROLL_VELOCITY;
        } else if (diff < 0 && diff < Config.MIN_SCROLL_VELOCITY * -1) {
            diff = Config.MIN_SCROLL_VELOCITY * -1;
        }

        if (diff > 0 && diff > Config.MAX_SCROLL_VELOCITY) {
            diff = Config.MAX_SCROLL_VELOCITY;
        } else if (diff < 0 && diff < Config.MAX_SCROLL_VELOCITY * -1) {
            diff = Config.MAX_SCROLL_VELOCITY * -1;
        }

        //prints("diff=>" + diff + " || last_value=>" + last_value);
        last_value -= diff;
        //Config.prints("last_value=>" + last_value);
        if (last_value < 0) {
            last_value = 0;
        }else if(last_value > maxScroll){
            last_value = maxScroll;
        } 
        list.scrollTo((int) last_value);
    }
});

}

//if you want the scroll to start from a specific position

    public void setAutoValue(int index) {


       this.autoValue = index;
    }
  }
}

public static double MAX_SCROLL_VELOCITY = 1;

public static double MIN_SCROLL_VELOCITY = 0.5;

我将此代码放在一个对象“ScrollList”中并像这样使用它。

protected ArrayList<ScrollList> scrolls = new ArrayList<>();     
protected void setScroll(ListView list, int... index) {

   ScrollList scrollList = new ScrollList(list);

   if (index.length > 0) {
       scrollList.setAutoValue(index[0]);
   }

   scrolls.add(scrollList);

}

ArrayList是因为,在我的情况下,我有一些视图,我有一个列表视图。

GC

答案 2 :(得分:0)

这是另一种方法:

Contains(...)

将监听器添加到null时:

public class ScrollListener {

    private BooleanProperty scrolling = new ReadOnlyBooleanWrapper(false);

    private EventHandler<? super MouseEvent> dragDetectedFilter = e -> scrolling.set(true);

    private EventHandler<? super MouseEvent> mouseClickedFilter = evt -> {
        if (scrolling.get()) {
            scrolling.set(false);
            evt.consume();
        }
    };

    private EventHandler<? super MouseEvent> mouseExitedHandler = e -> scrolling.set(false);

    private Node observableNode;

    public ScrollListener(Node observableNode) {
        this.observableNode = observableNode;
    }

    public void enable() {
        observableNode.addEventFilter(MouseEvent.DRAG_DETECTED, dragDetectedFilter);
        observableNode.addEventFilter(MouseEvent.MOUSE_CLICKED, mouseClickedFilter);
        observableNode.addEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler);
    }

    public void disable() {
        observableNode.removeEventFilter(MouseEvent.DRAG_DETECTED, dragDetectedFilter);
        observableNode.removeEventFilter(MouseEvent.MOUSE_CLICKED, mouseClickedFilter);
        observableNode.removeEventHandler(MouseEvent.MOUSE_EXITED, mouseExitedHandler);
    }

    public ReadOnlyBooleanProperty scrollingProperty() {
        return scrolling;
    }

    public boolean isScrolling() {
        return scrolling.get();
    }
}
滚动列表时

ListView会被消耗

答案 3 :(得分:0)

这样做很简单:

import com.sun.javafx.scene.control.skin.VirtualFlow;
import com.sun.javafx.scene.control.skin.VirtualScrollBar;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import javafx.beans.InvalidationListener;
import javafx.beans.Observable;
import javafx.beans.property.IntegerProperty;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;

public class ListViewHelper
{
    public static VirtualFlow getVirtualFlow(ListView lv)
    {
        return (VirtualFlow) lv.lookup(".virtual-flow");
    }

    public static VirtualScrollBar getVbar(VirtualFlow vf)
    {
        try {
            final Method method = VirtualFlow.class.getDeclaredMethod("getVbar");
            method.setAccessible(true);
            return (VirtualScrollBar) method.invoke(vf);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException |
                InvocationTargetException ex) {
            throw new RuntimeException(ex);
        }
    }

    public static VirtualScrollBar getHbar(VirtualFlow vf)
    {
        try {
            final Method method = VirtualFlow.class.getDeclaredMethod("getHbar");
            method.setAccessible(true);
            return (VirtualScrollBar) method.invoke(vf);
        } catch (NoSuchMethodException | SecurityException | IllegalAccessException | IllegalArgumentException |
                InvocationTargetException ex) {
            throw new RuntimeException(ex);
        }
    }

}

只需调用getVbar(getVirtualFlow(listViewInstance)),您就会有一个滚动条。然后,您可以将侦听器添加到滚动条的valueProperty。