Draw graphics beneath components - java

So I am currently making a login screen that has a cool looking background effect made using the Graphics object and a 'game loop'. When I add in a JTextField though, it is seen underneath everything and not above. Is there anyway to to make the graphics draw underneath all components inside of the JFrame?
Here is an image of the graphics:
The text field is there, just underneath everything being drawn to the surface of the frame. I want to somehow reorder this so it draws underneath components.
Here is my current frame code:
frame = new JFrame("Login");
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.getContentPane().setPreferredSize(new Dimension(450, 200));
frame.getContentPane().setBackground(Color.black);
frame.setBackground(Color.black);
frame.setLayout(new FlowLayout());
JTextField user = new JTextField(20);
user.setLocation(100, 200);
user.setVisible(true);
frame.add(user);
frame.pack();
frame.setVisible(true);
frame.createBufferStrategy(2);
buff = frame.getBufferStrategy();
Painter painter = new Painter();
frame.add(painter);
Any help please?

AnimationTest shows one approach. It overrides paintComponent() and invokes super.paintComponent() to ensure that components are rendered atop the background. Click anywhere to position a text field; resize to see how the default FlowLayout works. JPanel is double buffered by default using the existing buffer strategy.

Related

Swing layering - transparent component ignores underlying AWT element

First, to get it out of the way, I absolutely NEED to use heavyweight AWT component with a swing application. I need features from both of them.
The task is simple - render a heavyweight AWT Canvas (or any other element), render OpenGL scene directly onto it, then display Swing buttons above it for the UI.
My problem is that it works half-way.
I don't seem to have problems with Z-ordering. I am using jLayeredPanes for it, and I can move Canvas between layers and it actually works, popping on top or below other elements.
The problems are with transparency.
The thing is, Swing elements have Opaque parameter, and when it's set to false (non-opaque) - it should basically be transparent and you should see the next element below it. In my case, however, the AWT Canvas gets ignored, and you instead only see the next underlying SWING element.
Here are a couple of screenshots. They are taken from a standalone test project of mine. The canvas is stretched to the size of the frame, and in the upper left there is a JLayeredPane dummy element that is a simplified version of the menu.
On the first screenshot, the JLayeredPane's Opaque setting is set to true, and you can see that it's background property is set to Blue color.
On the second screenshot, everything is exactly the same but Opaque is set to false. Instead of displaying whatever is on the Canvas - what gets drawn in empty grey jFrame background.
Lastly, on the third screenshot I have put Canvas into a jPanel instead of leaving it on its own. As you can see, the Panel's orange color is seen through the transparent jLayeredPane, but the Canvas is yet again hidden.
Here's the code for the Frame layout. I would not post my rendering/context code right now
frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(width, height));
canvas = new Canvas();
canvas.setSize(width,height);
//this part exists only in the third example
JPanel p = new JPanel();
p.setSize(width,height);
p.setBackground(Color.orange);
p.add(canvas);
// third example end
JLayeredPane pane = new JLayeredPane();
JLayeredPane paneMenu = new JLayeredPane();
JButton button = new JButton();
button.setSize(20,20);
paneMenu.setSize(200,200);
paneMenu.add(button, new Integer(1));
paneMenu.setBackground(Color.BLUE);
paneMenu.setOpaque(false); //True for the first example
pane.add(p, new Integer(1)); // canvas for the first two examples
pane.add(paneMenu, new Integer(2));
pane.setOpaque(false);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
Could anyone please explain me what is going on and how to do what I need to do.
I will repeat again - I have to use heavyweight component as render target. I am aware of solutions like JOGL's GLPanel which is a lightweight Swing-compatible component. But I tried that method and the performance is really slow, because instead of directly rendering onto it as a context target - it reads FrameBuffer from memory, flips it, and then paints it as BufferedImage. This path is not fitting for the limited resources of an embedded system that I'll be running on.
c0der said: Please post minimal reproducible example
Errrrm.... Didn't I?
Here, you can have it in complete java class form if you want, but I literally changed some variables for constants.
import javax.swing.*;
import java.awt.*;
public class Main
{
public static void main(String[] args)
{
JFrame frame = new JFrame("AWT test");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.setPreferredSize(new Dimension(500, 500));
Canvas canvas = new Canvas();
canvas.setSize(500,500);
canvas.setBackground(Color.RED);
//this part exists only in the third example
JPanel p = new JPanel();
p.setSize(500,500);
p.setBackground(Color.orange);
p.add(canvas);
// third example end
JLayeredPane pane = new JLayeredPane();
JLayeredPane paneMenu = new JLayeredPane();
JButton button = new JButton();
button.setSize(20,20);
paneMenu.setSize(200,200);
paneMenu.add(button, new Integer(1));
paneMenu.setBackground(Color.BLUE);
paneMenu.setOpaque(false); //True for the first example
pane.add(p, new Integer(1)); // canvas for the first two examples
pane.add(paneMenu, new Integer(2));
pane.setOpaque(false);
frame.add(pane);
frame.pack();
frame.setVisible(true);
frame.transferFocus();
}
}
A little update:
I initially suspected that because Swing elements delegate all their drawing to the underlying heavyweight element (In my case JFrame), then what happens is that the frame generates a single frameBuffer for itself and then displays on top of Canvas. Canvas itself is not handled in this generation and thus the frame "covers" over the canvas.
That doesn't seem to be the case. I tried making the JFrame undecorated, all panels non-opaque, and display the picture. The result - canvas is still "cut", and through the hole you can see the underlying IDE menu.
This makes me think that somewhere during Drawing, the Canvas itself detects that it is obscured by another element, and that it doesn't need to draw that area. So it "optimizes" itself and doesn't update these pixels.
Maybe I'm wrong. But here's another screenshot. This is the same example as before, but I took out 3d rendering and simply trying to display Canvas with background set to Red.
Once again, going to reply to my own question.
It turned out that I need to do
setComponentMixingCutoutShape(paneMenu, new Rectangle());
for the menu pane that lies underneath the button. That essentially tells java not to cut out the element from the heavyweight underlying component.

Java Color Chooser panel in main window?

I'm trying to figure out how to add a HSV color chart to the window of my application. I'm aware of the color chooser offered by Java, but i would like to have the chart integrated in my own window instead of opening it a new window. Is there a way to add one of the panels from the color chooser directly to my window, or is there a way to create one myself?
JFrame frame = new JFrame();
frame.setSize(500, 500);
frame.getContentPane().setLayout(new BorderLayout());
JColorChooser colorChooser = new JColorChooser();
AbstractColorChooserPanel hsvPanel = colorChooser.getChooserPanels()[1];
frame.add(hsvPanel, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
I was able to pull the HSV Panel out by accessing the element at 1 in the getChooserPanels for the default JColorChooser.
Although this might not be ideal considering the implementation of the JChooserPanel could change the ordering.

JLabel and JTextArea not working

I am making a window that has a large text area and a small text area under it. This is what i have so far:
This is the code I have for it:
JPanel window=new JPanel(){
protected void paintComponent(Graphics g){
super.paintComponent(g);
ImageIcon ii=new ImageIcon("textEffect.png");
Image i=ii.getImage();
g.drawImage(i,0,0,null,this);
}
};
JLabel label=new JLabel("Say: ");
JTextArea dialog=new JTextArea(20,50);
JTextArea input=new JTextArea(1,46);
JScrollPane scroll=new JScrollPane(
dialog,
JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED,
JScrollPane.HORIZONTAL_SCROLLBAR_NEVER
);
//main method
public static void main(String[] args){
new Window();
}
//Makes window and starts bot
public Window(){
super("Pollockoraptor");
setSize(600,400);
setResizable(false);
setDefaultCloseOperation(EXIT_ON_CLOSE);
dialog.setEditable(false);
dialog.setOpaque(false);
dialog.setBackground(new Color(0, 0, 0, 0));
dialog.setLineWrap(true);
input.addKeyListener(this);
label.setVerticalTextPosition(SwingConstants.BOTTOM);
label.setHorizontalTextPosition(SwingConstants.LEFT);
window.add(scroll);
window.add(label);
window.add(input);
window.setBackground(new Color(97,118,131));
add(window);
setVisible(true);
}'
How do I make the larger textarea partially transparent so I can see the background and how do I move "Say:" to be infront of the smaller textarea?
JPanel uses a FlowLayout by default. You'll want to change the layout manager, personally, I'd recommend something like GridBagLayout, but that's just me.
See Laying Out Components Within a Container for more details
To make the JTextArea see through, you're going to have to make the JScrollPane and it's JViewPort transparent as well.
Swing only knows how to paint fully opaque or fully transparent components. You can create a translucent effect by making the component transparent and overriding it's paintComponent method and using an AlphaComposite or painting a with a Color which has an alpha value set to something below 255
For example...
Can not draw image on JTextArea background when using Nimbus Look And Feel
JTextArea not selectable, but still showing a "ghost" cursor
Swing Graphics on JFrame
And general advice...
Key Bindings over KeyListener
Override getPreferredSize of your custom component to get better results when been laid out
Use JFrame#pack over setSize, this will calculate the window size based on the needs of the content and take into account the window frame decorations as well
How do I make the larger textarea partially transparent so I can see the background
Check out Backgrounds With Transparency for problems when using a transparent background and a general purpose solution you can use to you don't need to do custom painting all the time.

Putting a JLabel on top of a JLabel in Java

I would like to know how to put a JLabel on top of another JLabel at a specific position, in a class that extends JPanel in Java. People have asked for help with this before but the solutions that I found do not satisfy me. I am using GridLayout, here is some of my code:
//imports
public class Game extends JPanel implements MouseListener {
Icon background = new ImageIcon(getClass().getResource("/background.jpg"));
Icon foreground = new ImageIcon(getClass().getResource("/foreground.jpg"));
JLabel backgr = new JLabel(background);
JLabel foregr = new JLabel(foreground);
JFrame frame = new JFrame("Game");
public Game() {
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
backgr.addMouseListener(this);
frame.setLayout(new FlowLayout());
frame.setPreferredSize(new Dimension(200,200));
frame.pack();
frame.setVisible(true);
frame.addMouseListener(this);
backgr.setLayout(new BorderLayout());
frame.add(backgr);
backgr.add(foreground);
}
}
(I have a lot of other methods in this class, such as some mouse stuff, but I don't include them here since they are not relevant to this problem.)
Currently the code almost works as I want, the foreground picture is displayed on top of the background, right in the middle of it. But I would like the foreground picture to be displayed at specific coordinates on the background picture. I thought that if I wanted to display the foreground at coordinates (50, 50), I could say backgr.add(foreground, 50,50), this compiles but returns an error when I run the program. Any tips of what to do? It would be really appreciated.
Edit: I discovered that by saying backgr.setLayout(null), backgr.add(foregr) and then foregr.setLocation(50,50), we can do what I was looking for. But since not using a layout manager is discouraged, I am looking for better solutions, so that I can use the coordinates on the backgr Icon.
People have asked for help with this before but the solutions that I found do not satisfy with me since they require me to set the frame layout to null, but I am using GridBagLayout
The frame is using a GridBagLayout.
You are adding the foreground JLabel, so the background JLabel. The background label can use any layout (including null) that you wish.
the foreground picture is displayed on top of the background, right in the middle of it.
That is because you are using a BorderLayout and are adding the foreground to the CENTER, which is the default when you don't specify a constraint.

Why is this JPanel not sticking to the specified size?

So, I'm trying to learn Java Swing and custom components. I've created a JFrame, given it a background color, and added a JPanel:
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(1000, 2000);
frame.setBackground(Color.WHITE);
JPanel jp = new JPanel();
jp.setBackground(Color.BLUE);
jp.setSize(40, 40);
frame.add(jp);
frame.setVisible(true);
The result is a 1000x2000 window colored blue (as opposed to a white window with a 40x40 blue box inside it). Why is the JPanel expanding beyond its specified size?
Using your code, simply add one line to change the LayoutManager of the JFrame. Then, when you add the component, it will keep it's preferred size.
Also, instead of calling jp.setSize(40,40), call jp.setPreferredSize(new Dimension(40,40)).
And, you need to call pack() on the JFrame to tell it to layout its components.
JFrame frame = new JFrame("Testing");
frame.setLayout(new FlowLayout()); // New line of code
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setPreferredSize(new Dimension(1000, 2000)); // Modified line of code
frame.setBackground(Color.WHITE);
JPanel jp = new JPanel();
jp.setBackground(Color.BLUE);
jp.setPreferredSize(new Dimension(40, 40)); // Modified line of code
frame.add(jp);
frame.pack(); // added line of code
frame.setVisible(true);
Also, you should read up on all of the different LayoutManagers available to you. Here is a great tutorial.
The default layout manager for a JFrame is BorderLayout. When you add a component to the frame without constraints, it uses BorderLayout.CENTER as the default constraint. This means the component takes up all available space, regardless of its requested size.

Categories

Resources