I have a JScrollPane displaying (as its viewport view) MyPanel, a subclass of JPanel.
MyPanel implements custom painting by overloading paintComponent. The total size of the displayable content of MyPanel is generally quite wide (meaning 50x to 200x wider than the size of the JScrollPane viewport) and using a Timer, I scroll horizontally to view different sections of the underlying MyPanel. I also allow using the scroll bar thumb to manually seek to a specific area of MyPanel.
In my paintComponent implementation I am currently finding the portion of MyPanel that is currently visible in the view port using JViewport#getVisibleRect, and just painting that portion each time the view port position is changed.
This works fine - but I end up repainting a significant percentage of the visible portion of MyPanel over and over as the timed scrolling only moves the view port 1/50 of the view port width at a time. Also, I generally end up scrolling through the entire horizontal extent of MyPanel, so I have to paint it all at least once anyway.
That leads me to think about painting the entire contents of MyPanel just once (to a BufferedImage?) and then letting JScrollPane (or JViewport) handle clipping and blitting only the needed area of the BufferedImage.
Intuitively this seems to me to be the most efficient way of handling this, and something that would be relatively common.
As I investigate the Swing tutorials and other sources, I learn that Swing is already double buffered. If I try to force this on my own brute-force, independent of Swing functionality, it sounds like I'll end up with triple-buffering.
I haven't found the recipe (if it exists) to exploit JScrollPane to do this for me.
Is there an example available, or some direction as to how to do this (if possible)?
Swing automatically paints only the smallest necessary area of components for you. repaint(4 args) is only useful when the component is partially changed and you don't want the entire visible area to be repainted. And in your practical, it has the same effect as repaint(no-args).
The auto-clipped area is already small enough for visibility concerns as you described in your question. And you can configure it in your program.
Also, you don't need to worry about the scrolling -- JScrollPane invokes repaint of its children automatically.
You can easily experiment on these:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class Test extends JFrame {
private Random rnd = new Random();
private Color c = Color.WHITE;
public Test () {
final JPanel pnl = new JPanel() {
#Override
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.setColor(c);
g.fillRect(0, 0, getWidth(), getHeight());
Rectangle r = g.getClipBounds();
System.out.println(r.width + ", " + r.height);
}
};
pnl.setPreferredSize(new Dimension(10000, 10000));
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(400, 400);
setLocationRelativeTo(null);
add(new JScrollPane(pnl));
setVisible(true);
new Thread() {
#Override
public void run () {
while (true) {
c = new Color(rnd.nextInt(0xffffff));
pnl.repaint();
try {
Thread.sleep(100);
} catch (InterruptedException e) {}
}
}
}.start();
}
public static void main (String args[]) {
new Test();
}
}
Related
Background: Making a game in Swing. It is simple turn base game. Not a whole lot going on. Because of this I didn't think I would need to implement a Game Tick. Rather, my thought was when a component got changed or needed to be updated just simply revalidate/repaint that component on the fly rather than repainting the whole screen.
I have a GameJPanel which currently has all the components on it. This JPanel is the one that contains the components that get revalidated/repainted etc.
I figured I could make JLayeredPane that holds GameJPanel and my OptionJPanel. Have a Button on GameJPanel that when pressed causes the OptionJPanel to show on top of it and having its JPanel 50% transparent (so it gives the affect it dims the GameJPanel).
However, once I did this what happened was that the GameJPanel started to replace OptionJPanel components (because of the events... etc; repainting of the components).
So currently I am at a loss on what to do. I'm thinking if I had some sort of game tick I wouldn't be having this issue, however, I am not 100% certain. I'm a little worried if I implemented a gametick that the events in game will cause the GameJPanel components to show through for half a second then get replaced. There are some events that cause components to repaint themselves without manually doing it (like quick example for JLabel setText();)
As an example of what I'm trying to go for.
I have tried with a CardLayout but I couldn't figure out how to have the OptionJPanel be on top of GameJPanel while seeing GameJPanel in the background (I tried setting background color, setOpaque(false)..., tried to limit Option JPanel size but I think the CardLayout stretches it (not 100% sure)) all I got was a gray background when doing so.
I would prefer not to go the CardLayout route because in the future I also plan on placing components on top of the GameJPanel (like someone clicks a button, have another panel on a different layer have a component slide in or out etc).
I use CardLayout a ton with my other components in GameJPanel to swap screens around, but haven't had the need to have the other components behind the one showing to show through.
Any ideas on how to go about this would be great or even example code that shows this.
As noted above, you would use a JDialog, a component that is easy to make (similar to making a JFrame) and easy to place. Simply place it "relative-to" the JFrame, e.g.,
myDialog.setLocationRelativeTo(myJFrame);
... and it will automatically center itself on the JFrame. The tricky part is dimming the underlying JFrame, and for this you would need to use a JGlassPane added to the JFrame's rootpane, one set with a background color that uses an alpha composite value. The tricky part with this is to draw the darker background without causing side effects, and to do this, please read Rob Camick's (StackOverflow user camickr) excellent tutorial on drawing in Swing with alpha composites which you can find here: Java Tips Weblog: Backgrounds with Transparency
An example of such a program is shown here:
import java.awt.Color;
import java.awt.Component;
import java.awt.Dialog.ModalityType;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.GridLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import javax.imageio.ImageIO;
import javax.swing.*;
public class DialogEg {
// path to example image used as "game" background
private static final String IMG_PATH = "https://upload.wikimedia.org/"
+ "wikipedia/commons/7/76/Jump_%27n_Bump.png";
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
private static void createAndShowGui() {
// get the "game" background image, or exit if fail
BufferedImage img = null;
try {
URL imgUrl = new URL(IMG_PATH);
img = ImageIO.read(imgUrl);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
// pass "game" image into main JPanel so that it will be drawn
DeMainPanel mainPanel = new DeMainPanel(img);
JFrame frame = new JFrame("Dialog Example");
frame.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
frame.add(mainPanel); // add main JPanel to JFrame
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
}
// main JPanel
#SuppressWarnings("serial")
class DeMainPanel extends JPanel {
private BufferedImage img; // background image
// JButton action that shows the JDialog and darkens the glasspane
private PauseAction pauseAction = new PauseAction("Pause");
public DeMainPanel(BufferedImage img) {
super();
this.img = img;
add(new JButton(pauseAction));
}
// draw the "game" background image within the JPanel if not null
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (img != null) {
g.drawImage(img, 0, 0, this);
}
}
// size this JPanel to match the image's size
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet() || img == null) {
return super.getPreferredSize();
}
int width = img.getWidth();
int height = img.getHeight();
return new Dimension(width, height);
}
}
// Action / ActionListener for JButton -- shows JDialog and darkens glasspane
#SuppressWarnings("serial")
class PauseAction extends AbstractAction {
private static final int ALPHA = 175; // how much see-thru. 0 to 255
private static final Color GP_BG = new Color(0, 0, 0, ALPHA);
private DeDialogPanel deDialogPanel = new DeDialogPanel(); // jpanel shown in JDialog
public PauseAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
// comp is our JButton
Component comp = (Component) e.getSource();
if (comp == null) {
return;
}
// create our glass pane
JPanel glassPane = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
// magic to make it dark without side-effects
g.setColor(getBackground());
g.fillRect(0, 0, getWidth(), getHeight());
super.paintComponent(g);
}
};
// more magic below
glassPane.setOpaque(false);
glassPane.setBackground(GP_BG);
// get the rootpane container, here the JFrame, that holds the JButton
RootPaneContainer win = (RootPaneContainer) SwingUtilities.getWindowAncestor(comp);
win.setGlassPane(glassPane); // set the glass pane
glassPane.setVisible(true); // and show the glass pane
// create a *modal* JDialog
JDialog dialog = new JDialog((Window)win, "", ModalityType.APPLICATION_MODAL);
dialog.getContentPane().add(deDialogPanel); // add its JPanel to it
dialog.setUndecorated(true); // give it no borders (if desired)
dialog.pack(); // size it
dialog.setLocationRelativeTo((Window) win); // ** Center it over the JFrame **
dialog.setVisible(true); // display it, pausing the GUI below it
// at this point the dialog is no longer visible, so get rid of glass pane
glassPane.setVisible(false);
}
}
// JPanel shown in the modal JDialog above
#SuppressWarnings("serial")
class DeDialogPanel extends JPanel {
private static final Color BG = new Color(123, 63, 0);
public DeDialogPanel() {
JLabel pausedLabel = new JLabel("PAUSED");
pausedLabel.setForeground(Color.ORANGE);
JPanel pausedPanel = new JPanel();
pausedPanel.setOpaque(false);
pausedPanel.add(pausedLabel);
setBackground(BG);
int eb = 15;
setBorder(BorderFactory.createEmptyBorder(eb, eb, eb, eb));
setLayout(new GridLayout(0, 1, 10, 10));
add(pausedPanel);
add(new JButton(new FooAction("RESUME")));
add(new JButton(new FooAction("RESTART")));
add(new JButton(new FooAction("EXIT TO MAP")));
}
// simple action -- all it does is to make the dialog no longer visible
private class FooAction extends AbstractAction {
public FooAction(String name) {
super(name);
}
#Override
public void actionPerformed(ActionEvent e) {
Component comp = (Component) e.getSource();
Window win = SwingUtilities.getWindowAncestor(comp);
win.dispose(); // here -- dispose of the JDialog
}
}
}
The GUI looks like this initially:
but then when the dialog shows and the glass pane is darkened, it looks like this:
So after about a month of working on my game I was drawn to this post once again. I implemented part of my game with what DontKnowMuchButGettingBetter's way and also implemented this by just adding the components to the GlassPane so to speak (Made a JPanel, set it to be GlassPane, did whatever on that Panel)...
The later implementation (GlassPane), isn't the best way to go about this because then you can't use the glass pane for other useful things.
I came back to my original idea to use a JLayeredPane. Having different Components on different levels and working off that. My issue before was that when components were getting repainted, the components in the backer layers were over painting the ones in the front layer.
Well I just came across a method called isOptimizedDrawingEnabled()... By making this method always return false for the JLayeredPane I was able to achieve what I wanted.
I have 2 issues.
First issue: I have to set the JFrame as non resizable, however, an error is thrown up when I enter in frame.setResizable(false);
Second issue: I have ran into the problem of the JFrame not fitting the component within it perfectly. I have set the dimensions for the JFrame to 600x720 and the board component as being 600x600. However, when I extend the JFrame I can see that there is more of the component to be revealed. How would I change this to accommodate the component to fit snugly but also leave space for another component of size 100x120? My understanding of this is that JFrame sets the size with the borders included, however, I want the space within the JFrame to be exactly 600x720 pixels without including the border.
The code is set out below.
Game Class
package snake;
//This class is used to run the game.
public class Game {
/**
* #author HyperBlue
*/
//Declaring a static variable of type Board. This can be accessed from anywhere in the program. The fact that it is static means that it cannot be edited.
public static Board board;
public static void main(String[] args) {
// TODO Auto-generated method stub
//Creates an object board from the Board() construct
board = new Board();
}
}
Board Class
package snake;
//Importing allows us to use pre-defined classes in Java, this contains its methods. We can also import entire packages which contain a number of classes in that package.
//This class allows us to assign/capture the width and height of an object.
import java.awt.Dimension;
//The Toolkit is an abstract class containing abstract and (possibly) non-abstract methods. Abstract classes cannot be instantiated (i.e. we cannot make an object from them). Abstract methods have no body (no code), for example we declare it as "public abstract boolean isChanged() ;", the semi colon shows it has no body (i.e. no {}).
import java.awt.Toolkit;
//ActionEvent gets information about an event (input) and its source. You can create an object from this.
import java.awt.event.ActionEvent;
//The ActionListener defines what should be done when a certain action is performed by the user.
import java.awt.event.ActionListener;
//This imports the JFrame class from the swing package.
import javax.swing.JFrame;
import javax.swing.Timer;
//This class is used to create the game board.
//The ActionListener is implemented because it is implementing an interface. What ActionListener does is it handles events; the ActionListener defines what should be done when a certain action is performed by the user.
public class Board implements ActionListener {
//The JFrame is the window in which everything will be placed into, this will provide the framed window (what is visible to us) in which the game will run in. We are creating a variable frame of type JFrame.
public JFrame frame;
//Creating a variable drawBoard of type DrawBoard. This allows us to add the component of drawBoard to the Board.
public DrawBoard drawBoard;
//Defining a new Timer called ticker. This is using the form new Timer(int delay in milliseconds, ActionListener listener). What the timer does is it allows threads to schedule the execution of instructions. In this case to constantly refresh the drawBoard component at regular intervals. This will give the appearance of motion. What "this" does is it is in reference to the current instance,
public Timer ticker = new Timer(20, this);
//This is a constructor for the class Board. This will allow us to create an object.
public Board() {
//Making an instance of dimension dim and assigning it to the size of the screen.
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
//Declaring instance of the JFrame 'frame'. This JFrame is called to declare a title for this frame - "Snake".
frame = new JFrame("Snake");
//JFrame is initially set to invisible, so we use the setVisible method (setting it to true) to make the JFrame 'frame' visible.
frame.setVisible(true);
frame.setPreferredSize(new Dimension(600, 720));
frame.getContentPane().add(drawBoard = new DrawBoard());
frame.pack();
//What this does is it places the JFrame 'frame' into the middle of the user's screen, this diminishes the issue of not all screens being the same resolution and size. This is done by setting the (x, y) position of the JPanel. For example, the x position is gained by dividing the size of the monitor by 2 and negating the size of the JPanel by 2 from that value, this places it in the middle of the screen's x axis. This is true for the y-axis too.
frame.setLocation((dim.width / 2) - (frame.getWidth() / 2), (dim.height / 2) - (frame.getHeight() / 2));
//Sets the operation which will happen when the user closes the JFrame 'frame', the EXIT_ON_CLOSE exits the application using the System exit method. This means that when the JFrame is closed, the application will be exited (closed).
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//Starts the timer, this starts is sending action events to its listeners.
ticker.start();
}
//Overriding the actionPerformed method from the ActionListener class.
#Override
//This is the actionPerformed method. We parse it the ActionEvent e, what this is is an object which gives information about the event and its source. This allows us to perform an action based upon a specific event (e.g. a keyboard key pressed).
public void actionPerformed(ActionEvent e) {
//This repaints this component for every tick
//drawBoard.repaint();
}
}
DrawBoard Class
package snake;
//Allows use of default sRGB colours.
import java.awt.Color;
//Graphics is an abstract class that allows us to draw onto components.
import java.awt.Graphics;
import javax.swing.JPanel;
//Warnings will not be thrown (are suppressed).
#SuppressWarnings("serial")
//This class is used to create the board component in which the snake can move on.
//What extending does is it allows us to inherit the methods and attributes (properties) of another class. In this case, the DrawBoard class (subclass - inherits state and behaviour from all of its ancestors) inherits properties from the JPanel class (superclass - gives properties to its subclasses).
public class DrawBoard extends JPanel{
//Declaring the colour 'yellow' as the hex colour code (turned to decimal using a hex calculator so Java can use it) which was chosen in the design stage.
public static Color yellow = new Color(13816442);
//We are overriding the protected method in order to define our own body (and properties) for the paintComponent method. Overriding this allows us to define how we will paint the component DrawBoard. Protected means that it can only be accessed by things within the same package.
#Override
//A component is an object which has a graphical representation that can interact with the user (e.g. buttons).
//What this does is it paints the component using the graphics class, defined as instance g.
protected void paintComponent(Graphics g) {
//'Super.' refers to the method calling its super class, which in this case is JPanel. Doing this allows me to use in built 'drawings' such as rectangle and oval, which can be drawn by calling their methods.
super.paintComponent(g);
//Setting the colour in which graphics objects are made to the colour defined in the colour 'yellow'
g.setColor(yellow);
//Filling in a rectangle which starts at the point (0, 120) - [this is from the top left of the screen, with (0, 120) referring to 120 pixels down] and has a width and height of (600, 600), in other words provides a background of colour 'yellow' defined.
g.fillRect(0, 120, 600, 600);
}
}
Regarding:
How to Get JPanel to Fit Snugly With It's Components?
Let the layout managers do this work for you.
Suggestions:
Don't set sizes or preferred sizes.
Instead let the component's preferred size and your layout managers do the sizing for you.
If you do need to actively have a hand in setting some sizes, override getPreferredSize() and return an appropriate dimension, but do so taking care not to upset the preferred size of the constituent components. This must be done with care.
Re " I have set the dimensions for the JFrame to 600x720 and the board component as being 600x600. However, when I extend the JFrame I can see that there is more of the component to be revealed." -- You're forgetting the size of the top bar of the JFrame, something that may change size depending on the look and feel. Again, don't set the JFrame's size, and this will be a moot point.
To center a JFrame, simply call frame.setLocationRelativeTo(null); after it has been packed.
Avoid over-use of comments as these make your code nearly unreadable.
If you have problems and need help with an error such as you mention here: "I have to set the JFrame as non resizable, however, an error is thrown up when I enter in frame.setResizable(false);", then show the offending code and the error message.
Don't call frame.setVisible(true); until all components have been added and the JFrame has been packed.
For example
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.*;
import javax.swing.*;
public class Game {
public static Board board;
public static void main(String[] args) {
board = new Board();
}
}
class Board implements ActionListener {
public JFrame frame;
public DrawBoard drawBoard;
public Timer ticker = new Timer(20, this);
public Board() {
frame = new JFrame("Snake");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setPreferredSize(new Dimension(600, 720));
frame.getContentPane().add(drawBoard = new DrawBoard(), BorderLayout.CENTER);
frame.getContentPane().add(new BottomComponent(), BorderLayout.PAGE_END);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
ticker.start();
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
class DrawBoard extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = PREF_W;
public static Color yellow = new Color(13816442);
public DrawBoard() {
setBorder(BorderFactory.createTitledBorder("Draw Board"));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(yellow);
g.fillRect(0, 120, 600, 600);
}
}
class BottomComponent extends JPanel {
private static final int PREF_W = 600;
private static final int PREF_H = 120;
public BottomComponent() {
setBorder(BorderFactory.createTitledBorder("Bottom Component"));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
}
I want to put a JPanel in a JApplet, the problem is that I can't see it :( I've overridden the paintComponent of my JPanel in order to have a background image, but I can't see anything. When I remove the paintComponenet method that I had overriden, and set a color to the background of this panel, it seems that JPanel fills the JApplet and still no component is visible :-S I've tried different layouts. I also put my panel in the center of another panel which fills my JApplet but nothing changed, and still no component and no background image is visible :(
import java.awt.BorderLayout;
import java.awt.Graphics;
import javax.swing.ImageIcon;
import javax.swing.JApplet;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
public class Main extends JApplet implements Runnable{
private JTextArea display;
private Thread outputThread;
JPanel boardPanel;
private ClientViewManager view;
#Override
public void init() {
try {
javax.swing.SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
createGUI();
}
});
} catch (Exception e) {
System.err.println("createGUI didn't successfully complete");
}
}
private void createGUI() {
display = new JTextArea(4, 30);
display.setEditable(false);
getContentPane().add(new JScrollPane(display), BorderLayout.SOUTH);
setFocusable(true);
setVisible(true);
setName("CE Tanks");
setSize(600, 600);
setLocation(100, 100);
boardPanel = new JPanel();
boardPanel.setLayout(null);
boardPanel.setBackground(new java.awt.Color(128, 255, 255));
getContentPane().add(boardPanel, BorderLayout.CENTER);
}
public void start() {
outputThread = new Thread(this);
outputThread.start();
}
public void run() {
view = new ClientViewManager();
boardPanel.add(view);
boardPanel.repaint();
repaint();
}
}
class ClientViewManager extends JPanel {
private int rows=8;
private int columns=8;
public ClientViewManager() {
super(null);
JLabel lb= new JLabel("lb.jpg");
lb.setLocation(10, 10);
lb.setSize(50, 50);
lb.setOpaque(false);
lb.setVisible(true);
this.add(lb);
}
public void paintComponent(Graphics g) {
g.drawImage(new ImageIcon("ground.jpg").getImage(), 0, 0, columns * 50,
rows * 50, this);
}
}
The code above can be compiled. I cant even add Keylistener to neither my JPanel nor to my JApplet. I used java.awt.KeyEventDispatcher and in dispatchKeyEvent(KeyEvent e) I printed something in console but, it was printed 3 times. :(
I've overridden the paintComponent of my JPanel inorder to have a background image,
But you didn't add the custom component to your applet:
//boardPanel = new JPanel();
boardPanel = new ClientViewManager();
Also:
get rid of setVisible(). This is not required for any of the controls in your program. By default all components except top level Container (Jframe, JDialog etc) are already visible. In the case of JApplet, you don't need to make it visible as this is part of the process of displaying an applet.
get rid of setSize() and setLocation() you can't control the position of the applet this way.
Don't read the image file in the paintComponent() method. This is not efficient as this method is invoked whenever Swing determines the component needs to be repainted.
JLabels are opaque by default so there is not need to invoke the setOpaque method.
When doing custom painting you should also override the getPreferredSize() method of the component to return the proper size of the custom painting so layout managers can use this information. It works in this case because you added the panel to the CENTER of the BorderLayout. Try adding the panel to the NORTH to see what happens.
Edit:
Now I see where you are adding the ClientViewManager. I'm not sure why you are trying to do this with a Thread but once again there are several problems.
When you add/remove components from a visble GUI then the basic code is:
panel.add(...);
panel.revalidate();
panel.repaint();
However this still won't work because you are using a null layout and the size of the panel is 0. Use a proper layout manager and implement the getPreferredSize() method as suggest above and the component will be displayed properly.
I recommend you to use the GUI Builder of Netbeans to build a GUI like that, and then compare the generated code to your code. Netbeans results really useful to help you create swing code.
i found this link.. LINK what i want is there's a JPanel that has a background and another JPanel with half the size of the first JPanel but with an image that is transparent and with a face or a ball at the middle.. :) just like the screenshot from the link.. is that possible to code in java? :) im just thinking it like for web programming. just a sort of DIV's to have that but i dont know in java.. :) sorry for bad english.. :D i have this as a background..
package waterKing;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
#SuppressWarnings("serial")
public class Main extends JFrame {
MainData data = new MainData();
public static void main(String[] args) {
Main frmMain = new Main();
frmMain.setExtendedState(Frame.MAXIMIZED_BOTH);
frmMain.setVisible(true);
}
public Main() {
data.tk = getToolkit();
data.d = data.tk.getScreenSize();
data.jP = new JPanel() {
protected void paintComponent(Graphics g) {
data.e = getSize();
data.iI = new ImageIcon("images/mainBG.png").getImage();
g.drawImage(data.iI,0, 0, data.d.width, data.d.height, null);
super.paintComponent(g);
}
};
data.jP.setOpaque(false);
data.jSp = new JScrollPane(data.jP);
data.jB = new JButton("EXIT");
data.jB.setBounds(10,10,200,40);
data.jB.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
data.jP.setLayout(null);
data.jP.add(data.jB);
this.setTitle("Water King Inventory System");
this.setUndecorated(true);
this.getContentPane();
this.add(data.jSp);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
}
}
i dont know how to add another JPanel to show in the middle with this background
i dont know how to add another JPanel to show in the middle with this background
Its just like adding components to a panel. You need to use a layout manager and then the component will be positioned properly based on the rules of the layout manager. In your case you can set the layout manager of the background panel to be a BorderLayout. Then you can add a JLabel with the appropriate Icon to the center of the BorderLayout.
You will need to set the preferred size (or override the getPreferredSize() method of your panel since you add it to a scroll pane. Scrollbars will only appear when the preferred size of the panel is greater than the size of the scroll pane.
You should not be reading the image in your paintComponent() method since this method is called multiple times.
You should not be using the "screen size" to determine the width/height of the image because the frame will contain a border. You need to use the size of the panel.
Get rid of all the setBounds() code. Learn to use layout managers.
For a general purpose background panel that takes into account most of the suggestions made here check out Background Panel.
In Java2D when you use setOpaque I am a little confused on what the true and false does.
For example I know that in Swing Opaque means that when painting Swing wont paint what is behind the component. Or is this backwards? Which one is it?
Thanks
The short answer to your question is that "opaque" is defined in English as completely non-transparent. Therefore an opaque component is one which paints its entire rectangle, and every pixel is not at all translucent to any degree.
However, the Swing component opacity API is one of those mis-designed and therefore often mis-used APIs.
What's important to understand is that isOpaque is a contract between the Swing system and a particular component. If it returns true, the component guarantees to non-translucently paint every pixel of its rectangular area. This API should have been abstract to force all component writers to consider it. The isOpaque API is used by Swing's painting system to determine whether the area covered by a given component must be painted for components which overlap it and which are behind it, including the component's container and ancestors. If a component returns true to this API, the Swing system may optimize painting to not paint anything in that area until invoking the specific component's paint method.
Because of contractual implication of isOpaque, the API setOpaque should not exist, since it is actually incorrect for anything external to call setOpaque since, in turn, the external thing can't know whether the component in question will (or even can) honor it. Instead, isOpaque should have been overridden by each concrete component to return whether it actually is, in fact, opaque given its current properties.
Because the setOpaque API does exist, many components have mis-implemented it (quite understandably) to drive whether or not they will paint their "background" (for example JLabel and JPanel filling with their background color). The effect of this is to create an impression with users of the API to think that setOpaque drives whether or not that background should paint, but it doesn't.
Furthermore, if, say, you wish to paint a JLabel with a translucent background you need to set a background color with an alpha value, and do setOpaque(true), but it's not actually opaque - it's translucent; the components behind it still need to paint in order for the component to render properly.
This problem was exposed in a significant way with the Java 6's new Nimbus Look & Feel. There are numerous bug reports regarding transparent components filed against Nimbus (see stack overflow question Java Nimbus LAF with transparent text fields). The response of the Nimbus development team is this:
This is a problem [in] the orginal design of Swing and how it has been confusing for years. The issue is setOpaque(false) has had a side effect in [existing] LAFs which is that of hiding the background which is not really what it is [meant] for. It is [meant] to say that the component may have transparent parts and [Swing] should paint the parent component behind it.
So, in summary, you should not use setOpaque. If you do use it, bear in mind that the combination of some Look & Feels and some components may do "surprising" things. And, in the end, there is actually no right answer.
javadoc says : If true the component paints every pixel within its bounds. Otherwise, the component may not paint some or all of its pixels, allowing the underlying pixels to show through.
try this example program too...
http://www.java2s.com/Code/JavaAPI/javax.swing/JPanelsetOpaquebooleanisOpaque.htm
I think that the following also needs to be added:
The term opaque has different meanings in Java 2D and in Swing.
In Java 2D opacity is a rendering concept. It is a combination
of an alpha value and the Composite mode. It is a degree to
which the pixel colours being drawn should be blended with pixel
values already present. For instance, we draw a semi-transparent
rectangle over an existing oval shape. The oval is therefore
partially visible. This concept is often compared to light
going trough glass or water.
In Swing, an opaque component paints every pixel within its
rectangular bounds. A non-opaque component paints only a subset of
its pixels or none at all, allowing the pixels underneath it to
show through. The opaque property was set for efficiency reasons; Swing
does not have to paint areas behind opaque components.
Source: Java docs and Filthy Rich Clients
package com.zetcode;
import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import static javax.swing.SwingConstants.CENTER;
import net.miginfocom.swing.MigLayout;
class DrawingPanel extends JPanel {
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.green);
g2d.fillOval(20, 20, 100, 100);
g2d.setColor(Color.blue);
g2d.setComposite(AlphaComposite.getInstance(
AlphaComposite.SRC_OVER, 0.1f));
g2d.fillRect(0, 0, 150, 150);
}
}
class MyLabel extends JLabel {
public MyLabel(String text) {
super(text, null, CENTER);
}
#Override
public boolean isOpaque() {
return true;
}
}
public class OpaqueEx2 extends JFrame {
public OpaqueEx2() {
initUI();
}
private void initUI() {
JLabel lbl1 = new JLabel("Java 2D opacity");
JLabel lbl2 = new JLabel("Swing opaque");
DrawingPanel dpanel = new DrawingPanel();
MyLabel mylbl = new MyLabel("isOpaque()");
mylbl.setBackground(Color.decode("#A9A9A9"));
createLayout(lbl1, lbl2, dpanel, mylbl);
setTitle("Opaque");
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
private void createLayout(JComponent... arg) {
JPanel pnl = new JPanel(new MigLayout("ins 10"));
pnl.add(arg[0], "w 150");
pnl.add(arg[1], "w 150, wrap");
pnl.add(arg[2], "w 150, h 150");
pnl.add(arg[3], "w 150, h 150");
add(pnl);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
OpaqueEx2 ex = new OpaqueEx2();
ex.setVisible(true);
}
});
}
}
In the code example, we have two components. The component on the left is a panel which uses AlphaComposite to paint a highly translucent rectangle over an oval. The component on the right is a label. Labels are non-opaque in most look and feels. We overwrite the label's isOpaque() method to set a gray background.