2016-11-15 7 views
0

私はデモのために、以下の基本的なGUIがあります。JavaFX - ユーザーがあるノードから別のノードにマウスをドラッグしたときに通知しますか?

enter image description here

を、私は、以下の機能を実現しようとしているが、私は私が試みられてきたすべての道を使い果たしました。

ユーザーは、ImageViewの者のいずれかをクリックして、ユーザーがのが マウスボタンを放すまで、周りのユーザーのカーソルを次の 矢印を作成します左にすることができます。プログラムはUser just clicked from R to Bを印刷し 、ユーザーはレッド ImageViewをクリックし、ブルーImageViewの上にドラッグしてから行かせた場合(矢印は、Xを起動し、Yは、彼がクリックされたと終了のxところ、yは彼のマウスが現在あるところ です)

ユーザーはレッドImageViewをクリックし、マウスを手放すが、異なるImageView上 なかった場合、プログラムはUser just clicked from R but did not target a different ImageViewを印刷します。

ImageViewをクリックすると矢印が表示され、2番目が消えるとマウスが消えます。

import javafx.application.Application; 
import javafx.scene.Scene; 
import javafx.scene.image.Image; 
import javafx.scene.image.ImageView; 
import javafx.scene.layout.Pane; 
import javafx.scene.shape.Line; 
import javafx.stage.Stage; 

import java.util.HashMap; 

public class Test extends Application 
{ 
    public static int HEIGHT = 500, WIDTH = 600; 

    @Override 
    public void start(Stage primaryStage) throws Exception 
    { 
     ImageView blue = new ImageView(new Image("blue.png")), 
       red = new ImageView(new Image("red.png")), 
       dark = new ImageView(new Image("dark.png")); 

     // Final array as to bypass the `final` requirement of event handler inner classes. 
     final ImageView[] hoveredOver = new ImageView[1]; 
     final Line[] linePtr = new Line[1]; 
     linePtr[0] = new Line(); 
     linePtr[0].setStrokeWidth(10); 

     HashMap<ImageView, Character> lookup = new HashMap<ImageView, Character>(3) 
     {{ 
       put(blue, 'B'); 
       put(red, 'R'); 
       put(dark, 'D'); 
      }}; 

     for (ImageView i : new ImageView[] { blue, red, dark }) 
     { 
      i.setFitWidth(150); 
      i.setFitHeight(150); 

      // Set the anchor points of the click and display the arrow. 
      i.setOnMousePressed(e -> { 
       linePtr[0].setStartX(e.getX()); 
       linePtr[0].setStartY(e.getY()); 
       linePtr[0].setVisible(true); 
      }); 
      // Move the arrow as the mouse moves. 
      i.setOnMouseDragged(e -> { 
       linePtr[0].setEndX(e.getX()); 
       linePtr[0].setEndY(e.getY()); 
      }); 
      i.setOnMouseReleased(e -> { 
       // Not null means that the user WAS actually just now hovering over an imageview. 
       if (hoveredOver[0] != null) 
        System.out.printf("The user clicked from %c to %c!\n", lookup.get(i), lookup.get(hoveredOver[0])); 
        // Null means the user is not over an ImageView. 
       else 
        System.out.printf("The user initially clicked %c but did not drag to another Imageview.\n", lookup.get(i)); 
       linePtr[0].setVisible(false); 
      }); 
      // If the user enters ANY of the ImageViews, 
      // Set a variable so that the drag release listener 
      // can know about it! 
      i.setOnMouseDragOver(e -> hoveredOver[0] = i); 
      i.setOnMouseDragExited(e -> hoveredOver[0] = null); 
     } 

     blue.setX(400); 
     blue.setY(250); 

     red.setY(300); 
     red.setX(50); 

     /* 
     In this example I'm using a Pane but in my real program 
     I might be using a VBOX HBOX etc where I cannot freely move stuff around as I'd like. 
     This makes things extremely difficult and without using a 'Pane' 
     I don't know how this can even be done. Suggestions? 
     */ 
     Pane pneRoot = new Pane(blue, red, dark, linePtr[0]); 
     Scene scene = new Scene(pneRoot, WIDTH, HEIGHT); 
     primaryStage.setScene(scene); 
     primaryStage.show(); 
    } 

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

これが私の最高の試みであったと言っても近くにありません。それは行(矢印ではなく、理想的には私が矢印を曲線のように動かすように、this example image from a popular video gameのように動かす)が、私のニーズに合っていません。しかし、ImageViewを「ドラッグ」している間は、それを検出できません。

これを行うより良い方法はありますか?私は、私はそれ以上にダウンしているコードを単純にすることはできないように感じる。MUST別の方法がある。

答えて

2
  1. Javaはオブジェクト指向言語です。基本的な考え方は、モデリングするデータを表すクラスを作成し、そのクラスからオブジェクトを作成することです。あなたが物事を見るために任意の地図と一緒に物事を結びつけていて、明白な理由で配列が蹴られているなら、あなたは間違った場所で始まっています。
  2. JavaFXには、観察可能なシステムpropertiesがあります。これらは変更可能な方法でオブジェクトをラップし、変更に対応できるように観察することができます。
  3. MouseEventsMouseDragEventsのドキュメントをよく読んで理解してください。ドラッグを扱うための3つの異なるモードがあります。マウスドラッグ中にドラッグが開始されたノード以外のノードに送信されるイベント(マウスドラッグイベント)については、完全に「ドラッグドラッグ解除ジェスチャー」モードにする必要があります。 dragDetectedイベントに応答するときにノード上でstartFullDrag()を呼び出して、このモードをアクティブにすることができます。

私はその後、あなたのようなことを行うことができます

public class NamedDragAwareImageView { 

    private final ObjectProperty<NamedDragAwareImageView> source ; 
    private final ObjectProperty<NamedDragAwareImageView> destination ; 
    private final String name ; 
    private final ImageView imageView ; 

    public NamedDragAwareImageView(ObjectProperty<NamedDragAwareImageView> source, 
     ObjectProperty<NamedDragAwareImageView> destination, 
     String name, String resource) { 

     this.source = source ; 
     this.destination = destination ; 
     this.name = name ; 
     this.imageView = new ImageView(new Image(resource)); 

     imageView.setOnDragDetected(e -> { 
      source.set(this); 
      destination.set(null); 
      imageView.startFullDrag(); 
     }); 

     imageView.setOnMouseDragReleased(e -> { 
      if (source.get() != null && source.get() != this) { 
       destination.set(this); 
      } 
     }); 

     // other image view config... 

    } 

    public ImageView getView() { 
     return imageView ; 
    } 

    public String getName() { 
     return name ; 
    } 

} 

のようなもので始まります:

// observable properties to represent start and end nodes for drag: 
ObjectProperty<NamedDragAwareImageView> source = new SimpleObjectProperty<>(); 
ObjectProperty<NamedDragAwareImageView> destination = new SimpleObjectProperty<>(); 


Pane root = new Pane(); 
// create your named image views, referencing the source and destination 
// and add their image views to root, e.g. 
NamedDragAwareImageView red = new NamedDragAwareImageView(source, destination, "Red", "red.png"); 
root.getChildren().add(red.getView()); 

// recommend using SVG paths (i.e. javafx.scene.shape.Path) for the arrow 
// easy to draw programmatically, easy to manipulate elements etc: 
Path arrowHead = new Path(); 
MoveTo arrowHeadStart = new MoveTo(); 
arrowHead.getElements().add(arrowHeadStart); 
arrowHead.getElements().addAll(/* draw an arrow head with relative path elements... */); 
arrowHead.setVisible(false); 
// avoid arrowHead interfering with dragging: 
arrowHead.setMouseTransparent(true); 

// this will contain a MoveTo and a bunch of LineTo to follow the mouse: 
Path arrowLine = new Path(); 
arrowLine.setMouseTransparent(true); 

root.getChildren().addAll(arrowHead, arrowLine); 

// change listener for source. source is set when drag starts: 
source.addListener((obs, oldSource, newSource) -> { 
    if (newSource == null) return ; 
    arrowHeadStart.setX(/* x coord based on newSource */); 
    arrowHeadStart.setY(/* similarly */); 
    arrowHead.setVisible(true); 
}); 

// change listener for destination. destination is only set 
// when drag complete: 
destination.addListener((obs, oldDestination, newDestination) -> { 
    if (newDestination != null) { 
     System.out.println("User dragged from "+source.get().getName()+ 
      " to "+destination.get().getName()); 
    } 
}); 

root.setOnMouseDragOver(e -> { 
    if (source.get()==null && destination.get()!=null) { 
     // update arrowStart position 
     // add line element to arrowLine 
    } 
}); 

root.setOnMouseReleased(e -> { 
    // clear arrow: 
    arrowHead.setVisible(false); 
    arrowLine.getElements().clear(); 
}); 
+0

それはスキルの天井がJavaであるか高で私の心を吹きます。私とこの記事の検索を終了するかもしれない他の人のためにこのコードブロックにコメントを追加できますか?例えば、 'Path'と' MooveTo'は何ですか?ここでは何が使われていますか? 'Source.addListener((obs、oldSource、newSource)'は 'ChangeListener'ですか?' Source'のポインタが 'NamedDrag ... 'によって変更されると、そのリスナーがトリガされますか?私の 'Pane'は' Pane'ではなく 'GridPane'や' VBox'などですか?デフォルトで 'NamedDrag ...'を呼びますが、何もありません?ありがとうございます – Hatefiend

+0

@Hatefiend私が知る限り、管理されたレイアウトの唯一の違いは、 'arrowHead'と' arrowLine'に対して 'setManaged(false)'を呼び出さなければならないということです。 –

関連する問題