How do I get the ImageView contained inside a Pane? - java

Situation
I have an application which contains a GridPane thats composed of multiple Panes. Inside of every Pane is an ImageView that allows me to display an Image inside that Pane. My application follows the MVC pattern and I would like my setPaneImage( ) function to dynamically change one Pane's ImageView image.
Problem
I don't know how to access the Pane's ImageView ? I know you can use the ImageView's setImage( ) to change its image. To access the ImageView contained inside the Pane, I suppose you have to use the .getChildren( ) method but I can't figure out how I can implement this...
Here's the simplified code :
public class ViewController extends Application implements EventHandler
{
final Image paneVide = new Image( "sample/img/paneVide.png" );
#Override
public void start(Stage primaryStage) throws Exception
{
// Initializing attributes
BorderPane borderPane = new BorderPane( );
gridPane = new GridPane( );
Pane p = new Pane( );
// Adding ImageView to the Pane
ImageView imv = new ImageView( );
imv.setPreserveRatio( true );
imv.setFitHeight( p.getHeight() );
imv.setFitWidth( p.getWidth() );
p.getChildren().add( imv );
// Setting handler
p.setOnMouseEntered( this );
// Adding to GridPane
gridPane.add( p, i, j );
// [...] Rest of the display code
}
#Override
public void handle(Event event)
{
Pane p;
if( event.getSource() instanceof Pane ) {
p = (Pane) event.getSource();
// MOUSE HOVER
if( event.getEventType().toString() == "MOUSE_ENTERED" )
setPaneImage( p, "VIDE" );
}
}
// Here's the function where I don't really know what to do...
// How do I access the Pane's ImageView so that I can dynamically
// change its image ??
public void setPaneImage( Pane p, String img )
{
switch( img )
{
case "VIDE":
p.getChildren().add( new ImageView(paneVide) ); // Setting pane's image
break;
}
}
}
Thank you very much !

The simplest way would be to store a reference to the ImageView in a field:
private ImageView imv;
public void start(Stage primaryStage) throws Exception {
...
this.imv = new ImageView();
...
}
public void setPaneImage(Pane p, String img) {
...
imv.setImage(paneVide);
...
}
Assuming the ImageView is the only child of the Pane, you could also retrieve the ImageView from the child list:
public void setPaneImage(Pane p, String img) {
ImageView imv = (ImageView) p.getChildren().get(0);
...
}
Additional notes:
Some parts of your code could be considered bad practice:
if(event.getEventType().toString() == "MOUSE_ENTERED")
You're comparing Strings using == instead of equals
Instead of comparing the type converted to String you should compare to the event type: event.getEventType() == MouseEvent.MOUSE_ENTERED
You don't need that check. It's the only event the handler listens to. Handling different types of events should be done using a different handler. You could create such a handler using anonymus classes or lambda expressions easily. You could avoid the source check using the same strategy.
public void setPaneImage(Pane p, String img) {
switch( img )
{
case "VIDE":
Using magic strings is a bad idea. It's hard to find the error if you've got a typo in the string. You could simply just pass the Image itself (paneVide). This way you don't need the switch at all and any typos would become obvious on compilation.
imv.setFitHeight(p.getHeight());
imv.setFitWidth(p.getWidth());
You do this before the first layout pass. At that time the size of p is still 0. You should use bindings instead to accommodate for later updates of the properties:
imv.fitHeightProperty.bind(p.heightProperty());
imv.fitWidthProperty.bind(p.widthProperty());
imv.setManaged(false); // make sure p can shrink
It would also be beneficial to make sure the GridPane resizes p.
public class ViewController extends Application implements EventHandler
The Application class is the entry point to the program. Using it as controller and event handler violates the seperation of concerns principle. Also it's preferable to specify the type parameter for EventHandler.

Related

Stack<Canvas> in a JavaFX FXML document

I'm creating an image manipulation program in JavaFX using FXML to organize the UI. It currently supports things like loading images, drawing, and saving those changes.
I do not know how/cannot find a way to represent a Stack of Canvas' in the FXML document. My goal is to have a stack of Canvas' that will allow me to undo the changes the user has made by simply clearing the top layer in the stack of canvas'. The idea is that each edit would reside on it's own canvas.
Below is some code from the FXML document. Here is the center pane of my border pane. This is the center of the image manipulating program. Inside of it I have a stack pane so that I can overlay things. The two comments are what I would expect to be able to do but those attempts do not work.
<!-- center pane -->
<center>
<StackPane>
<Canvas fx:id="currCanvas" />
<!-- <Canvas fx:id="canvasStack" /> -->
<!-- <Stack fx:id="canvasStack" /> -->
<Canvas fx:id="previewCanvas" />
</StackPane>
</center>
If I was going to implement this without the FXML document it would be much simpler but it would be more difficult to organize the UI. My confusion is that I do not know how to accomplish this using FXML.
Thanks
All you really need is one Canvas but whenever an edit is made, you should make a snapshot of the canvas or make a copy of the pixels in the canvas then push that array of pixels or object containing the pixel to a Stack. That would be a bad idea because making a copy of the canvas will be very expensive both computationally and memory wise. While feasible, I would advise against it.
To me the best and easiest way to deal with "undo operations" is to draw first on a BufferedImage then draw that image on the Canvas as explained here and here and second to introduce the concept of Action in your application. For instance a "paint action" would look something like:
interface Action
{
//Used to re-apply action after it was undone
public void apply();
//Undo the action
public void undo();
}
class PaintAction() implements Action
{
static class Pixel
{
final int x;
final int y;
final int oldColor;
final int newColor;
PixelPos(int x, int y, int oldColor, int newColor)
{
this.x = x;
this.y = y;
this.oldColor = oldColor;
this.newColor = newColor;
}
}
List<Pixel> affectedPixels;
PaintAction()
{
affectedPixels = new ArrayList<>();
}
#Override
public void apply(Canvas canvas)
{
for (Pixel pixel : pixel)
{
//draw new pixel's color on the canvas
}
}
#Override
public void undo(Canvas canvas)
{
for (Pixel pixel : pixel)
{
//draw old pixel's color on the canvas
}
}
public void addPixel(Pixel pixel)
{
affectedPixels.add(Pixel);
}
}
So when the user presses the mouse button to start painting, you create a new PaintAction then whenever the user moves the mouse, you would create a new Pixel object and add it to the list of "affected pixels by the PaintAction" then proceed to change the color of the pixel in the BufferedImage.
Then all you would need will be to keep a stack of Action and apply, undo them as you see fit.
Hope that make sense, cheers.
Simply use a Pane. This way there are no issues with aligning the children. Panes don't provide a Stack of children, but a List can be used for stack operation too, although the removing the last item from the list is a bit more difficult than for a stack, but simple enough.
The following code simply adds circles and rectangles, but you could replace this with adding Canvases instead:
#Override
public void start(Stage primaryStage) {
Button doBtn = new Button("draw");
Button undo = new Button("undo");
undo.setDisable(true);
Pane stack = new Pane();
stack.setPrefSize(400, 400);
VBox root = new VBox(new HBox(doBtn, undo), stack);
doBtn.setOnAction(new EventHandler<ActionEvent>() {
private int count = 0;
#Override
public void handle(ActionEvent event) {
// create & add some node
Node addedNode;
switch (count % 2) {
case 0:
addedNode = new Circle(count * 5 + 5, count * 5 + 5, 5, Color.BLUE);
break;
case 1:
Rectangle rect = new Rectangle(count * 5, 390 - count * 5, 10, 10);
rect.setFill(Color.RED);
addedNode = rect;
break;
default:
return;
}
stack.getChildren().add(addedNode);
undo.setDisable(false);
count++;
}
});
undo.setOnAction(evt -> {
// remove last child
List<Node> children = stack.getChildren();
children.remove(children.size() - 1);
// check, if undo button needs to be disabled
undo.setDisable(children.isEmpty());
});
Scene scene = new Scene(root);
primaryStage.setScene(scene);
primaryStage.show();
}
Note that I wouldn't recommend creating new Canvases for every operation though, since this could lead to memory issues pretty fast...

JavaFX - Notify when user drags mouse from one Node to another?

I have the following basic GUI for demonstration:
I'm trying to achieve the following functionality but I've exhausted all avenues that I've attempted.
User can left click on any of the ImageView's and it will create an
arrow that follows the user's cursor around until the user let's go of
the mouse button. (arrow start x,y is where he clicked and end x,y is
where his mouse currently is) If the user clicked on the Red
ImageView and dragged it over the Blue ImageView and then let go,
the program would print User just clicked from R to B
If the user clicked on the Red ImageView and let go of the mouse but
was not over a different ImageView, the program would print User
just clicked from R but did not target a different ImageView.
Under all circumstances, the arrow will appear when the user clicks on
the ImageView and will disappear the second he lets go of the mouse.
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 was my best attempt and it's not even close. It moves a line (not an arrow, and ideally I want my arrow to curve as it moves much like this example image from a popular video game) but does not suit my needs. It cannot detect however when I let go while 'dragging over' an ImageView.
Is there a better way to do this? I feel like I can't simply the code I have down any further but there MUST be another way.
Java is an object-oriented language. The basic idea is that you create classes to represent the data you are modeling and then create objects from those classes. If you are tying things together with arbitrary maps to look things up, and arrays kicking around for no apparent reason, you are starting in the wrong place.
JavaFX has a system of observable properties. These wrap objects in a mutable way and can be observed so you can respond to changes.
Make sure you read and understand the documentation on MouseEvents and MouseDragEvents. There are three different modes for handling dragging. For events (mouse drag events) to be sent to nodes other than the one on which the drag was initiated during a mouse drag, you need to be in full "press-drag-release gesture" mode. You can activate this mode by calling startFullDrag() on the node when responding to a dragDetected event.
I would start with something like
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 ;
}
}
Then you can do things like:
// 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();
});

Pilling two obj with OnMousePressed on top of each other in javafx

Been trying around and searching but couldn't find any solution, so I finally decided to give up and ask further...
Creating a javafx app, I load tiles in a TilePane.
This tiles are clickable and lead to a details page of their respective content.
On each tile, if they do belong to a certain pack, I do display the pack name, that is also clickable and lead to a page showing that specific pack content.
So that means the container, the tile, that is a Pane is clickable and on top of it I have a Label that is claickable also. What happens is when I do click the Label, it also triggers the Pane onMousePressed()... Here is a part of the tile creation code, the part focused on the onMousePressed(). I tried to make the Pane react by double click and the Label by single, it works, but I want to Pane to open with a single click.
I would be more than thankfull for any ideas how to solve that.
public DownloadTile (Downloadable upload, MainApp mainApp) {
_mainApp = mainApp;
_upload = upload;
_tile = new Pane();
_tile.setPrefHeight(100);
_tile.setPrefWidth(296);
_tile.setStyle("-fx-background-color: #ffffff;");
_tile.setCursor(Cursor.HAND);
}
public void refresh() {
_tile.getChildren().clear();
_tile.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isPrimaryButtonDown() /*&& event.getClickCount() == 2*/) {
_mainApp.showDownloadDialog(dt, _upload);
}
}
});
if (_upload.getPack() != null) {
Label pack = new Label();
pack.setText(_upload.getPack());
pack.getStyleClass().add("pack-link");
pack.setCursor(Cursor.HAND);
pack.relocate(10, 48);
_tile.getChildren().add(pack);
pack.setOnMousePressed(new EventHandler<MouseEvent>() {
#Override
public void handle(MouseEvent event) {
if (event.isPrimaryButtonDown()) {
_mainApp.showPackPage(_upload);
}
}
});
}
}
Your label will receive the mouseclick first (since it's on top), so after you have processed the click, you can stop it from being passed down the chain using 'consume':
pane.setOnMouseClicked(
(Event event) -> {
// process your click here
System.out.println("Panel clicked");
pane.requestFocus();
event.consume();
};

Java / JavaFX getting around final or effectively final, is this a good approach?

this feels like I am cheating or doing something wrong. I am a Java student working on a simple JavaFX project.
As I loop through and create buttons in a flowPane, I was having trouble using the loop counter i inside an inner class. It's the part where I assign event handlers. I have dealt with this issue before, I get the difference between "final" and "effectively final" so I don't believe I am asking that.
It's just that creating this copy of i by using "int thisI = i" just feels wrong, design-wise. Is there not a better way to do this? I looked into lambdas and they also have the "final or effectively final" requirement.
Here's my code, any level or criticism or suggestion for improvement is welcome, thanks!
private FlowPane addFlowPaneCenter() {
FlowPane flow = new FlowPane();
flow.setPadding(new Insets(0, 0, 0, 0));
flow.setVgap(0);
flow.setHgap(0);
flow.setPrefWrapLength(WIDTH_OF_CENTER); // width of function buttons
Button centerButtons[] = new Button[NUM_BUTTONS];
ImageView centerImages[] = new ImageView[NUM_BUTTONS];
for (int i=0; i < NUM_BUTTONS; i++) {
centerImages[i] = new ImageView(
new Image(Calculator.class.getResourceAsStream(
"images/button-"+(i)+".png")));
centerButtons[i] = new Button();
centerButtons[i].setGraphic(centerImages[i]);
centerButtons[i].setPadding(Insets.EMPTY);
centerButtons[i].setId("button-"+(i));
flow.getChildren().add(centerButtons[i]);
// add a drop shadow on mouseenter
DropShadow shadow = new DropShadow();
// ***** here's the workaround is this really a good approach
// to use this in the inner class instead of i? thanks *****
int thisI = i;
// set event handlers for click, mousein, mouseout
centerButtons[i].setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
// change graphic of button to down graphic
ImageView downImage = new ImageView(new
Image(Calculator.class.getResourceAsStream(
"images/button-"+(thisI)+"D.png")));
// call function to effect button press
System.out.println("Button click");
// change graphic back
centerButtons[thisI].setGraphic(centerImages[thisI]);
}});
centerButtons[i].addEventHandler(MouseEvent.MOUSE_ENTERED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
centerButtons[thisI].setEffect(shadow);
}
});
centerButtons[i].addEventHandler(MouseEvent.MOUSE_EXITED,
new EventHandler<MouseEvent>() {
#Override public void handle(MouseEvent e) {
centerButtons[thisI].setEffect(null);
}
});
}
return flow;
}
You can remove the arrays centerButtons and centerImages completely. Instead create local variables for the image and the button within the loop and use those, e.g.
final ImageView image = new ImageView(...);
final Button button = new Button();
button.setGraphic(centerImages[i]);
...
You can use the local variables in your eventhandlers, e.g.
button.setOnAction(new EventHandler<ActionEvent>() {
#Override public void handle(ActionEvent e) {
...
// change graphic back
button.setGraphic(image);
}});
Two minor improvements I noticed:
Try to avoid creating an Image more than once, because every time you create an Image, the actual data will be loaded again. Your handler will create a new Image for each click. I usually create Images in static final fields.
Event handlers are a nice opportunity to practice lambda expressions. :)

Drag and Drop in Java

I have been searching the internet to find examples or how to use java's drag and drop. I have found a few, but all the examples only allow you to drag into a specific location, i.e another list box or text area.
I want to know if there is a way to drag items onto a jpanel or similar container, having the item put anywhere freely on the container.
As long as the target is a supported drop target for the item you are dragging then you can drop it to containers like JPanel.
You control the way the dragged item is displayed at the drop location. If your panel overrides paintComponent() then you can paint the item however you find appropriate.
'is a way to drag items into a jpanel'
You can set a DropTarget to your JPanel.
public class MyDropTarget extends JPanel implements DropTargetListener {
private MyImage image;
private String text;
public MyDropTarget() {
setBackground(new Color(30,60,10));
this.setBorder( BorderFactory.createBevelBorder( BevelBorder.LOWERED, new Color(30,60,10).brighter(), new Color(30,60,10).darker() ) );
DropTarget dt = new DropTarget(this, DnDConstants.ACTION_COPY_OR_MOVE, this, true, null);
this.setDropTarget( dt );
}
#Override
public void paintComponent( Graphics g ) {
super.paintComponent( g );
if( image != null && image.getImage() != null ) {
g.drawImage( image.getImage(), 0, 0, null );
if(image.isError()){
g.setColor(Color.BLACK);
g.drawString( text, 0, 0 );
}
}
}
public void dragEnter( DropTargetDragEvent dtde ) {
this.setBorder( BorderFactory.createBevelBorder( BevelBorder.RAISED, Color.RED.brighter(), Color.RED.darker() ) );
}
public void dragExit( DropTargetEvent dte ) {
this.setBorder( BorderFactory.createBevelBorder( BevelBorder.LOWERED, UIManager.getColor( "MenuBar.highlight" ), UIManager.getColor( "MenuBar.shadow" ) ) );
}
public void dragOver( DropTargetDragEvent dtde ) {
}
public void drop( DropTargetDropEvent dtde ) {
try {
text = (String) dtde.getTransferable().getTransferData( DataFlavor.stringFlavor );
image = (MyImage)dtde.getTransferable().getTransferData( DataFlavor.imageFlavor );
this.setBorder( BorderFactory.createBevelBorder( BevelBorder.LOWERED, new Color(30,60,10).brighter(), new Color(30,60,10).darker() ) );
dtde.dropComplete( true );
repaint();
} catch( UnsupportedFlavorException e ) {
e.printStackTrace();
} catch( IOException e ) {
e.printStackTrace();
}
this.setDropTarget( null );
}
public void dropActionChanged( DropTargetDragEvent dtde ) {
}
}
I implemented Drag&Drop in this way:
The quite convenient mechanism for the implementation of Drag&Drop has appeared in Java 6, but it does have its disadvantages. For example, you should explicitly specify a Drop Target, what is not very useful when you need to lay down the object near the Drop Target. Also in the standard implementation there is no guarantee of execution order of listeners’ methods. I'll tell you the concept of implementing a more extensible Drag&Drop.
Initially the mouse listeners (Mouse Listener and MouseMotionListener) should be assigned to all Drag Sources. It’s need to implement 3 methods: a method of mouse click on the object, a method of moving the mouse while holding the mouse button on the object (mouseDragged in MouseMotionListener) and the mouse up method.
The listeners assignment looks as follows:
component.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
//block click right mouse button
if (MouseEvent.BUTTON1 == e.getButton()) {
startDrag(e);
}
}
#Override
public void mouseReleased(MouseEvent e) {
//block click right mouse button
if (MouseEvent.BUTTON1 == e.getButton()) {
endDrag(e);
}
}
});
component.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
drag(e);
}
});
Accordingly when you click on the object Drag&Drop starts, when you move the mouse the object should be moved, when you release the mouse the object should change its position and be moved to a new container. If the object will be moved in the frame of one container, then it is possible to implement only mouseDragged () method, in which the coordinates of the dragged object will be changed:
#Override
public void mouseDragged(MouseEvent e) {
Point mouseLocation = e.getLocationOnScreen();
Component draggedComponent = (Component) e.getSource();
SwingUtilities.convertPointFromScreen(mouseLocation,
draggedComponent.getParent());
draggedComponent.setLocation(mouseLocation);
}
But dragged object coordinates can be set relative to the container in which it is located. Accordingly, when the mouse is moved to another container it is necessary to add a component to a new container and to calculate the new coordinates, etc. This method is not very beautiful and extensible, so I suggest using GlassPane to display the dragged object.
The algorithm looks as follows:
Click on the object.
Get a screenshot of the object (see how to make a screenshot).
Hide the original object.
Draw on glassPane a screenshot of the object, based on the
coordinates of the mouse.
When you move the mouse you need to redraw a screenshot according
to the new coordinates.
When you release the mouse you need to place the object on the
container under which the cursor is located.
Display the original object.
With this approach, we have no any dependences on the container on which the cursor should be placed to make Drop and correspondingly the object can be "Dropped" anywhere.
GlassPane with transparency effect:
public class GhostGlassPane extends JPanel {
private final AlphaComposite composite;
private BufferedImage ghostImage = null;
private Point location = new Point(0, 0);
public GhostGlassPane() {
setOpaque(false);
composite = AlphaComposite.getInstance(AlphaComposite.
SRC_OVER, 0.7f);
}
public void paintComponent(Graphics g) {
if (ghostImage == null)
return;
Graphics2D g2 = (Graphics2D) g;
g2.setComposite(composite);
g2.drawImage(ghostImage, (int) (location.getX()),
(int) (location.getY()), null);
}
}
In this response only concept of implementation is given.
This information is taken from my article: Frequently Asked Questions during Java applet development

Categories