define container minimum size using java layouts - java

i am writing a stand alone app in java using a couple of JPanel with different layouts in order to arrange the user interface.
now my problem is that when i take the upper side of the window (its a pannel in a border layout which is inside another panel which using border layout),im tring to add a class that extends panel is order to paint an icon on the top of my window (draw on the panel) . the problem is that the layout is cuting a part of the icon, or in other words, minimazing the panel to a certain size.
i tried changing to flowlayout and others but is does the same...
so i wanted to ask if an option which tells the layout that a container (panel or others) can not be set to a size lower then a given size exists? other suggestions will allso help but keep in mind that i am tring to add the icon with mininal change to the GUI.
thanks for reading this and helping
moshe

Container can hold MinimumSize for JComponent, simple example,
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class CustomComponent extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent() {
setTitle("Custom Component Graphics2D");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponents());//
pack();
// enforces the minimum size of both frame and component
setMinimumSize(getSize());
setVisible(true);
}
public static void main(String[] args) {
CustomComponent main = new CustomComponent();
main.display();
}
}
class CustomComponents extends JPanel {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 300);
}
#Override
public void paintComponent(Graphics g) {
int margin = 10;
Dimension dim = getSize();
super.paintComponent(g);
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width - margin * 2, dim.height - margin * 2);
}
}

Related

Can't see child JPanel in JPanel in JFrame

I've created a JFrame.
Inside this JFrame, I've created a JPanel.
Inside this JPanel I've created another JPanel (lets call it "A").
I've drawn in "A" a rectangle, and wanted to create buttons using graphics.
There is no rectangle in my gui. I could see that the paintComponent() method inside "A" is not being invoked.
Code:
The JPanels: (the child JPanel is inner class)
public class MemoryPanel extends JPanel {
public MemoryPanel(){
setPreferredSize(new Dimension(350,448));
}
#Override
public void paintComponent(Graphics g) {
//POSITIONING
setLayout(new BorderLayout());
//CREATE MEMORY BUTTONS
MemButton a=new MemButton();
//Drawing Rectangles for Memory
add(a,BorderLayout.CENTER);
}
private class MemoryButton extends JPanel{
public MemoryButton(){
setLayout(null);
setPreferredSize(new Dimension(87,40));
}
#Override
public void paintComponent(Graphics g){
Graphics2D td= (Graphics2D)g;
td.drawRect(0, 0, 20, 20);
}
}
}
You should program the JButtons first in order for your graphics to work as buttons. I belive this post will help you with that:
Creating a custom button in Java
I you want a rectangle to be the background for your buttons you can draw it in your main panel and add the buttons on it. Try using different Layouts to mantain some order.
I've made a simple GUI to test your code and the rectangle appears correctly.
I made no relevant changes in the code that you posted.
import javax.swing.JFrame;
import javax.swing.SwingUtilities;
public class SimpleJFrameProgram extends JFrame {
private static final long serialVersionUID = 1L;
public SimpleJFrameProgram() {
super("TEST");
initComponents();
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.pack();
this.setLocationRelativeTo(null);
this.setVisible(true);
}
private void initComponents() {
MemoryPanel memoryPanel = new MemoryPanel();
this.add(memoryPanel);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
try {
new SimpleJFrameProgram();
} catch (Exception e) {
e.printStackTrace();
}
}
});
}
}
I've applyed minor changes to your MemoryPanel: replaced MemButton by your MemoryButton and fill the rectangle in red to improve its visibility for the test. Without this last change, the rectangle appears too.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class MemoryPanel extends JPanel {
public MemoryPanel(){
setPreferredSize(new Dimension(350,448));
}
#Override
public void paintComponent(Graphics g) {
// POSITIONING
setLayout(new BorderLayout());
// CREATE MEMORY BUTTONS
MemoryButton a = new MemoryButton();
// Drawing Rectangles for Memory
add(a,BorderLayout.CENTER);
}
private class MemoryButton extends JPanel{
public MemoryButton(){
setLayout(null);
setPreferredSize(new Dimension(87,40));
}
#Override
public void paintComponent(Graphics g) {
Graphics2D td = (Graphics2D) g;
td.setColor(Color.red);
td.fillRect(0, 0, 20, 20);
}
}
}
This is the obtained result:
Maybe your problem is located on initializing the parent JFrame.
Changing the class name of MemoryButton fixed it.
I had another package with the same class name.

JPanel appearing to be two times bigger than set resolution

I must say I tried everything and I can't understand what's wrong with my code.
In my SIView class I create MainFrame which extends JFrame with specified resolution (let's say X,Y).
Then I create gamePanel which extends JPanel whith the same resolution as MainFrame, and add it to MainFrame. The problem is that effective resolution of the panel is twice as big (x*2, y*2). It's like the panel is being streched to be twice as big.
Frame will display only a quarter (upper left quarter) of the panel either with pack() or mannualy setting the size, unless I set it to double the resolution in which case It's ok, but that's not a proper way to do that(When calculating positions in the game I have to double everything or divide it by 2 to keep proper proportions). I even tried different Layout managers wthout any succes.
Here's the code of the main view class:
public class SIView implements Runnable {
private final MainFrame mainFrame;
private final GamePanel gamePanel;
public SIView(BlockingQueue<SIEvent> eventQueue) {
this.eventsQueue = eventQueue;
mainFrame = new MainFrame();
gamePanel = new GamePanel();
gamePanel.setVisible(true);
mainFrame.getContentPane().add(gamePanel);
// mainFrame.pack();
#Override
public void run() {
mainFrame.setVisible(true);
}
public void init() {
SwingUtilities.invokeLater(this);
}
//some code not related
}
the frame class:
public class MainFrame extends JFrame {
/**
*
*/
private static final long serialVersionUID = 6513420589842315661L;
public MainFrame() {
setTitle("Space Intruders");
setSize(new Dimension(SIParams.RES_X, SIParams.RES_Y));
setResizable(false);
setLayout(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
}
panel class:
public class GamePanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 8112087715257989968L;
private final PlayerShipView playerShip;
private final ArrayList<SmallEnemyShipView> smallEnemyShip;
private final ArrayList<LightMissleView> lightMissle;
public GamePanel() {
setPreferredSize(new Dimension(SIParams.RES_X, SIParams.RES_Y));
setMaximumSize(new Dimension(SIParams.RES_X, SIParams.RES_Y));
setBounds(0, 0, SIParams.RES_X, SIParams.RES_Y);
setBackground(new Color(0, 0, 0));
setLayout(new OverlayLayout(this));
setDoubleBuffered(true);
// TODO
playerShip = new PlayerShipView();
smallEnemyShip = new ArrayList<SmallEnemyShipView>();
lightMissle = new ArrayList<LightMissleView>();
this.add(playerShip);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
//some code not related
}
If I use LayoutManager and properly override getPreferredSize() in GamePanel, the code seems to work as expected:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import org.jfree.ui.OverlayLayout;
public class SIView implements Runnable {
public static interface SIParams {
int RES_X = 500;
int RES_Y = 400;
}
public static class GamePanel extends JPanel {
public GamePanel() {
setBackground(new Color(0, 0, 0));
setLayout(new OverlayLayout());
}
#Override
public Dimension getPreferredSize() {
return new Dimension(SIParams.RES_X, SIParams.RES_Y);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
}
// some code not related
}
private JFrame mainFrame;
private GamePanel gamePanel;
#Override
public void run() {
mainFrame = createMainFrame();
gamePanel = new GamePanel();
mainFrame.getContentPane().add(gamePanel);
mainFrame.pack();
mainFrame.setVisible(true);
}
private JFrame createMainFrame() {
JFrame frame = new JFrame();
frame.setTitle("Space Intruders");
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
return frame;
}
public void init() {
SwingUtilities.invokeLater(this);
}
// some code not related
public static void main(String[] args) {
new SIView().init();
}
}
I also dropped the MainFrame class since it is not needed to extend JFrame in this context.
I just solved everything :D But I must say I feel stupid. The problem was with painting my components, in paintcomponent() method I painted rectangle on a relative position while changing component position also relatively. That gave the effect of 2xtimes movement etc. because while the component was moving the rectangle was moving inside of it too. I guess I have a lot to learn about Swing ;) Sorry for all this trouble ;)
PS. I didn't have to change anything in Panel/Frame classes except for using pack() method after everything.

Changing visible JPanels in a JFrame from another class

I have a class named Window that extends JFrame and sets up the basic layout. To that JFrame I want to add different JPanels and a seperate class named Tracking is deciding which JPanel is being showed in the JFrame. I would like to have the Tracking class being able to change what JPanel is being showed in the JFrame.
Each JPanel is a seperate class, the JFrame and the Tracking class are also seperate classes.
Window.java
package Setup;
import javax.swing.JFrame;
public class Window extends JFrame
{
private static final int width = 1280;
private static final int height = 720;
public Window()
{
setResizable(false);
setSize(width, height);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// frame.setUndecorated(true);
// frame.setExtendedState(JFrame.MAXIMIZED_BOTH);
setVisible(true);
}
public static void main(String[] args)
{
new Window();
}
}
Animation.java
package Setup;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
public class Animation extends JPanel
{
private final int width = super.getWidth();
private final int height = super.getHeight();
private Image dbImage;
private Graphics dbGraphics;
public Animation()
{
setSize(width, height);
setVisible(true);
}
#Override
public void paint(Graphics g)
{
dbImage = createImage(getWidth(), getHeight());
dbGraphics = dbImage.getGraphics();
paintComponent(dbGraphics);
g.drawImage(dbImage, 0, 0, this);
}
#Override
public void paintComponent(Graphics g)
{
//ADD Animation to get attention from users
g.drawString("TEST TEST TEST", 50, 50);
repaint();
}
}
You have any number of cohoes
You Could...
Pass a reference of the frame or the controller. This would then allow you access to all the components on the frame.
This is a bad idea as it would expose the frame to the controller unnecessarily, give he controller more power then it actually needs...
You Could...
Pass the list of panels to the controller. This wold allow he controller to change the panels as it required.
This not too bad an idea, but a clever controller would be able to use the getParent method to deduce the top level container, again, exposing parts of your application unnecessarily
You Could...
Create a simple model that has simple accessors that would allow the controller to the ability to suggest to the UI what views should be active. These could be named, for instances, or you could simply supply next/previous/first/last methods to allow the controller to change the view.
This allows you to change the views ordering in a pluggable fashion,simply by supplying a new model, which wouldn't affect any other part of the application

Java Swing: problems with width

I have problems with understanding the behavior of my application. I want to create a simple window (1000x700px), divided into two parts (250px and 750px width respectively). I tried the following code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Example extends JFrame
{
private static final long serialVersionUID = 1L;
public Example()
{
this.setSize(1000, 700);
this.setTitle("Example");
this.setResizable(false);
this.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
JPanel navigation_panel_wrap = new JPanel();
JPanel content_panel_wrap = new JPanel();
navigation_panel_wrap.setPreferredSize(new Dimension(250, 700));
content_panel_wrap.setPreferredSize(new Dimension(750, 700));
content_panel_wrap.setBackground(Color.green);
navigation_panel_wrap.setBackground(Color.red);
this.getContentPane().add(navigation_panel_wrap);
this.getContentPane().add(content_panel_wrap);
}
public static void main(String[] args)
{
Example example = new Example();
example.setVisible(true);
}
}
As you can see I manually set layout manager for JFrame (FlowLayout instead of BorderLayout with zero horizontal and vertical gaps). Of course, I can just use BorderLayout and than use add() method with BorderLayout.EAST and BorderLayout.WEST parameters, but I want to understand what's wrong with FlowLayout.
When I run my application, I get the following (no green JPanel):
If I decrease width of, for example, content_panel_wrap and make it 744px instead of 750px, everything works correctly.
So the question is - what are these strange 6 pixels? I'm not sure this value is constant for all operating systems, so I want to understand its origin.
There's nothing wrong with FlowLayout but you will need to call pack() for all components to be sized.
As for your codes problem (+1 to #Reimeus) calling pack() is the solution.
as per docs:
Causes this Window to be sized to fit the preferred size and layouts
of its subcomponents. If the window and/or its owner are not yet
displayable, both are made displayable before calculating the
preferred size. The Window will be validated after the preferredSize
is calculated.
Tips:
Dont extend JFrame unnecessarily.
Use Event Dispatch Thread when creating and changing UI components:
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
// create UI components etc here
}
});
Dont call setPreferredSize() rather override getPrefferedSize() of component.
Dont call setSize(...) on JFrame rather call JFrame#pack() before setting it visible.
Dont forget to call JFrame#defaultCloseOperation(..) or your initial/EDT thread will not be terminated when JFrame is closed.
Here is an example combining my advice and your code:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.FlowLayout;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class Example {
private final JFrame frame;
public Example() {
frame = new JFrame();
frame.setTitle("Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);//app exited when frame closes
frame.setResizable(false);
frame.setLayout(new FlowLayout(FlowLayout.LEFT, 0, 0));
JPanel navigation_panel_wrap = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(250, 700);
}
};
JPanel content_panel_wrap = new JPanel() {
#Override
public Dimension getPreferredSize() {
return new Dimension(750, 700);
}
};
content_panel_wrap.setBackground(Color.green);
navigation_panel_wrap.setBackground(Color.red);
frame.add(navigation_panel_wrap);
frame.add(content_panel_wrap);
//pack frame (size components to preferred size)
frame.pack();
frame.setVisible(true);//make frame visible
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new Example();
}
});
}
}

Making image scrollable in JFrame contentpane

I am trying to display a large image inside a JFrame's contentpane. I would like to make the image or contentpane scrollable as the image is large. I tried to do it using Jscrollpane and add it into the contentpane but it didn't work. Did some searching for solution but end up failed to find one. Can someone guide me? My code are below
FinalEnvironment.java
package environment;
import java.awt.*;
import java.net.URL;
import javax.swing.*;
public class FinalEnvironment{
public FinalEnvironment(){
Image Eastlake;
URL EastlakeURL = null;
EastlakeURL = FinalEnvironment.class.getResource("/image1/eastlake_night.png");
Eastlake = Toolkit.getDefaultToolkit().getImage(EastlakeURL);
JFrame frame = new JFrame("UniCat World");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 600);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
JMenuBar yellowMenuBar = new JMenuBar();
Map map = new Map(800, 550, Eastlake);
yellowMenuBar.setOpaque(true);
yellowMenuBar.setBackground(Color.YELLOW);
yellowMenuBar.setPreferredSize(new Dimension(800, 50));
frame.setJMenuBar(yellowMenuBar);
JScrollPane scroller = new JScrollPane(map);
scroller.setAutoscrolls(true);
scroller.setPreferredSize(new Dimension(800, 550));
frame.getContentPane().add(scroller, BorderLayout.CENTER);
frame.setSize(800, 600);
frame.setVisible(true);
}
public static void main(String[] args){
FinalEnvironment fe = new FinalEnvironment();
}
}
Here is my map.java
package environment;
import java.awt.*;
import javax.swing.*;
public class Map extends JPanel{
private int width;
private int height;
private Image img;
public Map(int width, int height, Image img){
this.width = width;
this.height = height;
this.img = img;
}
protected void paintComponent(Graphics g)
{
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(img,0,0,2624,1696,null);
}
}
Lastly, I would like to place Jbuttons on top of this image. Should I call a Rectangle and place it on top the image in the contentpane which then I use Point to position my buttons or should I straight away use the image or the component itself to do it? I need the button to be able to synchronize with the image when it is scrolled instead of static in the contentpane.
Thanks
What I would do here:
1.Have a panel (canvas) which only responsibility is to paint a given image independent of the real image size in overridden method paintComponent()
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
2.Make sure the canvas preferred size equals to image real size.
3.Have a second panel which will serve as content pane of a frame.
4.In it you will set a JScrollPane as its centre.
5.In the scroll pane viewport will be the component from step 1.
6.Add your button to canvas panel from step 1. It will be scrolled together with the image.
7.Add the content pane, the panel from step 3, to a frame, and run the application.
EDIT:
Code sample with button added to canvas, which stays always in its place, independent of scroll position or frame size.
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
public class ScrollImageTest extends JPanel {
private static final long serialVersionUID = 1L;
private BufferedImage image;
private JPanel canvas;
public ScrollImageTest() {
try {
this.image = ImageIO.read(new URL("http://interviewpenguin.com/wp-content/uploads/2011/06/java-programmers-brain.jpg"));
}catch(IOException ex) {
Logger.getLogger(ScrollImageTest.class.getName()).log(Level.SEVERE, null, ex);
}
this.canvas = new JPanel() {
private static final long serialVersionUID = 1L;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(image, 0, 0, null);
}
};
canvas.add(new JButton("Currently I do nothing"));
canvas.setPreferredSize(new Dimension(image.getWidth(), image.getHeight()));
JScrollPane sp = new JScrollPane(canvas);
setLayout(new BorderLayout());
add(sp, BorderLayout.CENTER);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JPanel p = new ScrollImageTest();
JFrame f = new JFrame();
f.setContentPane(p);
f.setSize(400, 300);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
}
}
What if you use your dimensions to set the Map's preferred size. For instance, give Map this method:
// method in the Map class
#Override
public Dimension getPreferredSize() {
return new Dimension(width, height);
}
This way the Map JPanel will take up the necessary room to show the entire image. Also, why does your drawImage method in the paintComponent method have the large magic numbers? Why not use the width and height there as well? Edit 1: or don't even specify the image size as Boro suggests in his answer (1+ to him).
Why is everybody reinventing the wheel??? There is no need for a custom panel to paint the image!!!
All you need to do is create a JLabel and add an ImageIcon to the label and you won't have a problem. The label will:
paint the image at (0, 0) at its original size (which is exactly what the custom code is doing).
determine the preferred size of the image based on the image size. Now scrolling will happen automatically.
Also there is rarely any reason to use the setPreferredSize() method since all components have a default preferred size. So you should not set the default size of the menu bar. The only time I set a preferred size would be on the JScrollPane. This will allow the frame to be packed at a reasonable size and then scrollbars will appear automatically based on the size of the image in the label.
In addition to other helpful answers, you might like studying this example that uses mouse gestures to scroll arbitrary content.

Categories