Making a glassPane fit with the dimensions of a JPanel - java

StackOverflow!
I'm working on a project, in regards to making a navigation system, and we're in the beginning stages, working with drawing the map and navigating it itself.
We have a function, where you should be able to draw a square on top of the current data having been drawn already. I want to avoid having to repaint all of the data for the roads all over again, with each mouseDragged action event. To do this, I found that using a glass pane would be the optimal choice.
The problem recides with the dimensions of the glasspane and the coordinates it reads. When I click on a certain spot on our JPanel (which we use to draw on), it knows how and where to zoom in properly, but when I draw my square, it is being drawn a specific amount of pixels above my mouse's position. This seems to be due to the fact that my glass pane dimension does not correspond to my JPanel dimensions. The glass pane seems to also cover my menubar, whereas the JPanel itself does not read clicks which occur on the menubar.
I want to somehow fit a glasspane to my JPanel, and I've tried a few different things.
One thing I tried, was to draw on a JRootPane instead of a JPanel, but that doesn't seem to be possible. Then I tried to just retrieve the "MyGlassPane" class from my JPanel (which also has its own class), and call it's repaint method, but that's where the issue originated from.
Most lately, I've retrieved the JRootPane which the JPanel itself uses or is part of, and then retrieved the glasspane of this JRootPane, but it seems that my menubar, added to the JFrame, is also part of this JRootPane, meaning that the glasspane covers the menubar as well.
Here is some very simplified code, which covers the issue, but not the mouse-listening nor data-drawing aspects. It does however show off the fact that it's annoying that the rectangle is being drawn on top of the menubar.
import java.awt.*;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import javax.swing.*;
public class TestForRootPaneStuff {
JFrame frame = new JFrame();
JRootPane root;
JPanel panel = new JPanel();
JLabel label = new JLabel("Hello there!");
public void rootPanePls()
{
frame.add(panel);
panel.add(label);
root = panel.getRootPane();
JMenuBar menubar = new JMenuBar();
JMenu prettyMenu = new JMenu("Pretty");
menubar.add(prettyMenu);
frame.setJMenuBar(menubar);
MyGlassPane gp = new MyGlassPane();
root.setGlassPane(gp);
gp.setVisible(true);
gp.setEnabled(true);
gp.repaint();
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(250, 250));
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args) {
TestForRootPaneStuff derp = new TestForRootPaneStuff();
derp.rootPanePls();
}
private class MyGlassPane extends JComponent implements ItemListener
{
//React to change button clicks.
#Override
public void itemStateChanged(ItemEvent e) {
setVisible(e.getStateChange() == ItemEvent.SELECTED);
}
//Draw the square for zoomToSquare
#Override
protected void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.drawRect(10, 10, 100, 100);
}
}
}
And this is a corresponding screenshot, as a result of running this code. (I can't post direct images, due to lack of activity on here)
I hope my explaination makes at least a bit of sense. I'm aware that my explaination is perhaps a bit too in-depth as for actually resolving the issue, but I felt like it was necessary to invelop you guys a little bit more into my situation, and my reason for needing a solution for this.
TL;DR:
I need a way to make a glassPane (retrieved/set from somewhere) completely fit the dimensions of a specific JPanel, so that I can receive the same mouse event coordinates for both the JPanel and the glasspane. See code above for visualization of the issue.
Is there a smart way to go about this? Setting the RootPane glasspane was my final idea...
Thanks in advance, if anyone has any insight to bring!
Kirluu~

not an asnwer longer comment, just guessing based on wrong/uncomplete code posted here
use JLabel instead of JComponent, JComponent hasn't any LayoutManager in API, sure JLabel too, but it to be container very simple like as contianer, is transparent, anything in paintComponent can be (semi) transparent
your code is incompleted why is there ItemListener, don't put JComponents to JComponent as contianer, use JPanel instead, or mentioned opaque JLabel is easiests of ways for GlassPane, 1st code line should be super.paintComponent in paintComponent, dispose() all custom painting for anything placed/laid in GlassPane
JLabel can has easilly to copy the coordinates from JPanel, use ComponentListener added to JPanel, with delaing (350-600) events from ComponentListener (one event per one pixel) by using Swing Timer, EDIT if resize continue call timer.restart() </EDIT, if resize ended then Swing Action/ActionListener will fire setBounds for JLabel (whatever) placed in GlassPane
search for simple RepaintManager (just by covering JLabels bounds in GlassPane) for animation in GlassPane

Related

Removing JFrame Border?

I have a Java Swing GUI and everything in my JFrame is offset by a few pixels. On MacOS, I had to offset everything by 12 pixels downward to account for it. On Windows, everything is shifted to the left and downward as well. I discovered that
setUndecorated(true);
removes the JFrame border (which I suspect is the cause of my problems) but it also removes the title bar.
Is there a way I can remove the JFrame border (or some other alternative to make sure everything is centered) and still keep the title bar? I need the title bar so that I can move the JFrame around and have the maximize/minimize/close functions.
Also, the layout is set to null in case that matters. (Everything I'm doing is pixel - based so I cannot set it to anything else).
Thanks.
I found a solution after Googling a bit more. Calling
getContentPane().setPreferredSize(...)
pack();
inside the JFrame constructor will adjust everything so that the titlebar and borders will not impact the view of the content pane.
For anyone else that may need this, you have to set the preferred size of the content pane specifically as that is what you want to appear correctly.
This way you can keep the titlebar and all the normal functionality of a JFrame window that you would otherwise lose with setUndecorated(true);.
The reason why you are adding 12px is because this is consume by the Title and border.
If you use setUndecorated(true) You will loose the title bar and you have to implement the addWindowListener to add a location changing of of an application.
The best way to do is:
Class Main{
public static void main(String[] args){
//JFrame
JFrame frame = new JFrame();
//Create a Main Panel and setPreferred Size and not set Size or set Bound
JPanel mainPanel = new JPanel(); //You value down
mainPanel.setPrefferedSize(new Dimension(x, y));
frame.add(mainPanel);
//and then in last add
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//the pack method help you to setSize of the JFrame According to the
//Component size
frame.pack();
frame.setVisible(true);
}
}

Elements don't appear in a Panel with a GridLayout or FlowLayout, but with a setBounds they do

I'm doing a program that is composed by multiple panels in a JFrame.
I need to do every elements in differents classes (It's because in my school, we need to have every elements separeated in different classes for clean code), but every example that I see with my kind of problem, they do everything in one class.
And I think that my problem comes from having multile classes so I show you my classes.
I have a panel in wich I need to put 2 panel, here is the code :
public class Inscription extends JPanel{
private PanneauBoutons panneauBoutons = new PanneauBoutons();
private PanneauFormulaire panneauFormulaire = new PanneauFormulaire();
public Inscription(){
this.setLayout(new BorderLayout());
this.setBorder(BorderFactory.createLineBorder(Color.RED, 2));
this.add(panneauFormulaire,BorderLayout.CENTER);
this.add(panneauBoutons,BorderLayout.SOUTH);
this.setVisible(true);
}
}
And here is the Panel panneauFormulaire :
public class PanneauFormulaire extends JPanel{
private JLabel labelMatricule;
private JTextField zoneTexteMatricule;
public PanneauFormulaire(){
this.setLayout(new GridLayout(8,2,10,10));
this.setBorder(BorderFactory.createLineBorder(Color.black));
labelMatricule = new JLabel("Matricule : ");
this.add(labelMatricule);
zoneTexteMatricule = new JTextField(30);
this.add(zoneTexteMatricule);
this.setVisible(true);
}
So the problem Inscription don't appear on the main Frame if I don't do setBounds, but I want a BorderLayout...
(I tested and with a set bounds I can see the borders, so I think that it means the panel are really added to the Frame so why without setBounds I see anything?).
And the other problem is that the panel PanneauFormulaire don't appear on the Inscription panel...
So if I miss something, can you help me? thank you
And here it is the class that extends JFrame :
public class FenetrePrincipale extends JFrame {
private Container cont;
private Inscription inscriptionForm;
public FenetrePrincipale(){
super("IESN");
setBounds(100,100,1200,960);
getContentPane().setLayout(null);
setLocationRelativeTo(null);
setResizable(false);
...
inscription.addActionListener(new ActionListener(){
#Override
public void actionPerformed(ActionEvent e){
cont.removeAll();
inscriptionForm = new Inscription();
inscriptionForm.setOpaque(true);
cont.add(inscriptionForm);
invalidate();
repaint();
}
});
You should NOT be using a null layout and setBounds(). Swing was designed to be used with layout managers.
but when I click on an option in the menu, the current panel need to be change by another one,
Then you should be using a CardLayout.
Read the section from the Swing tutorial on How to Use CardLayout for working examples. So download the example and use it as the starting point of your project. The code will be better structured then what you currently have and it is easier to change working code than it is to fix broken code.
so why without setBounds I see anything?
That is because you set your layout to null in getContentPane().setLayout(null);.
Java containers comes with a default layout which you are allowed to set to a different one. How the components are arranged in the container are dependent on the layout you use. The layout will directly affects the location, alignment, spacing, dimension, preferredSize of the components.
However, if you choose not to use any layout (.setLayout(null)). Swing will not know how you want the components to be arranged, hence you see nothing in your content pane.
Since you wanted "absolute control" over the components, you will be expected to set the bounds (location and dimension) of each added component manually by yourself. This is why you are not seeing any components (even if you already added it) until you set the bounds for them.
Java, elements don't appear in a Panel with a GridLayout or FlowLayout, but with a setBounds they do
Every layout has their own characteristics and for some of them the order of your codes does makes a difference. Hence, I will advise you to go through what each layout can do for you. Then, depending on your needs, choose one (or a combination of a few) and study how to use it.
And here it is the class that extends JFrame :
You probably won't want to extends to a JFrame. You can always make a customized Container like JPanel and add it to the frame.
(Why would you want to paint your paintings on a frame instead of a piece of paper?)

How to add a drawing (paint method) and another panel with controls in one frame?

I need to have a JFrame where the upper part is a drawing made by paint() and the lower part is a panel composed of JLabel, JTextField and JButton components.
Is this possible? How am I supposed to do this?
I need to have a Jframe where the upper part is a drawing made by paint() and the lower part is a panel composed of JLabel, JTextField and JButtons.
There is no conflict on what you want to do. You can have a main JPanel with 2 sub panels. One on the top for your drawings, the other at the bottom for containing your JComponents such as JButtons:
The structure in code may look like this:
class MainPanel extends JPanel{
private DrawingSpace drawingSpace; //Customized JPanel for drawing
private JPanel subPanel;
public MainPanel(){
setPreferredSize(new Dimension(400, 400));
initComponents();
}
private void initComponents(){
drawingSpace = new DrawingSpace();
subPanel = new JPanel();
}
}
You can have a customized JPanel as follows (this is optional):
class DrawingSpace extends JPanel
{
public DrawingSpace(){
//Set size..etc
}
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
//perform your drawings here..
}
}
After the implementations for the JPanels, you can just add an instance of MainPanel to the JFrame:
JFrame frame = new JFrame();
frame.add(new MainPanel());
//Other codes for JFrame not shown here
The soulution suggested by user3437460 (use a JPanel for the upper part, and override the painting methods in that JPanel) is the preferrable way to solve this.
However as you asked for a solution to directly paint the upper part (which is not advised, but there are solutions):
A (nasty) workaround for the question would be overriding the necessary paint method of JFrame, draw your upper part, translate the graphics context by some 100 pixels and call inherited paint methods to draw the bottom part. (Note that you'll have layout manager issues, as the layout manager won't see the 100px height of the upper part. However, if you're using an absolute layout, it could work. Hacks, hacks hacks :(
Another super-hack is to actually make the lower part big enough (if you use absolute layout, position your lower part at y=100px). Then add your own GlassPane and render the content for the upper part (or anywhere) on the glassPane.
Of course you can create a dedicated layout manager, which leaves the top 100 px part empty. Use that layout manager, and then you get some empty space on the top, which you can draw on.
I think now you can agree that the problem is rather "how to put a custom drawn component on the top of the window", which is solved by putting a custom drawn JPanel on the top of the window. Keep it easy! Peace!
ps: override paintComponent() instead of paint() of JPanel. See bottom of http://www.oracle.com/technetwork/java/painting-140037.html
For the painting portion, create a JPanel (or other paintable component) and override the paint method. Use a second JPanel and place all of your other components in that.
From there, check out how to do layout management at https://docs.oracle.com/javase/tutorial/uiswing/layout/using.html#set
The simplest way to do this is to use GridLayout, with the painted panel on the top half and the components panel on the bottom half.

Display a grid of JPanels in a JFrame?

I'm very new to Java but have some experience with C++. This is a homework assignment so I'm really just looking for someone to point me in the right direction.
The assignment requires a JFrame with JPanel objects displaying every card in a deck in a 13x4 grid. The Professor has supplied us with some code to get us started:
import javax.swing.*;
import java.awt.*;
public class Main
public static void main(String[] args)
{
//load the card image from the gif file.
final ImageIcon cardIcon = new ImageIcon("cardImages/tenClubs.gif");
//create a panel displaying the card image
JPanel panel = new JPanel()
{
//paintComponent is called automatically by the JRE whenever
//the panel needs to be drawn or redrawn
public void paintComponent(Graphics g) {
super.paintComponent(g);
cardIcon.paintIcon(this, g, 20, 20);
}
};
//create & make visible a JFrame to contain the panel
JFrame window = new JFrame("Title goes here");
window.add(panel);
window.setPreferredSize(new Dimension(200,200));
window.pack();
window.setVisible(true);
}
}
I have tried out a few things, but I can't seem to get multiple panels to display. Should I use a gridLayout() feature? or just create multiple panels and specify each one's location in the frame?
Again if someone can just point me in the right direction that would be awesome.
For displaying elements at the same size, evenly distributed within the container, then yes, GridLayout would be a good choice.
If you need to display the components in the grid at there preferred size (which may be different for each component) then GridBagLayout would be a better choice
If the code was supplied by a your professor, then you need to go back and make them fix it.
Firstly, a JLabel would be easier and provide better support for what you are trying to achieve...
Secondly, because the JPanel doesn't override getPreferredSize, most of the layout managers will set the size of the component to 0x0
There is a way to display multiple JPanels in one JFrame. Unlucky you the way is not so easy. Java has many diffrent LayoutManagers.
For your purpose I would recommend GridBagLayout, it is more complex, but definately the thing you need.
Here is a good tutorial, which helped me to understand it:
GridBagLayout
Hope it is a help.

Embed JPanel in JApplet (always on top)

I have a hybrid swing application which can be run as applet or java application with the following structure:
public class Gui extends JApplet {
public void init() {...}
public void paint(Graphics g) {
...
g.drawImage(image, 0, 0, this);
}
public static void main(String args[]) {
JFrame frame = new JFrame();
JDialog dialog = new JDialog(frame);
Gui gui = new Gui();
gui.init();
gui.start();
dialog.add("Center", gui);
...
}
}
The whole gui consists of one drawing area which is continously updated.
I would like to embed a panel which is always on top and independent of the underlying drawing process.
I tried using JLayeredPane and add it between JDialog and JApplet, but Japplet cant be added to the Pane because it is a top level container.
I also tried realising it with the glasspane but no success at all.
Is there a solution without refactoring to much since the structure should be kept as far as possible.
Don't override paint() of a top level container, like JApplet or JFrame. Custom painting is done by overriding the paintComponent() method of a JPanel (or JComponent). Then you add the panel to the frame.
dialog.add("Center", gui);
The is not the way to add a component to a panel. Read the Container API to find the proper add(...) method to use. Also don't hardcode string values. Every layout manager contains variables that can be used as the constraint values.
I would like to embed a panel which is always on top and independent of the underlying drawing process.
Not sure this makes sense. If the panel is always on top, then it would cover the painting.
I tried using JLayeredPane
That sounds like the proper approach. You add the layered pane to the frame or applet. Then you can have a background painting panel and another transparent panel on top. Read the Swing tutorial on Using Layered Panes for a working example.

Categories