JavaFX Canvas vs. Node+Binding performance - java

I have a use case where I need to draw a component consisting of around 100 lines and up to 1000 other elements (rectangles and text, mostly).
The content of the component should resize when the Parent node changes its size (e.g. when the application window changes its size).
I can think of two approaches how to deal with this:
Use JavaFX Line, Text, Rectange etc. Nodes and bind the widths and heights to the parent Node.
As in 1. but, instead of binding, develop a Pane which layouts the elements accordingly
Use the Canvas node and draw everything like with Graphics2D in Swing.
Which approach would be faster? How large is the impact of using bindings to change the size of this number of Nodes?
Is it possible to ensure, using the Node+Binding approach, that the lines are crisp? AFAIK the lines need to be drawn subpixel to be crisp, so adding 0.5px to the position is necessary. But is it possible to do this conditionally in a Binding? I already tested adding 20 Lines to the Pane and Binding their y-position to the height of the Parent, but some are drawn crisp and some are blurry.
If creating a new container which layouts its children (like VBox and so on) is the best option, how complex would be the task?

Related

Image priority Java

I'm putting images on my JFrame using ImageIcon and then, to display the images:
getContentPane().add(label[3]);
getContentPane().add(label[2]);
getContentPane().add(label[1]);
Now Image 3 will have priority over 2 and 2 over 1 etc
Is there anyway to set the priority or reverse it so the last image I put on screen will have priority over the other images?
Your question is not clear at all, if you want to put the images along a third dimension axis, you should take a look at this layered panes tutorial.
From the official documentation:
A layered pane is a Swing container that provides a third dimension
for positioning components: depth, also known as Z order. When adding
a component to a layered pane, you specify its depth as an integer.
The higher the number, closer the component is to the "top" position
within the container. If components overlap, the "closer" components
are drawn on top of components at a lower depth. The relationship
between components at the same depth is determined by their positions
within the depth.
As shown in this image, you should be able to position your components on the top of the others.
If this does not meet your requirement, please edit your question to be more specific.

How to draw selectable, movable, and re-sizable rectangles on JavaFX canvas?

Is there a way to allow the user to draw individually selectable, movable, and re-sizable shapes within JavaFX and/or canvas that I am not considering or aware of?
I am new to JavaFX (Java GUIs in general, actually) and am trying to devise a method of allowing a user to draw (click & drag) up to 100 rectangles on a pane/workspace/page. I am also looking for a method to make this scalable, but keeping static proportions for now will be fine. I have not found a great deal of comparable examples in my search, most of which explore only one of those options but leave out certain things I need to accomplish. Usually they show pre-defined nodes or canvas shapes that aren't individually workable. So I am trying to find the best method to accomplish this.
Drawing the shapes is NOT the issue, I know how to do that. It's making them individual objects within the workspace (selectable, movable, re-sizable).
My current idea is to create a Javafx pane, then create 100 canvases within the pane as predefined layers which will be the same dimensions as the entire workspace. Each drawn rectangle will occupy one of the layers/canvas. Selecting each rectangle will involve a click event within the drawn rectangle within the layer allowing the user to edit the, (move, re-size, etc.)
The following Oracle tutorial is what I am going off of for this method of layering. http://docs.oracle.com/javafx/2/canvas/jfxpub-canvas.htm
I have the feeling that my working premise for this method is flawed. One reason is it requires pre-defined number of layers. Although I want to limit this anyway, it is not very dynamic. But mostly, it just seems convoluted.
My apologies for the lack of code, and for the the conceptual nature of the question, but I've been searching and experimenting (unsuccessfully) for a couple weeks. Any help or insight would be appreciated.
Thank You
Project Context
I'm creating a basic form creator. The user (in creation mode) will drag a series of rectangles that are associated with corresponding objects indicating certain attributes, coordinates, and dimensions of each rectangle. This data will be saved and used (in form mode) for the placement of text fields for form use. Wherever rectangle were drawn on the workspace, text fields of the same location and dimension will be placed on the form.
The easiest method I can think of is by making a group, and adding rectangles to the group, on-demand. You can then attach transforms and mouselisteners to each rectangle, and have it so that the properties of the transform is updated when a drag is detected. I've put together a simple demo here. Use a right-click to make a rectangle, left mouse drag to resize, and middle mouse drag to move the rectangle.
Group root = new Group();
Scene scene = new Scene(root, 1080, 720, true);
scene.addEventFilter(MouseEvent.MOUSE_PRESSED,e->{
if(e.getButton()==MouseButton.SECONDARY)
{
//make a new rectangle
Rectangle rect = new Rectangle(100,100);
root.getChildren().add(rect);
rect.setFill(Color.CYAN);
Scale scaler = new Scale(1,1);
Translate locationCenter = new Translate(e.getX(),e.getY());
rect.getTransforms().add(locationCenter);
rect.getTransforms().add(scaler);
rect.getTransforms().add(new Translate(-50,-50));
//listen for left mouse drags
rect.addEventFilter(MouseEvent.MOUSE_DRAGGED, e2->{
if(e2.getButton()==MouseButton.PRIMARY)
{
//resize with left drag
double scaleX=e2.getSceneX()-rect.localToParent(0,0).getX();
double scaleY=e2.getSceneY()-rect.localToParent(0,0).getY();
scaler.setX(scaleX/100);
scaler.setY(scaleY/100);
} else if(e2.getButton()==MouseButton.MIDDLE)
{
//move with middle drag
locationCenter.setX(e2.getSceneX());
locationCenter.setY(e2.getSceneY());
}
});
}
});

Why can't I resize a canvas using the layout constraints?

Have an AnchorPane, then put a Canvas. Set the canvas' layout constraints (top, left, right, bottom). Technically, the canvas should resize if the AnchorPane resizes. But it does not. If you use something like a button instead of a canvas, it works.
Why can't I resize a canvas using the layout constraints?
First: Canvas is a direct sub-class of Node and does not override Node.isResizable(), so the default is taken:
public boolean isResizable() {
return false;
}
This is just a technical explanation why parent layout containers will skip the Canvas during layout calculations.
The other consideration is that Canvas and "normal" JavaFX Nodes internally work completely different.
During every layout pass (Parent.layoutChildren() and Node.autosize()) the layout of a Node gets recalculated to always reflect the correct state. There are many other factors (cache hint, optimizations, visible state, managed state, ...) that are getting into play, but this is the very basic concept here.
The Canvas on the other hand is a Node that paints all its "children" onto a whiteboard via the GraphicsContext and is done. No layouting, no autosize. So this is blazing fast (after the initial painting) and if you change this you should know what you are doing and why you are doing this, because making a Canvas resizable strips many benefits away.
But there is a very nice and fast example how to create a resizable canvas: http://fxexperience.com/2014/05/resizable-grid-using-canvas/

Java Swing - Dynamic List that Fills Available Space?

This doesn't seem like it should be very hard but I can't figure out how to do this:
I have a subclass of JPanel. It has a fixed height, but can be any width. The subclasses, on construction, set their preferred size using setPreferredSize(), which means I have to provide a width in addition to the height.
I would like to make a scrolling list of some number of my subclass, where the subclasses all fill the available horizontal space.
Right now I have a scroll view containing a JPanel containing my subclasses. The containing JPanel uses a BoxLayout with a vertical orientation.
Vertically, it looks great. Horizontally, my custom panels are just stuck at the preferred size. What would be the easiest way to make my panels fill the available horizontal space? I tried writing some layout listeners for them, but the performance was flakey (it seems that sometimes the event messages get dropped?) and the code looked hacky. Other views, such as the JList, JTree, and scroll views seem to resize automatically to fill the available space in a BoxLayout, so I feel like there must be something I can do in my JPanel subclass that I haven't thought of.
I'm open to using another Layout Manager if something else is better suited for this. I looked at GridBagLayout, but that seemed more geared towards static layouts where components aren't added and removed at runtime.
Edit: I found this on Oracle's Documentation for BoxLayout which has an example that looks exactly like what I want:
What if none of the components has a maximum width? In this case, if all the components have identical X alignment, then all components are made as wide as their container. If the X alignments are different, then any component with an X alignment of 0.0 (left) or 1.0 (right) will be smaller. All components with an intermediate X alignment (such as center) will be as wide as their container. Here are two examples:
Could someone show me the code that will produce the same results? The example code in the documentation doesn't look like it covers this particular picture.
When you put your component in a JScrollPane, your component may implement the Scrollable interface to adjust the scrollpane’s behaviour. By doing this you can implement the method getScrollableTracksViewportWidth() to return true so your component will always have the available width and be scrollable in vertical direction only.
This is how JList, JTable, JTree, and all the text components of Swing do it.
Alright - I found the problem. It appears that the default implementation of getMinimumSize() and getMaximumSize() will simply return the value of getPreferredSize() if it is set. So by setting a preferred size without a maximum size, my maximum size was the preferred size. By overriding getMaximumSize() to return (99999, preferredHeight), it now works exactly as I want.

Fixed percentage based grid with MiGLayout

I am trying to create a grid with MiGLayout that is enforced on its children. This means that if I insert a child into grid position (1,1) and the grid's size is [10%!] that this child must NOT be bigger and overlap other cells. The child must be shrunk to fit the Grid cell.
This is what I have so far:
new MigPane("", "[5%!][20%!][5%!][65%!][5%!]", "[45%!][50%!][5%!]");
Now, I insert a big component (a picture that I have no control over) in Grid 1,1, like this:
migPane.add(myImageView, "cell 1 1, width 100%!");
However, that does not seem to restrict the ImageView at all.
How do I tell MiGLayout that I want "myImageView" to be put in grid 1,1 and size it to fit? Is there a "fit" keyword? :)
Note that specifying anything with pixels/points/mm/cm/inches is NOT what I want. My app always runs full-screen and must scale seamlessly (it is not a traditional form app, it is a video system using JavaFX).
It looks like percentages are supported, according to the docs:
Overrides the default size of the component that is set by the UI
delegate or by the developer explicitly on the component. The size is
specified as a BoundSize. See the Common Argument Types section above
for an explanation. Note that expressions is supported and you can for
instance set the size for a component with "width pref+10px" to make
it 10 pixels larger than normal or "width max(100, 10%)" to make it
10% of the container's width, but a maximum of 100 pixels.
Maybe try something like: "width max(100%, 100%)".

Categories