2012-03-15 68 views
13

Tôi có ứng dụng JavaFX 2.0, nơi tôi cần thực hiện một số hành động, sau khi người dùng nhấp vào một mục trong phần tử ListView. Để xây dựng giao diện người dùng i đang sử dụng FXML, trong đó tôi có một cái gì đó như thế này:Làm cách nào để xử lý mục ListView được nhấp?

 <children> 
      <ListView fx:id="listView" GridPane.columnIndex="0" 
      GridPane.rowIndex="1" labelFor="$pane" 
      onPropertyChange="#handleListViewAction"/> 
     </children> 

Và đây là những gì tôi có trong một điều khiển cho sự kiện này:

 @FXML protected void handleListViewAction(ActionEvent event) { 
      System.out.println("OK"); 
     } 

Và đây là một lỗi , tôi nhận được, khi hiện trường, mà là dành cho gui này được xây dựng:

javafx.fxml.LoadException: java.lang.String does not define a property model for "property". 
at javafx.fxml.FXMLLoader$Element.processEventHandlerAttributes(Unknown Source) 
at javafx.fxml.FXMLLoader$ValueElement.processEndElement(Unknown Source) 
at javafx.fxml.FXMLLoader.processEndElement(Unknown Source) 
at javafx.fxml.FXMLLoader.load(Unknown Source) 
at javafx.fxml.FXMLLoader.load(Unknown Source) 
at javafx.fxml.FXMLLoader.load(Unknown Source) 
at fxmlexample.FXMLExampleController.handleSubmitButtonAction(FXMLExampleController.java:49) 
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) 
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) 
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) 
at java.lang.reflect.Method.invoke(Method.java:601) 
at javafx.fxml.FXMLLoader$ControllerMethodEventHandler.handle(Unknown Source) 
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) 
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) 
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) 
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) 
at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) 
at javafx.event.Event.fireEvent(Unknown Source) 
at javafx.scene.Node.fireEvent(Unknown Source) 
at javafx.scene.control.Button.fire(Unknown Source) 
at com.sun.javafx.scene.control.behavior.ButtonBehavior.mouseReleased(Unknown Source) 
at com.sun.javafx.scene.control.skin.SkinBase$5.handle(Unknown Source) 
at com.sun.javafx.scene.control.skin.SkinBase$5.handle(Unknown Source) 
at com.sun.javafx.event.CompositeEventHandler.dispatchBubblingEvent(Unknown Source) 
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) 
at com.sun.javafx.event.EventHandlerManager.dispatchBubblingEvent(Unknown Source) 
at com.sun.javafx.event.CompositeEventDispatcher.dispatchBubblingEvent(Unknown Source) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.BasicEventDispatcher.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.EventDispatchChainImpl.dispatchEvent(Unknown Source) 
at com.sun.javafx.event.EventUtil.fireEventImpl(Unknown Source) 
at com.sun.javafx.event.EventUtil.fireEvent(Unknown Source) 
at javafx.event.Event.fireEvent(Unknown Source) 
at javafx.scene.Scene$MouseHandler.process(Unknown Source) 
at javafx.scene.Scene$MouseHandler.process(Unknown Source) 
at javafx.scene.Scene$MouseHandler.access$1300(Unknown Source) 
at javafx.scene.Scene.impl_processMouseEvent(Unknown Source) 
at javafx.scene.Scene$ScenePeerListener.mouseEvent(Unknown Source) 
at com.sun.javafx.tk.quantum.GlassViewEventHandler.handleMouseEvent(Unknown Source) 
at com.sun.glass.ui.View.handleMouseEvent(Unknown Source) 
at com.sun.glass.ui.View.notifyMouse(Unknown Source) 
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 
at com.sun.glass.ui.win.WinApplication.access$100(Unknown Source) 
at com.sun.glass.ui.win.WinApplication$2$1.run(Unknown Source) 
at java.lang.Thread.run(Thread.java:722) java.lang.NullPointerException 
at javafx.scene.Scene.doCSSPass(Unknown Source) 
at javafx.scene.Scene.access$2900(Unknown Source) 
at javafx.scene.Scene$ScenePulseListener.pulse(Unknown Source) 
at com.sun.javafx.tk.Toolkit.firePulse(Unknown Source) 
at com.sun.javafx.tk.quantum.QuantumToolkit.pulse(Unknown Source) 
at com.sun.javafx.tk.quantum.QuantumToolkit$8.run(Unknown Source) 
at com.sun.glass.ui.win.WinApplication._runLoop(Native Method) 
at com.sun.glass.ui.win.WinApplication.access$100(Unknown Source) 
at com.sun.glass.ui.win.WinApplication$2$1.run(Unknown Source) 
at java.lang.Thread.run(Thread.java:722) 

Và khối cuối cùng của ngoại lệ này (từ đây java.lang.NullPointerException) được looped.

Trả lời

26

Thuộc tính và giá trị FXML được ánh xạ trực tiếp tới API FX. Vì vậy, để tìm hiểu cách viết trình xử lý, trước tiên bạn có thể tạo các thực thể bắt buộc bằng API.

Dường như bạn muốn thêm hành động trên phần tử ListView khi nhấp chuột, vì vậy bạn cần thêm trình xử lý nhấp chuột. Trong API có vẻ cách tiếp theo:

final ListView lv = new ListView(FXCollections.observableList(Arrays.asList("one", "2", "3"))); 
    lv.setOnMouseClicked(new EventHandler<MouseEvent>() { 

     @Override 
     public void handle(MouseEvent event) { 
      System.out.println("clicked on " + lv.getSelectionModel().getSelectedItem()); 
     } 
    }); 

Nhìn vào mã mà bạn có thể tìm hiểu những gì trong FXML bạn cần phải thuộc tính overrided onMouseClicked:

<ListView fx:id="listView" onMouseClicked="#handleMouseClick"/> 

Và trong điều khiển bạn cần cung cấp xử lý với MouseEvent tham số :

@FXML 
private ListView listView; 

@FXML public void handleMouseClick(MouseEvent arg0) { 
    System.out.println("clicked on " + listView.getSelectionModel().getSelectedItem()); 
} 
+0

Xin chào. Nếu tôi có một EventHandler mà không bị ràng buộc với một bộ điều khiển nhưng có thể được sử dụng độc lập ở nhiều nơi trong ứng dụng của tôi. Có thể gán EventHandler trực tiếp trong fxml thay vì một phương thức từ bộ điều khiển không? –

+0

Đây chính xác là những gì tôi đang tìm kiếm! Cảm ơn! – Maslor

+0

Tôi thực sự không chắc chắn làm thế nào để sử dụng điều này bên ngoài phương pháp bắt đầu của tôi. Làm thế nào tôi có thể gọi cho ownerStage từ bên trong một Controller chẳng hạn? – santafebound

7

câu trả lời từ @Sergey Grinev có thể gặp sự cố. nếu ListView của bạn được lấp đầy không đủ, nói cách khác có thể có một số không gian trống. thì bạn đã nhập không gian trống sẽ không kích hoạt lựa chọn thay đổi. enter image description here

giải pháp: Sử dụng tùy chỉnh ListCell.

  1. xác định lớp tùy chỉnh mở rộng ListCell.
  2. trong hàm updateItem, bạn có thể đặt trình xử lý sự kiện nhấp vào mục thành nút gốc của ô.

Nếu bạn không biết bạn có bao nhiêu mục. bạn sẽ không bao giờ chỉ làm như vậy. bạn nên sử dụng thời gian cho không gian.

Demostrate:

trong đoạn mã ListView:

listView.setCellFactory(new AppListCellFactory()); 

AppListCellFactory.java:

 
public class AppListCellFactory implements Callback, ListCell> {

// only one global event handler private EventHandler<MouseEvent> oneClickHandler; public AppListCellFactory(){ oneClickHandler = new EventHandler<MouseEvent>() { @Override public void handle(MouseEvent event) { Parent p = (Parent) event.getSource(); // do what you want to do with data. AppClickHandler.onAppBeanClicked((AppBean) p.getUserData()); } }; } @Override public ListCell<AppBean> call(ListView<AppBean> param) { return new DemoListCell(oneClickHandler); } public static final class DemoListCell extends ListCell<AppBean> { private EventHandler<MouseEvent> clickHandler; /** * This is ListView item root node. */ private Parent itemRoot; private Label label_AppName; private ImageView imgv_AppIcon; DemoListCell(EventHandler<MouseEvent> clickHandler) { this.clickHandler = clickHandler; } @Override protected void updateItem(AppBean app, boolean empty) { super.updateItem(app, empty); if (app == null) { setText(null); setGraphic(null); return; } if (null == itemRoot) { try { itemRoot = FXMLLoader.load(getClass().getResource(("fxml/appList_item.fxml"))); } catch (IOException e) { throw new RuntimeException(e); } label_AppName = (Label) itemRoot.lookup("#item_Label_AppName"); imgv_AppIcon = (ImageView) itemRoot.lookup("#item_ImageView_AppIcon"); itemRoot.setOnMouseClicked(clickHandler); } // set user data. like android's setTag(Object). itemRoot.setUserData(app); label_AppName.setText(app.name); imgv_AppIcon.setImage(new Image(getClass().getResource("img/icon_64.png").toExternalForm())); setGraphic(itemRoot); } }

}

+7

Và sau đó họ ngạc nhiên vì sao mọi người ghét Java ... – shinzou

2

Cách khắc phục đơn giản đối với câu trả lời của Sergey, mà không cần phải tạo một listCell hoàn toàn mới (trong trường hợp của bạn, nhưng chúng tôi không phải làm trong trường hợp văn bản thông thường).

tất cả những gì bạn cần làm là tạo biến tạm thời bằng với mục danh sách đầu tiên lúc đầu và biến được thay đổi thành từng mục được nhấp mới. Một khi bạn cố gắng bấm vào mục một lần nữa, biến tạm thời biết nó giống nhau, và với một câu lệnh if bạn có thể làm được điều đó.

tạm Item là một thế giới mà bạn đặt lên hàng đầu String tempItem = "admin";

cho tôi, tôi biết trường đầu tiên của tôi sẽ luôn luôn được dán nhãn "admin" vì vậy tôi thiết lập nó như vậy. bạn sẽ phải lấy mục nhập đầu tiên và đặt nó bên ngoài phương thức bạn sẽ sử dụng để chọn danh sách.

private void selUser() throws IOException 
    { 
    String item = userList.getSelectionModel().getSelectedItem().toString(); 

     if(item != tempItem) 
     { 
      //click, do something 
     } 

     tempItem = item;  
} 

Đối với tôi, tôi đã sử dụng một tài liệu FXML đó gọi là phương pháp của tôi

@FXML 
public void userSel(MouseEvent mouse) throws IOException 
{ 
selUser(); 

}

Trong trường hợp này bạn chỉ có thể mất toàn bộ nội dung của selUser() phương pháp và đặt nó vào click chuột userSel.

1

Bạn cũng có thể sử dụng các sự kiện không mong muốn trên không gian trống.

trong call() của nhà máy di động add:

cell.setOnMousePressed((MouseEvent event) -> { 
    if (cell.isEmpty()) { 
     event.consume(); 
    } 
}); 

này sẽ vô hiệu hóa click & dragStart quá.

Tổng số các ví dụ cellFactory từ dự án của tôi:

public static Callback<ListView<BlockFactory>, ListCell<BlockFactory>> getCellFactory() { 
    return new Callback<ListView<BlockFactory>, ListCell<BlockFactory>>() { 

     @Override 
     public ListCell<BlockFactory> call(ListView<BlockFactory> param) { 
      ListCell<BlockFactory> cell = new ListCell<BlockFactory>() { 

       @Override 
       protected void updateItem(BlockFactory item, boolean empty) { 
        super.updateItem(item, empty); 
        if (item != null) { 
         ImageView img = new ImageView(item.img); 
         Label label = new Label(item.desc); 
         HBox bar = new HBox(img, label); 
         bar.setSpacing(15); 
         bar.setAlignment(Pos.CENTER_LEFT); 
         setGraphic(bar); 
        } 
       } 
      }; 
      cell.setOnMousePressed((MouseEvent event) -> { 
       if (cell.isEmpty()) { 
        event.consume(); 
       } 
      }); 
      return cell; 
     } 
    }; 
} 
Các vấn đề liên quan