In the code below, I create a JFrame, set the size to 800 by 800 pixels, set the layout to BorderLayout, then add a JPanel.
In the JPanel, I use paintComponent to draw a 20x20 pixel image in 7 locations.
At position 0-0, the icon appears in the upper right corner.
The icon is 20 pixels high, and the frame and panel are both 800 pixels high, so drawing the icon at x-780 should align the icon with the bottom of the window. But the icond doesn't even appear.
The remaining icons are drawn a x-770, x-760, x-758, and x-750. x-758 appears aligned with the bottom of the window. So I conclude that x[0] on the JPanel start at the JFrame's x[42]
I think I've set the BorderLayout correctly. I do call setSize() in the JPanel's constructor. I thought setting an explicit size might screw it up. But after commenting that line out, the program shows the same behavior.
Can you show me what I'm doing wrong?
Frametest.java
package frametest;
import java.awt.BorderLayout;
import javax.swing.JFrame;
public class FrameTest extends JFrame{
public static final int HEIGHT = 800;
public static final int WIDTH = 800;
public FrameTest(){
setLayout(new BorderLayout());
add(new Panel(WIDTH, HEIGHT), BorderLayout.CENTER);
pack();
setTitle("Frame Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(WIDTH, HEIGHT);
setResizable(false);
setLocationRelativeTo(null);
setVisible(true);
}
public static void main(String[] args) {
new FrameTest();
}
}
Panel.java
package frametest;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
public class Panel extends JPanel{
private int height;
private int width;
Image icon1 = new ImageIcon(this.getClass().getResource("icon1.png")).getImage(); //Note that the png file is 20 x 20 pixels.
public Panel(int width, int hieght){
setBackground(Color.BLUE);
//setSize(width, hieght);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(icon1, 0, 0, this); //appears in the upper-right corner
g2d.drawImage(icon1, 20, 780, this); //does not appear
g2d.drawImage(icon1, 40, 770, this); //appears with bottom pixels cut off
g2d.drawImage(icon1, 70, 760, this); //appears with the bottom pixels cut off
g2d.drawImage(icon1, 100, 758, this); //appears aligned with bottom of the window
g2d.drawImage(icon1, 130, 750, this); //appears slightly above the bottom of the window
g2d.drawImage(icon1, 780, 20, this); //appears aligned with the right side of the screen.
}
}
You are setting the size of the JFrame. This includes the Frame Decoration, the Menu Bar (if you have one), and so on. If you want your JPanel to have the Dimensions 800 x 800, set the preferred Size of your JPanel and use JFrame.pack().
To do so, remove the following line from Frametest.java:
setSize(WIDTH, HEIGHT);
Then, in Panel.java, change this line from
//setSize(width, hieght);
to:
setPreferredSize(new Dimension(800, 800));
Instead of this, you can also overwrite getPreferredSize():
public Dimension getPreferredSize ()
{
return new Dimension(800, 800);
}
Use frame.pack() (as you are already using) instead of frame.setSize() that fits the components as per component's preferred size. Just remove setSize() calls.
Override getPreferredSize() to set the preferred size of the JPanel in case of custom painting.
sample code:
class Panel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
}
#Override
public Dimension getPreferredSize() {
return new Dimension(..., ...);
}
}
It sounds like the problem is because of the border around the JFrame. Things like the menu bar at the top will take away some space from the available space to display things in the JFrame.
Try adding getPreferredSize() to your JPanel, and calling pack() on your JFrame.
Related
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);
I'm new to Java and I'm playing around with a simple GUI example:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Rectangle2D;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.WindowConstants;
public class DrawTest {
class DrawingPanel extends JPanel {
private Rectangle2D shape;
public DrawingPanel(Rectangle2D shape) {
this.shape = shape;
}
public void paintComponent(Graphics g) {
Graphics2D g2D = (Graphics2D) g;
super.paintComponent(g2D);
g2D.setColor(new Color(31, 21, 1));
g2D.fill(shape);
}
}
public void draw() {
JFrame frame = new JFrame();
Rectangle2D shape = new Rectangle2D.Float();
final DrawingPanel drawing = new DrawingPanel(shape);
shape.setRect(0, 0, 400, 400);
frame.getContentPane().add(BorderLayout.NORTH, new JButton("TestN"));
frame.getContentPane().add(BorderLayout.SOUTH, new JButton("TestS"));
frame.getContentPane().add(BorderLayout.EAST, new JButton("TestE"));
frame.getContentPane().add(BorderLayout.WEST, new JButton("TestW"));
frame.getContentPane().add(BorderLayout.CENTER, drawing);
frame.pack();
frame.setSize(500,500);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
public class DrawMain {
public static void main(String[] args) {
DrawTest test = new DrawTest();
test.draw();
}
}
As expected, this code produces a frame with the rectangle at the centre and buttons around it. However, if I change the code like this:
frame.getContentPane().add(BorderLayout.NORTH, drawing);
frame.getContentPane().add(BorderLayout.SOUTH, new JButton("TestS"));
frame.getContentPane().add(BorderLayout.EAST, new JButton("TestE"));
frame.getContentPane().add(BorderLayout.WEST, new JButton("TestW"));
frame.getContentPane().add(BorderLayout.CENTER, new JButton("TestC"));
the "TestC" button gets a huge area in the middle while the rectangle doesn't get enough space. This is even true if I remove the other buttons (TestS, TestE, TestW): I get a huge TestC button and a tiny part of the rectangle (even not the scaled rectangle) at the top.
Why doesn't the rectangle get enough space when it's drawn at the top (NORTH) but does get it when it's drawn at the CENTER?
The DrawingPanel should #Override getPreferredSize() to return an appropriate size.
The layout manager will then take that preferred size as a hint. Some layout managers will expand a component's height or width according to the logic of the layout and constraint. E.G. a BorderLayout will stretch components in the PAGE_START / PAGE_END to the width of the content pane, and LINE_START / LINE_END to the height of the tallest of either of those, or the CENTER. A GridBagLayout OTOH will completely hide / remove a component for which there is not enough space to display it at the preferred size, and that's where 'pack' comes in.
So change frame.setSize(500,500); (which is no better than a guess) to frame.pack();, which will make frame the minimum size it needs to be, in order to display the components it contains.
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 :
having a problem with top insets not giving the proper value when painting ... i am looking for y to be just under the title bar but it is not working correctly and putting it 25 pixels from the top where as the title bar only accounts for about 14 pixels .
Game Frame :
import java.awt.Dimension;
import javax.swing.JFrame;
public class gameFrame extends JFrame {
static int width = 400;
static int height = 400;
static int topinset;
static int FPS = 30;
public gameFrame(){
gamePanel gamePanel = new gamePanel();
gamePanel.setPreferredSize(new Dimension(width,height));
Thread thread = new Thread(gamePanel);
this.add(gamePanel);
this.setResizable(false);
this.setSize(width, height);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
this.setVisible(true);
topinset = this.getInsets().top;
thread.start();
}
public static void main(String[] args){
gameFrame gameFrame = new gameFrame();
}
}
Game Panel :
import java.awt.Graphics;
import javax.swing.JPanel;
public class gamePanel extends JPanel implements Runnable {
public gamePanel(){
this.setBounds(0,0,gameFrame.width,gameFrame.height);
this.setVisible(true);
}
#Override
protected void paintComponent(Graphics g){
super.paintComponent(g);
g.drawString("Width: "+ gameFrame.width + " Height: " + gameFrame.height, 0, gameFrame.topinset);
}
#Override
public void run() {
while(true){
repaint();
try {Thread.sleep(1000*gameFrame.FPS);}
catch (InterruptedException e) {}
}
}
}
You are doing your custom painting on the panel so there is no need to worry about the offsets of the frame. Just use the getWidth() and getHeight() methods to get the current size of the panel.
If you want your panel to be (400, 400), then override the getPreferredSize() method of your panel class to return that dimension. Then use frame.pack() and the frame will size itself properly to include the panel size and the frame decorations.
There is no need for static variables. Those variables should just be instance variables or constants.
There is no need for the setBounds() since the layout manager will determine the size/location of the panel in the frame.
There is no need to make the panel visible since is it visible by default.
Follow Java naming conventions. Class names should start with an upper case character.
Why are you doing custom painting? Why not just add a JLabel to the NORTH of the frame and display your text in the label?
Start by taking a look through Working with Text APIs
Text rendering is typically done from the base line, meaning text will painted above the y position you specify
As has already being stated, a components Graphics context is translated before it's painted so that the 0x0 position will be the top/left corner of the component
In the following example, the first String will not appear on the screen, as it's base line is along the top edge of the component, whereas the second String has being adjusted so that it's ascent line is along the top top
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics g2d = (Graphics2D) g.create();
FontMetrics fm = g2d.getFontMetrics();
g2d.drawString("Hello", 0, 0);
g2d.drawString("Hello", fm.stringWidth("Hello"), fm.getAscent());
g2d.dispose();
}
The idea of the program is that I have some buttons and an icon SOMEWHERE on the frame. I want the buttons to change the color. I'm only worried about making all the elements show up right now. If I comment out lines 11-13, I see "hello," printed out, with a red circle on top of it. Otherwise, I just have the button "red" without "hello" or my red circle. So here's my code:
import javax.swing.*;
import java.awt.*;
import java.awt.geom.*;
public class ButtonTester
{
public static void main (String[] args)
{
JFrame frame = new ButtonFrame();
frame.setLayout(new FlowLayout(FlowLayout.RIGHT));
JButton redButton = new JButton("Red");
frame.add(redButton);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class ButtonFrame extends JFrame
{
public static final int DEFAULT_WIDTH = 300;
public static final int DEFAULT_HEIGHT = 200;
public ButtonFrame()
{
setTitle("Hello");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
ButtonPanel panel = new ButtonPanel();
add(panel);
}
}
class ButtonPanel extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawString("Hello !", 100, 100);
Icon ico = new ColorIcon(32);
ico.paintIcon(null, g, 75, 75);
}
}
I'm 90% sure the problem is lines 11-13, but I'm not sure what to change to make everything visible.
Your problem is that your ButtonPanel's size is 0. Have it override getPreferredSize() and you will see what I mean:
class ButtonPanel extends JPanel {
private static final int PREF_W = 150;
private static final int PREF_H = PREF_W;
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.drawString("Hello !", 100, 100);
// !! Icon ico = new ColorIcon(32);
// Icon ico = new ImageIcon();
// ico.paintIcon(null, g, 75, 75);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
Also as an unrelated aside, why are you creating an Icon inside of the paintComponent method? This doesn't make sense to me and would only serve to needlessly slow your graphics down.
Edit
You state:
Ok, I see the difference after overriding getPreferredSize() But what would be the "better" or "correct" way to create the icon? I'm just trying to follow the directions for an exercise out of a Java textbook: Exercise 4.14. Write a program that shows a frame with three buttons labeled "Red", "Green", and "Blue", and a label containing an icon showing a circle that is initially red. As the user clicks the buttons, the fill color of the circle should change. When you change the color, you need to invoke the repaint method on the label. The call to repaint ensures that the paintIcon method is called so that the icon can be repainted with the new color.
You need to think on this a different way. Myself I'd create three ImageIcons one for a blue circle, one for red, and one for green. I'd then display the ImageIcon in a JLabel on my JFrame. I'd change the color by simply swapping the label's icons via its setIcon(...) method. I wouldn't worry about futzing with paintComponent(...) but rather would try to solve this in as simple a fashion as possible.