How do I create a transparent window with red border? - java

I'm trying to make a software that records the screen when a key is pressed. In order to indicate that the program is now recording, I want to put a red border around the outside of the screen. I'm having trouble getting it to work, here is my attempt so far:
public Main() {
JFrame frame = new JFrame("");
frame.setUndecorated(true);
frame.setAlwaysOnTop(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setSize((int)ss.getWidth(), (int)ss.getHeight());
frame.setLocationRelativeTo(null);
frame.setVisible(true);
frame.createBufferStrategy(3);
BufferStrategy bs = frame.getBufferStrategy();
Graphics2D g = (Graphics2D) bs.getDrawGraphics();
g.setColor(Color.RED);
g.drawRect(0, 0, frame.getWidth()-1, frame.getHeight()-1);
g.dispose();
bs.show();
}
It seems like setting the background transparent makes the graphics object not able to draw onto the jframe, and setting the background of the graphics object to transparent only leaves a white background with a red border, rather than transparent. I'm completely stuck on this one at the moment so any help would be appreciated!

You can't really draw on a component that way, you would need to override paintComponent(g) to do that.
You can simply add a border object:
((JComponent) frame.getContentPane()).setBorder(new LineBorder(Color.RED, 10));

I believe the following code achieves what you want. Notes after the code.
import static java.awt.Frame.MAXIMIZED_BOTH;
import static javax.swing.WindowConstants.EXIT_ON_CLOSE;
import java.awt.EventQueue;
import java.awt.Color;
import java.awt.Container;
import javax.swing.BorderFactory;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Recorder implements Runnable {
private JFrame frame;
#Override // java.lang.Runnable
public void run() {
showGui();
}
private void showGui() {
frame = new JFrame();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
Container contentPane = frame.getContentPane();
if (contentPane instanceof JComponent) {
JComponent jCmpt = (JComponent) contentPane;
jCmpt.setBorder(BorderFactory.createLineBorder(Color.RED, 5, true));
}
frame.setExtendedState(MAXIMIZED_BOTH);
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
/**
* Start here
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Recorder());
}
}
setExtendedState() maximizes the JFrame so that it takes up the entire screen.
setUndecorated() removes the title bar and the border of the JFrame.
setBackground() makes the JFrame transparent.
setLocationRelativeTo() is optional since the JFrame is maximized.
Finally I set a thick, red, rounded border around the content pane of the JFrame.
Note that you can close the JFrame by pressing Alt+F4 keys on the computer keyboard.
Optionally, you can also add the following:
frame.setAlwaysOnTop(true);

Related

Trouble with borders

I'm trying to get clarification as to how borders work, specifically the insets, and in searching through the Java docs and numerous websites I can't seem to find a clear explanation. Looking at this code:
import java.awt.AlphaComposite;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
import javax.swing.border.EmptyBorder;
public class ShadowWindow {
public static void main(String[] args) {
new ShadowWindow();
}
public ShadowWindow() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(new ShadowPane());
JPanel panel = new JPanel(new GridBagLayout());
panel.add(new JLabel("Look ma, no hands"));
frame.add(panel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class ShadowPane extends JPanel {
public ShadowPane() {
setLayout(new BorderLayout());
setOpaque(false);
setBackground(Color.BLACK);
setBorder(new EmptyBorder(0, 0, 10, 10));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fillRect(10, 10, getWidth(), getHeight());
g2d.dispose();
}
}
}
The way I interpret it this is what is happening:
JFrame frame (200 x 200) is created
JPanel shadowPane is created (also 200 x 200) with an empty border of 10 pixels created on the inside bottom and inside right of the JPanel
A second JPanel is created (200 x 200) and added on top of shadowPane
A rectangle is drawn (200 x 200) starting at x = 10 and y = 10
So my question in how is the shadowPane going past the range of the JFrame? Does the border go 10 pixels outside the JFrame or does it exist inside the JFrame. From everything I've found it should be inside, but that doesn't make sense based on how this code generates a shadow behind the frame. can anybody walk me through this? Thanks.
So my question in how is the shadowPane going past the range of the JFrame?
It's not. pack determines the preferred layout size of the content and makes the window big enough to accommodate it, because the frame is undercoated AND it's background is transparent, it "appears" as if the shadow hangs past the frame, it's an illusion.
The empty border is making sure that content added to the ShadowPane is "forced" into a small space.
Lets change the code slightly...
JFrame frame = new JFrame("Testing");
JPanel content = new JPanel(new BorderLayout());
content.setBackground(Color.RED);
//frame.setUndecorated(true);
//frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setContentPane(content);
//frame.setContentPane(new ShadowPane());
JPanel panel = new JPanel(new GridBagLayout());
panel.add(new JLabel("Look ma, no hands"));
ShadowPane shadowPane = new ShadowPane();
shadowPane.add(panel);
frame.add(shadowPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
What this does, is creates a new background panel, filled with the color red. It also adds the window decoration back in.
As you can see, the shadow pane and the label are are all rendered within the confines of the window.
If we once again remove the window decoration...
You can see that it's still the same...
So what's going on?
getPreferredSize is providing the core information about how the component would like to be size (in this case 200x200)
The EmptyBorder is defining a usable space within the ShadowPane which defines an area within which content can be displayed, it's leaving 10 pixels to the right and bottom of the component, in which components can't be displayed. This is take care of automatically by the layout manager. This means that the ShadowPane can actually paint here itself, but components added to it will never be displayed here, hence the shadow board.
Basically, it's smoke and mirrors and used to generate the illusion of a drop shadow behind the content added to the frame (or the ShadowPane in this case)

How to pass events through a JFrame?

I have an undecorated JFrame with some contents on it (Labels, Images, etc.). I need the JFrame to pass all the events through it. For example: when a click is made on that JFrame, I want it to pass that click through, to the window/anything that is underneath the frame.
Problem Example:
public static void main(String[] args) {
JFrame.setDefaultLookAndFeelDecorated(true);
JFrame f = new JFrame("Test");
f.setAlwaysOnTop(true);
Component c = new JPanel() {
#Override
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D)g.create();
g2.setColor(Color.gray);
int w = getWidth();
int h = getHeight();
g2.fillRect(0, 0, w,h);
g2.setComposite(AlphaComposite.Clear);
g2.fillRect(w/4, h/4, w-2*(w/4), h-2*(h/4));
}
};
c.setPreferredSize(new Dimension(300, 300));
f.getContentPane().add(c);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setVisible(true);
com.sun.awt.AWTUtilities.setWindowOpaque(f,false);
}
In this case, the JFrame has a border with close/minimize/fullscreen controls, and graphics on the JFrame still catch the events while just the transparent parts pass them through. I need both (transparent parts, and with graphics) to pass through the events.
Video Example of my goal: https://www.youtube.com/watch?v=irUQGDDSk_g
Similar question:
Pass mouse events to applications behind from a Java UI
This question tries to achieve a similar goal, but the JFrame is decorated (has close/minimize controls with an outer frame), and graphics still catch the user control events.
Question: How can I make a JFrame with graphics, that will not catch the events from the user controlling, but to pass in through?
This is not an answer, but a correction to the code example
The example you provide is actually doing some very dangerous things, first it's painting a translucent color onto an opaque component, this means that Swing doesn't know that it should actually be painting anything under the component and could also result in a number of very nasty paint artifacts, as Swing only knows about opaque and transparent component, it doesn't know about semi-transparent components, so you need to trick the API.
When performing custom painting, you should always call super.paintComponent to ensure that the Graphics context is setup correctly before painting. In your case, you should also make the component transparent using setOpaque and passing it false, for example...
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestFrame {
public static void main(String[] args) {
new TestFrame();
}
public TestFrame() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setUndecorated(true);
frame.setAlwaysOnTop(true);
frame.setBackground(new Color(0, 0, 0, 0));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class TestPane extends JPanel {
public TestPane() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(Color.BLUE);
g2d.setComposite(AlphaComposite.SrcOver.derive(0.5f));
g2d.fill(new Rectangle(0, 0, getWidth(), getHeight()));
g2d.dispose();
}
}
}

JScrollPane & Graphics2D

I am trying to draw graphics that is bigger than the JFrame and use JScrollPane to scroll the entire graphics. I created a simple example with two lines. The scroll bars appear but the graphics do not show.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Test extends JPanel{
public static void main(String... args) {
Test test = new Test();
JFrame frame = new JFrame();
JPanel panel = new JPanel();
panel.add(test);
JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.setHorizontalScrollBarPolicy(JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
scrollPane.setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS);
scrollPane.setBounds(0, 0, 1350, 700);
JPanel contentPane = new JPanel(null);
contentPane.setPreferredSize(new Dimension(1400, 700));
contentPane.add(scrollPane);
frame.setContentPane(contentPane);
frame.pack();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
#Override
protected void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D)g;
g2.drawLine(30,30,30,3000);
g2.drawLine(30, 400, 500, 3000);
}
}
Welcome to a wonderful example of why null layouts suck...
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
Also see Why is it frowned upon to use a null layout in SWING? for more details...
The basic problem is, the JScrollPane, has a JViewport, which actually contains your component. The JViewport uses your components sizing hints to make determinations about how big it should be and the JScrollPane uses the decisions the JViewport makes to make determinations about whether it needs to display the scrollbars or not.
The JViewport is taking a look at your component and has decided, because you've not told it otherwise, that it should be 0x0 in size.
You can prove this by adding a LineBorder to your component, setBorder(new LineBorder(Color.RED));, you won't see it either (or if you do, it will be a little red square)
Start by overriding the getPrefferedSize method of the Test panel and return some appropriate size
Next, call super.paintComponent before you perform any custom painting, otherwise you'll end up with some awesome, but annoying, paint artifacts...
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class Test extends JPanel {
public static void main(String... args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
Test test = new Test();
JFrame frame = new JFrame();
JScrollPane scrollPane = new JScrollPane(test);
frame.add(scrollPane);
frame.pack();
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.setVisible(true);
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(3000, 3000);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawLine(30, 30, 30, 3000);
g2.drawLine(30, 400, 500, 3000);
}
}
You'll probably want to take a look at the Scrollable interface next, so you can control the default size of the JViewport, so it won't try and fill the entire screen.
Take a look at Implementing a Scrolling-Savvy Client for more details
The problem comes from the lines
JPanel panel = new JPanel();
panel.add(test);
JScrollPane scrollPane = new JScrollPane(panel);
You are adding test to panel which uses FlowLayout by default. This layout does not strech the components in it, so test on which you draw has dimensions 0x0 and what you see in the scroll pane is the empty panel.
To fix this you can set panel to use BorderLayout which stretches the center component:
JPanel panel = new JPanel(new BorderLayout());
panel.add(test);
JScrollPane scrollPane = new JScrollPane(panel);
or add test directly to the scroll pane:
JScrollPane scrollPane = new JScrollPane(test);
Additionally:
Always call super.paintComponent(g) as the first line when overriding paintComponent.
Don't use null layouts (and consequently don't set bounds on components).
When you use setPreferredSize remember that if the dimensions are too large they will "flow off" the screen.

How do I change the position of JLabel or any other Component I add?

I am trying to create a translucent window which has no border or background other than the JLabel image's I put in it, using OverlayLayout and an extended JPanel...
My problem is when I try to add more components over the one I initially added which would be the background, I have no idea how to enable changing of the new components position.. x,y etc...
Please if possible show me what I can do and don't just point me to layoutmanagers, I need an example please if anyone is willing to show me.
Or better yet, show me what I need to do to my code in order to get the desired effect.. like changing "text" (A JLabel) position to be 10,10 ... x and y.
package core;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import javax.imageio.ImageIO;
import javax.swing.ImageIcon;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JWindow;
import javax.swing.OverlayLayout;
public class App {
// Window & Panel...
public JWindow frame;
public TranslucentPanel panel;
// OverlayLayout
public LayoutManager overlay;
// Components
public JLabel bg;
public JLabel test;
// Constructor
public App() {
try {
// Basics...
frame = new JWindow();
frame.setBackground(new Color(0, 0, 0, 0));
// Overlay
panel = new TranslucentPanel();
overlay = new OverlayLayout(panel);
panel.setLayout(overlay);
frame.setContentPane(panel);
// initComponents
initComponents();
// Finalize Frame
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
catch(Exception e) {
e.printStackTrace();
}
}
// Initialize Additional Components
public void initComponents() throws Exception {
test = new JLabel("test");
test.setForeground(Color.WHITE);
frame.add(test);
bg = new JLabel(new ImageIcon(ImageIO.read(getClass().getResource("/ball.png"))));
frame.add(bg);
// What must I do to be able to do this???
test.setLocation(10, 0);
}
// TranslucentPanel Class...
public class TranslucentPanel extends JPanel {
private static final long serialVersionUID = 1L;
public TranslucentPanel() {
setOpaque(false);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
}
One way would be to discard the Overlayout manager, set the TranslucentPanel's layout manager to something like BorderLayout and use the JLabel, bg as a container in of itself...
bg = new JLabel(new ImageIcon(ImageIO.read(getClass().getResource("/ball.png"))));
frame.add(bg);
// Set the layout of the JLabel
bg.setLayout(new GridBagLayout());
test = new JLabel("test");
test.setForeground(Color.WHITE);
// Add the test label to the bg JLabel...
bg.add(test);
Personally, I don't like this, as JLabel doesn't take into consideration the components (or the layout manager) when it makes it's calculations for it's preferred size.
Personally, I would create a custom background component that was responsible for painting the background image. Then, onto this, I would place the other components, using what ever combination of components and layout managers I need to produce the desired results.
Pixel perfect layouts are an illusion within modern UI design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
After reading the following pieces from your codes :
// What must I do to be able to do this???
test.setLocation(10, 0);
If I understand correctly , you want to arrange position of your component based on custom coordinates. If so then You can use Insets class http://docs.oracle.com/javase/7/docs/api/java/awt/Insets.html to achieve that.
So you can set position of your component according to position you want
Insets insets = panel.getInsets();
Dimension size =test.getPreferredSize();
// just replace 10 & 0 according to X & Y postion you want.
test.setBounds(10 + insets.left, 0 + insets.top,size.width, size.height);
Here is you modified version:
*Note that I don't have your Icon , so I just put text on your label to help you see the result.
import java.awt.*;
import javax.swing.*;
public final class App{
// Window & Panel...
public JWindow frame;
public TranslucentPanel panel;
// OverlayLayout
public LayoutManager overlay;
// Components
public JLabel bg;
public JLabel test;
// Constructor
public App() {
try {
// Basics...
frame = new JWindow();
// Overlay
// Overlay
panel = new TranslucentPanel();
overlay = new OverlayLayout(panel);
panel.setLayout(overlay);
frame.add(panel);
initComponents();
// Finalize Frame
frame.pack();
frame.setSize(400,400);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
catch(Exception e) { e.printStackTrace();}
}
// Initialize Additional Components
public void initComponents() throws Exception {
test = new JLabel("test");
test.setForeground(Color.RED);
panel.setLayout(null);
panel.add(test);
Insets insets = panel.getInsets();
Dimension size =test.getPreferredSize();
test.setBounds(10 + insets.left, 0 + insets.top,
size.width, size.height);
frame.add(panel);
}
// TranslucentPanel Class...
class TranslucentPanel extends JPanel {
private static final long serialVersionUID = 1L;
public TranslucentPanel() {
setOpaque(false);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D)g.create();
g2d.setComposite(AlphaComposite.SrcOver.derive(0.0f));
g2d.setColor(getBackground());
g2d.fillRect(0, 0, getWidth(), getHeight());
}
}
public static void main (String args []){
App ap = new App();
}
}
The output :
If you declare your position as test.setBounds(500 + insets.left, 10 + insets.top,size.width, size.height); then the output would be :

How do I make the JButtons all look the same when changing background color on some of them and not on others?

I have a JFrame that I am putting several JButtons on. Half the JButtons have color coding--i.e. I turn them blue when X event happens--and I use btn.setBackgroundColor(Color). When I use setBackgroundColor, I can see that I look the ones that are normal JButtons have shading/coloring/something that the ones with the setBackgroundColor do not. I've tried making the color transparent to a limited degree, but I still get a flat block of color, rather than a tinted version of the shaded button.
This seems like it should be a pretty easy thing to fix, but it is bugging me right now. I don't want to change the default LAF--it's fine. I don't want to abandon the color change. I do want the buttons to all appear styled (the word I'd use for HTML).
So I'm missing something right here....what is it?
Edited to add:
JFrame frame = new JFrame();
frame.add(new JButton("42"));
JButton btn24 = new JButton("24");
btn24.setBackground(Color.red);
frame.add(btn24);
frame.setVisible(true);
In the above example, "42" will--on my Windows machine--show a slight color variation at the bottom and the top, creating a rounded and shaded effect. The "24" button will show a red square. My question is: Is there a way to make "24" show the rounded/shaded/styled with the red tint on top? Or do I need to simple make all my buttons flat squares for a uniform appearance?
Thanks!
Create a custom JButton and override the paint method as illustrated bellow :
import static javax.swing.JFrame.EXIT_ON_CLOSE;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.GradientPaint;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) {
JButton btn24 = new DepthButton("24");
JButton btn25 = new DepthButton("25");
btn24.setBackground(Color.red);
btn25.setBackground(Color.GREEN);
JPanel pane = new JPanel(new BorderLayout());
pane.add(new JButton("42"), BorderLayout.PAGE_START);
pane.add(btn24, BorderLayout.PAGE_END);
pane.add(btn25, BorderLayout.CENTER);
frame.add(pane);
frame.pack();
frame.setDefaultCloseOperation(EXIT_ON_CLOSE);
frame.setVisible(true);
}
/**
*
* #author Romain Guy
*/
public static class DepthButton extends JButton {
/** Creates a new instance of DepthButton */
public DepthButton(String text) {
super(text);
setContentAreaFilled(false);
}
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
GradientPaint p;
p = new GradientPaint(0, 0, new Color(0xFFFFFF), 0, getHeight(), getBackground());
Paint oldPaint = g2.getPaint();
g2.setPaint(p);
g2.fillRect(0, 0, getWidth(), getHeight());
g2.setPaint(oldPaint);
super.paintComponent(g);
}
}
}
And Here is the Result:
The example is from an excellent book for advanced java swing : Filthy Rich Clients
https://github.com/romainguy/filthy-rich-clients/blob/master/Gradients/TwoStopsGradient/src/DepthButton.java

Categories

Resources