Geertjan is a DZone Zone Leader and has posted 457 posts at DZone. You can read more from them at their website. View Full User Profile

How to Add Resize Functionality to Visual Applications in Java?

02.05.2008
| 37729 views |
  • submit to reddit
In How to Create Visual Applications in Java?, I introduced you to the NetBeans Visual Library, specifically in the context of standard Java SE applications. Here we continue where we left off, but we add resize functionality... in very few lines of code!

At the end of the previous article, it was clear that (a) the NetBeans Visual Library is a very powerful framework for creating graphical widgets (for example, to make a widget move, you simply add one line of code), (b) one doesn't need the NetBeans Platform even though one is using one of its libraries and (c) one doesn't even need NetBeans IDE, even though one could do so, of course, but only if one wanted to do so. Simply put, you just download NetBeans IDE once, remove two of its JARs and then, if that's how you feel, you can remove NetBeans IDE completely. Next, simply put those two JARs on your classpath and you're good to go.

At the end of that article, we had created Chuk, one of the Sun evangelists, as a movable object with an editable label text field. We then also created Gregg, who is also a Sun evangelist, as another instance of the same object:

However, there was one problem with the above scenario, at least, according to Gregg: "Hey, how come Chuk's picture is larger?" That's the message he left in my blog, where I had also discussed this scenario in detail. So, this time, we're going to help Gregg. (So, all of this is for you, Gregg!) We're going to do two things, both of which are really cool, mostly undocumented, and illustrative of a whole host of things, as you'll discover if you stick with this article until the very end.

At the end, when your mouse moves over a widget, its borders will become resizable. Then, when you drag the handles on the border, the image will resize itself according to the movement of the mouse. So, you'll be able to make Chuk a whole lot smaller. And, Gregg can be made bigger (or smaller still):

Let's begin by asking ourselves how to create that border with the handles, i.e., the one that, when you see it, you think: "I am now able to resize something". Probably lots of code, right? Wrong. The Visual Library extends the borders offered by the JDK's Border classes. It provides a package called org.netbeans.api.visual.border.BorderFactory and another called org.netbeans.api.visual.border.Border. (I learned about all of this from Fabrizio Giudici's Creative Uses of the Visual Library, a document which I highly recommend.) Here we begin by declaring two constants, one for a normal border and one for resizing:

private static final Border RESIZE_BORDER = 
BorderFactory.createResizeBorder(8,Color.BLACK,true);
private static final Border DEFAULT_BORDER =
BorderFactory.createEmptyBorder(8);

Even though our default border is empy, we set its thickness to 8, so that the area reserved for our border is the same for both. (If you experiment later, you'll see that if you don't set a thickness, strange effects result when the resize border is enabled/disabled.) Now that we have our borders, we need to specify when they should be shown.

To do this, we create a class that extends IconNodeWidget, unlike the last time where we were extending GraphScene and then creating new nodes in attachNodeWidget. Creating a separate class gives us more room to maneouvre, allowing us to add a lot of behavior to an individual widget. Here we call our widget PhotoWidget, we expect to receive the scene, an image, and a name. We assign those to the widget's label and image. We then add, as before, the MoveAction, so that the widget can move. (Just one line of code and no listeners!) We also, for the first time, add the HoverAction, which will make the widget sensitive to our mouse, as it hovers over it:

private static final class PhotoWidget extends IconNodeWidget {

public PhotoWidget(Scene scene, Image pic, String name) {
super(scene);
setLabel(name);
setImage(pic);
setPreferredLocation(new Point(10, 20));
getActions().addAction(ActionFactory.createMoveAction());
getActions().addAction(scene.createWidgetHoverAction());
}

@Override
public void notifyStateChanged(ObjectState previousState, ObjectState newState) {
super.notifyStateChanged(previousState, newState);
getImageWidget().setBorder(
newState.isSelected() ? (
newState.isHovered() ? RESIZE_BORDER : DEFAULT_BORDER) : (
newState.isHovered() ? RESIZE_BORDER : DEFAULT_BORDER));
}

}

Finally, notice that we have overridden IconNodeWidget.notifyStateChanged, which will determine whether our border will be shown! And that depends on our calculations in the setBorder method, which returns a border, as calculated above. If the widget is selected or hovered over, the border changes.

Currently, however, even though the border changes, the image doesn't resize when we drag the border's handles. To be able to do that, we need to add four lines of additional code, just lines 7-10 below:

public PhotoWidget(Scene scene, Image pic, String name) {
super(scene);
setLabel(name);
setImage(pic);
setPreferredLocation(new Point(10, 20));
//Add lines 7-10 below:
setLayout(LayoutFactory.createVerticalFlowLayout(LayoutFactory.SerialAlignment.JUSTIFY, 1));
setChildConstraint(getImageWidget(), 1);
setCheckClipping(true);
getImageWidget().getActions().addAction(ActionFactory.createResizeAction());
getActions().addAction(ActionFactory.createMoveAction());
getActions().addAction(scene.createWidgetHoverAction());
}

It is very important that the ResizeAction appear BEFORE the MoveAction, otherwise the MoveAction will consume the event, which will result in your resize gesture causing a move action instead. Also note that we don't resize the WHOLE widget, but just the image. The other three lines change the layout and grab the image, such that they can be resized. We set the clipping check to true, so that the image is clipped as it resizes, otherwise bits of it would remain as we resized.

At this point, I should reveal that the above doesn't actually work, because of a known issue in the Visual Library. Guided by David Kaspar, the creator of the library, I hacked the sources of the library to make it work. You can do the same. Just attach the sources to your project, instead of the JAR. Then find ImageWidget.paintWidget and change this line:

gr.drawImage(image, 0, 0, observer);

...to these lines:

Rectangle bounds = getBounds();
gr.drawImage(image, bounds.x, bounds.y, bounds.width, bounds.height, 0,
0, width, height, observer);

Now that your image has bounds, it can be resized. You'll have to make this change in the sources yourself too, until the issue is fixed, hopefully soon.

Just to recap, starting from last time, we have a JFrame with a JScrollPane. We've added a Visual Library "scene" to the JScrollPane. Then we added a LayerWidget to which we added two IconNodeWidgets. Both can move, they're sensitive to hovering, they have names, and images. And now the IconNodeWidgets get new resize borders when hovered over. And the handles on those borders can be dragged to resize the widget, including its image.

For the record, here is our constructor, together with our declarations at the top of the class:

private static final Border RESIZE_BORDER =
BorderFactory.createResizeBorder(8, Color.BLACK, true);
private static final Border DEFAULT_BORDER =
BorderFactory.createEmptyBorder(8);
private LayerWidget mainLayer;
private Image CHUK = Utilities.icon2Image(
new ImageIcon(getClass().getResource("/demo/chuk.png")));
private Image GREGG = Utilities.icon2Image(
new ImageIcon(getClass().getResource("/demo/gregg.png")));

public DemoVisualFrame() {
Scene scene = new Scene();
initComponents();
jScrollPane1.setViewportView(scene.createView());
mainLayer = new LayerWidget(scene);
scene.addChild(mainLayer);
mainLayer.addChild(new PhotoWidget(scene, CHUK, "Chuk"));
mainLayer.addChild(new PhotoWidget(scene, GREGG, "Gregg"));
}

Hope you're happy now Gregg!

 

Published at DZone with permission of its author, Geertjan Wielenga.

Comments

Vincent Cantin replied on Wed, 2008/08/20 - 5:31am

Was it possible to fix the bug without hacking the sources by subclassing the ImageWidget and redefining the paint() function?

Vincent Cantin replied on Wed, 2008/08/20 - 6:14am in response to: Vincent Cantin

It seems that the IconNodeWidget doesn't allow us to use a subclass of the ImageWidget.

It also seems that the issue is still not fixed yet.

 

Varun Nischal replied on Thu, 2009/07/09 - 7:24am

Geertjan,

Well, I tried this with 6.0, 6.1 and 6.5 FCS. The issue is still there.

Is there any other alternative?

Thanks.

Daniel Morales replied on Mon, 2009/12/14 - 9:00pm

Yes, you can avoid the need to recompile the entire visual library.

How ? easy, follow these simple steps:

 

1. Create two classes, MyImageWidget and MyIconNodeWidget.

2. Copy the ImageWidget source code into MyImageWidget class and change what Geertjan commented in this post.

3. Copy the IconNodeWidget source code into MyIconNodeWidget class and change all the ImageWidget objects to MyImageWidget.

4. Change your personalized Widget class from extending IconNodeWidget to MyIconNodeWidget, for example change

 -> public class MyWidget extends IconNodeWidget

 to

-> public class MyWidget extends MyIconNodeWidget

 

And that's all, i know it works because i use it :)

 

Greetings !!!

Maatallah Riadh replied on Fri, 2010/01/29 - 3:48am

hello sir

I am currently developing a java application with the API Visual Library 2.0.

I encountered problems with layers widget and I like to have your help;

1 - always the first widget is added to the scene in the foreground, and others that continue (in order of addition) will be hidden by it.

2 - I want to add a componentWidget in another componentWidget but I encountered the same problem.

I think we should solve the first problem that the second will be solved.

thank you for helping me.

Riadh

John Philip Solano replied on Mon, 2010/07/19 - 1:06am in response to: Daniel Morales

Nice...

Gaurav Kumar replied on Mon, 2010/08/02 - 12:28pm

I need some help plz, I am working on JDeveloper i am acquiring images from flatbed scanners in my java application,I am not able to draw a resizable rectangle which can resize and drag.Rectangle must able to send acquired images to other jframe. any help will be appreciable!! thank you in advance...

Pari Dhanakoti replied on Sun, 2011/06/19 - 5:15pm

Hey, 1. Should I be removing the GraphDemoScene class altogether and add PhotoWidget? 2. Where is the obj for PhotoWidget created? What are the args passed? I understand a scene, Image n name has to be passed. Where are we creating them?? 3. How to get the sources files that you said you have "hacked"?

Pari Dhanakoti replied on Sun, 2011/06/19 - 5:17pm

Oops! I am sorry. Answer for my 1st two questions r right in your code! But what about getting those source files instead of the jar???

Pari Dhanakoti replied on Mon, 2011/06/20 - 9:37am

Hey All, If u were wondering like me, how to get the source files of the API 's Geertjan mentioned refer http://www.java2s.com/Open-Source/Java-Document/IDE-Netbeans/api/org/netbeans/api/visual/widget/ImageWidget.java.htm http://www.java2s.com/Open-Source/Java-Document/IDE-Netbeans/api/org/netbeans/api/visual/widget/general/IconNodeWidget.java.htm Include these in the pkg where ur source files are AND MOST IMPORTANT, comment the lines import org.netbeans.api.visual.widget.ImageWidget; (in IconNodeWidget.java) and import org.netbeans.api.visual.widget.general.IconNodeWidget; in DemoVisualFrame.java WHY???? Because we have included the source files instead of API. so comment the lines where u import the APi. Now everything works perfect for me!!!!! :) Cheers! Geertjan.

Awab Ahmad replied on Wed, 2013/01/09 - 2:54am in response to: Awab Ahmad

 Good. I like it.

Qandeel Khan replied on Tue, 2013/05/21 - 11:40pm

Very informative article about java.

Yogesh Kumawat replied on Tue, 2014/04/15 - 2:11am

 At the intention of that essay, we had designed Chuk, individual of the Bask missionarys, as a loose motive beside an editable call handbook province. excess baggage shipping by sea 

Yogesh Kumawat replied on Tue, 2014/04/15 - 5:01am

 Then, during you brake the grips on the flank, the icon devise resize itself travelling to the move of the rodent.  Volkswagen business car leasing kent

Yogesh Kumawat replied on Wed, 2014/04/16 - 3:12am

  Creating a discharge rate provides us further lobby to maneouvre, allowing us to tally a multiple of protocol to an distinctive widget. Here we challenge our widget PhotoWidget, we hope to get the landscape, an reflection, further a select.  Motorbike mot Reading 

Yogesh Kumawat replied on Thu, 2014/04/17 - 11:15pm

 We discern that, as a senior, you breathe on a finances. This is the understanding we give plausible expenses for our theme-paper utility. cheapest conveyancing fees

Yogesh Kumawat replied on Tue, 2014/04/22 - 7:46am

 Dissertation Poet’s club of specialists can edit exalted essence rule enactment writings to fulfill our patrons’ inexorable conditions. Payroll Processing


Yogesh Kumawat replied on Sat, 2014/05/10 - 1:46am

 Besides that relys on our prudences in the setBorder strategy, which rebounds a surround, as deliberate beyond. If the widget is selected or hovered too, the neighbor varys.  uk web hosting 

Alexey Kochinov replied on Wed, 2014/05/21 - 5:32am

 Geertjan,

Widget have resizable border with 8 controlpoint (3 on top bound, 3 on bottom bound, 1 left center, 1 right center).

Resizable worked, but mouse cursor not changed.

How we can change mouse cursor? (On example JFrame when we resize border)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.