我想用手势实现一个可滚动的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);
}
}
答案 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。