I am using JPanel to simulate squares. The idea is to put a sort of filter in front of a JPanel.
The first JPanel contains an ImageIcon and a background color.
A second JPanel containing a background color with transparency is put inside the first one.
I did it, but i have a problem : a margin is appearing in the top where the second JPanel doesn't cover the first one.
EDIT : code
class JPanelImage extends JPanel
private ImageIcon imageIcon = null;
public void paintComponent(Graphics g)
{
if(imageIcon != null)
{
Image image = imageIcon.getImage();
int height = 30;
int width = 18;
int marginLeft = (this.getWidth()-width) / 2;
int marginTop = (this.getHeight()-height) / 2;
super.paintComponent(g);
g.drawImage(image, marginLeft, marginTop, width, height, this);
}
else
super.paintComponent(g);
}
public void addColoredLayout(Color color) {
JPanelImage upperLayout = new JPanelImage();
upperLayout.setOpaque(true);
upperLayout.setBackground(color);
upperLayout.setPreferredSize(this.getPreferredSize());
this.add(upperLayout);
}
I also have a Window class (extending JFrame) that create a JPanel and apply the addColoredLayout method.
We have a gridBagLayout on the mainJPanel (which is the frame contentPane) and we had JPanelImage (a JPanel with a image on it) to mainJPanel.When we try to add a JPanel to these JPanelImage, we have the problem he told you.
mainJPanel.setLayout(gridBagLayout);
gridBagLayout.preferredLayoutSize(this.getContentPane()); // this.getContentPane() = mainJPanel
(...)
GridBagConstraints gridBagConstraints;
(...)
gridBagConstraints.gridy = i;
gridBagConstraints.gridx = j;
(...)
gridBagConstraints.insets = new Insets(0, 0, marginBot, marginRight);
gridBagConstraints.ipadx = 0;
gridBagConstraints.ipady = 0;
(...)
mainJPanel.add(tmpJPanelImage, gridBagConstraints);
EDIT : I just found the solution :
((FlowLayout) this.getLayout()).setVgap(0);
Thank you MadProgrammer for the hint.
Related
I have a beginner Swing/AWT question for you.
I am doing custom painting of a BoxPanel Object that extends JPanel.
I have n of these BoxPanels that are then drawn in a JFrame called FormSolutionViewer using a FlowLayout.
Now the issue I am having is that after the JFrame is created and made visible, it is repainted in an infinite loop, without any changes happening to the components. Can someone explain to me why this is and how to fix it so that it only gets repainted when the window is resized or some of the data actually changes?
public class BoxPanel extends JPanel {
private Box box;
private int scaleFactor;
public BoxPanel(Box box, int scaleFactor) {
super();
this.box = box;
this.scaleFactor = scaleFactor;
this.setLayout(null); // Use null layout for absolute positioning
}
/**
* Need to override getPreferredSize() when using a FlowLayout
*/
#Override
public Dimension getPreferredSize() {
return new Dimension(this.box.getLength() * this.scaleFactor, this.box.getLength() * this.scaleFactor);
}
/**
* Paint the box border, background and its rectangles
* #param g
*/
public void paintComponent(Graphics g)
{
super.paintComponent(g);
this.setBackground(Color.white);
// Display no. of rectangles contained in tooltip
this.setToolTipText("Box contains " + this.box.getRectangles().size() + " rectangles");
// Draw border of box
this.setBorder(BorderFactory.createLineBorder(Color.black));
/* Draw rectangles contained in box */
int i = 1;
for (Rectangle rect : box.getRectangles()) {
System.out.println("Rectangle " + i + ": " + rect.toString());
g.setColor(Color.BLACK); // Set color for border
g.drawRect(rect.getPos().getX() * this.scaleFactor, rect.getPos().getY() * this.scaleFactor,
rect.getWidth() * this.scaleFactor, rect.getHeight() * this.scaleFactor);
i++;
}
}
}
public class FormSolutionViewer extends JFrame {
private JPanel contentPane;
private FeasibleSolution solution;
int scaleFactor = 40;
int spacing = 5;
/**
* Create the frame.
*/
public FormSolutionViewer(FeasibleSolution solution, int x, int y, int dpi) {
this.solution = solution;
this.scaleFactor = (int) Math.round(dpi / 2.4);
setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE);
setBounds(x, y, 800, 600);
setTitle("Initialized Solution of " + solution.getInstance().toString());
this.setBackground(new Color(250, 250, 250));
contentPane = new JPanel();
contentPane.setBorder(new EmptyBorder(spacing, spacing, spacing, spacing));
FlowLayout layout = new FlowLayout(FlowLayout.LEADING, 5, 5);
contentPane.setLayout(layout);
this.setContentPane(contentPane);
/* Place the boxes */
int boxNo = 0;
int rowCount = 0;
for (Box box : solution.getBoxes()) {
boxNo++;
BoxPanel boxPanel = new BoxPanel(box, scaleFactor);
contentPane.add(boxPanel);
contentPane.setSize(this.getWidth(), 500);
boxPanel.setVisible(true);
}
}
}
this.setBackground(Color.white);
...
this.setToolTipText("Box contains " + this.box.getRectangles().size() + " rectangles");
...
this.setBorder(BorderFactory.createLineBorder(Color.black));
Don't set a property of the component in the painting method. When a property of a component is changed, Swing invokes revalidate()/repaint() on the component to reflect the new state of the component.
The point of a painting method is to paint the component in its current state, not change its state.
This program is supposed to draw a grid of Rectangles in a JPanel. I drew the grid onto the JPanel by overriding its paintComponent method so that every time the JPanel resizes, the size of the grid would change to fit the height of the JPanel. But when I change the size of the JFrame, the grid only resizes at certain intervals. Is there a better way to change the size of the grid?
import java.awt.*;
import java.awt.geom.Rectangle2D;
import javax.swing.*;
class TextFrame extends JPanel
{
int numOfCells = 99;
int cellSize, xOffSet;
Rectangle2D.Float[][] square = new Rectangle2D.Float[numOfCells][numOfCells];
public TextFrame()
{
setSize(400, 400);
}
public void paintComponent(Graphics comp)
{
Graphics2D comp2d = (Graphics2D)comp;
cellSize = (int)getHeight()/numOfCells;
if(getWidth()<=cellSize*numOfCells)
cellSize = getWidth()/numOfCells;
xOffSet = (getWidth()-(cellSize*numOfCells))/2;
Color black = new Color(0,0,0);
Color grey = new Color(128,128,128);
boolean col = true;
for(int i=0; i<square.length; i++)
{
for(int j=0; j<square[i].length; j++)
{
if(col)
comp2d.setPaint(black);
else
comp2d.setPaint(grey);
col = !col;
square[i][j] = new Rectangle2D.Float(xOffSet+j*cellSize, i*cellSize, cellSize, cellSize);
comp2d.fill(square[i][j]);
}
}
}
public static void main(String[] args)
{
JFrame frame = new JFrame("Conway's Game Of Life");
TextFrame life = new TextFrame();
frame.add(life);
frame.setSize(life.getHeight(), life.getWidth());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
The reason for intervals is integer arithmetic. Change:
int cellSize, xOffSet;
to:
float cellSize, xOffSet;
Also, change:
cellSize = (int)getHeight()/numOfCells;
to:
cellSize = getHeight()/(float)numOfCells;
Some other side notes:
Do not change visibility of paintComponent, it is defined as protected.
Don't forget to add super.paintComponent(comp); in paintComponent()
Do not call setSize(), override panel's getPreferredSize() and pack() the frame. For example:
public Dimension getPreferredSize(){return new Dimension(400, 400);}
Then, add frame.pack(); before making the frame visible.
Try to make paintComponent as fast as possible for better performance and user experience. You can move some things out of it, leaving only painting logic.
My main issue is with the following piece of code when setting up a JFrame:
Why the panel doesn't show if I use the pack() and how to make it work?
Why the first requestFocusInWindow() doesn't work and what the principle to use it?
Why the default layout manager of JPanel doesn't work if I delete the setLayout()?
public class SoundGUI extends KeyAdapter{
public static void main(String[] args) {
SoundGUI sGUI = new SoundGUI();
sGUI.setUp();
}
public void setUp () {
JFrame frame = new JFrame ("Key test");
frame.setSize (1000, 300);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible (true);
Panel p = new Panel ();
p.setLayout(new BorderLayout());//why this sentence is necessary FlowLayout doesn't fill the container but rather lets components size to their preferredSizes.
p.addKeyListener (this);
p.requestFocusInWindow();//it's useless here
//requestFocus only works on focusable components that are displayed.
MyDrawPanel dp = new MyDrawPanel();
dp.setBackground(Color.darkGray);
JLabel test = new JLabel("a trial");
JButton t = new JButton("b");
dp.add(t);
dp.add (test);
p.add (dp);
frame.getContentPane().add(p);
p.requestFocusInWindow();
//frame.pack();//why it doesn't work
//frame.setVisible(true);
}
class MyDrawPanel extends JPanel {
public void paintComponent (Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
for (int i = 0; i < 1000; i += 42) {
g2.fill3DRect(i,100 ,20 ,80 ,true);
}
g2.setColor(Color.black);
for (int i = 21; i < 1000; i += 42) {
g2.fill3DRect(i,100 ,20 ,80 ,true);
}
}
}
}
Suggestions:
Call setVisible(true) after calling pack(). Makes sense, doesn't it?
The BorderLayout will tell the MyDrawPanel to fill the p container, since the component is being added in a default way (meaning BorderLayout.CENTER), and that's how BorderLayout works.
FlowLayout doesn't fill the container but rather lets components size to their preferredSizes.
Don't mix Swing with AWT components. i.e., don't use Panels, but rather use JPanels.
requestFocus only works on focusable components that are displayed.
Better to use Key Bindings than KeyListeners.
Better to avoid setting the sizes of anything if possible.
Based on your new code, your problem is due to you're calling setSize(). Most layout managers don't respect this but rather the preferred size. If your drawing JPanel needs to be so big, then make it so. For example try:
class MyDrawPanel extends JPanel {
private static final int PREF_W = 1000;
private static final int PREF_H = 300;
public void paintComponent(Graphics g) {
super.paintComponent(g); //!! ******** don't forget this!!! *********
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.white);
for (int i = 0; i < 1000; i += 42) {
g2.fill3DRect(i, 100, 20, 80, true);
}
g2.setColor(Color.black);
for (int i = 21; i < 1000; i += 42) {
g2.fill3DRect(i, 100, 20, 80, true);
}
}
// the getPReferredSize will make this JPanel preferentially be this size
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
}
Also note that request focus does work if the component is focusable:
JPanel p = new JPanel(); //!! This should be a JPanel, not a Panel
p.setFocusable(true); //!! This is needed
p.setLayout(new BorderLayout());
p.addKeyListener(this);
p.requestFocusInWindow();
But also note that KeyListeners should be avoided. Use Key Bindings instead.
I've had this problem for a while now, searched many forums and sites (including this one), and still did not find an answer to my question.
This is my problem:
I am building a visual calendar. I have a parent panel with multiple panels in it. I repaint the parent panel, and make the 3 overlaying opaque(false). The paint of the parent panel is not showing until I resize the frame (or use the buttons that overlay one of the 3, but those are left out in this example because it makes the code longer)
Anyway, here is the code, I simplified it to the problem part:
public class Calendar extends JPanel{
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setSize(1600,150);
frame.add(new Calendar());
frame.setVisible(true);
}
public Calendar(){
setLayout(new GridBagLayout());
GridBagConstraints cc = new GridBagConstraints();
cc.weightx = 1;
cc.weighty = 1;
cc.gridx = 0;
cc.fill = GridBagConstraints.BOTH;
//Initiate Panels
JPanel yearpanel = new JPanel();
JPanel monthpanel = new JPanel();
JPanel daypanel = new JPanel();
yearpanel.setLayout(new GridBagLayout());
monthpanel.setLayout(new GridBagLayout());
daypanel.setLayout(new GridBagLayout());
// Set sizes
int width = (int) this.getPreferredSize().getWidth();
int height = (int) (this.getPreferredSize().getHeight() / 3);
yearpanel.setSize(width,height);
daypanel.setSize(width,height);
monthpanel.setSize(width,height);
//make transparent
yearpanel.setOpaque(false);
monthpanel.setOpaque(false);
daypanel.setOpaque(false);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Image image = Toolkit.getDefaultToolkit().getImage("Images/CalendarBackground.jpg");
g.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), null);
}
}
I have no idea why it does that + I could not find an answer online, only people with the same problem whose question got abandoned :/
Can anyone help me?
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Image image = Toolkit.getDefaultToolkit().getImage("Images/CalendarBackground.jpg");
g.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), null);
}
The image should not be loaded in the paint method. Instead it should be declared as a class attribute and preloaded.
The Toolkit method is asynchronous, so it is even more important to use the component as the ImageObserver.
g.drawImage(image, 0, 0, this.getWidth(), this.getHeight(), this);
Here is a working SSCCE that is something like what you seem to be attempting.
check the return value of Graphics.drawImage(). I bet it's returning false, which means the image you're painting isn't fully loaded and scaled yet. Try loading the image somewhere other than your paintComponent method, like your Calendar constructor.
I have a problem that, I need to only redraw/rebuild the drawing area if the "c" key is pressed.
The way I'm doing using repaint(), turns out to be causing the draw area to be of position.
I also notice that, whenever I re-size the frame, the keylistener is no longer working.
Problems:
unable to repaint correctly.
keylistener is not working after frame is re-sized.
Love to attach the display, but seems like it is blocked because I am newbie.
The following code is the main function that call the class "newZone".
frame.addComponentListener(new ComponentAdapter() {
public void componentResized(ComponentEvent e){
System.out.println("component Rebuild");
frame.getContentPane().removeAll();
frame.getContentPane().invalidate();
JComponent newContentPane = new newZone(frame.getSize());
newContentPane.setOpaque(true);
frame.getContentPane().add(newContentPane);
frame.getContentPane().revalidate();
frame.setContentPane(newContentPane);
}
});
The following is the class of newZone, which contains Paint & keylistener:
public class newZone extends JComponent implements MouseListener, MouseMotionListener, KeyListener {
JPanel panel1;
JTextArea textArea;
JScrollPane scrollPane;
MyDrawingTool Drawing;
static int firsttimer = 0;
static int preposX = 0;
static int preposY = 0;
static int widthPercentage = 80 , heightPercentage = 93;
static int numberOfYboxes,numberOfXboxes;
static Dimension currentPanelSize;
static final String NEWLINE = System.getProperty("line.separator");
static Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
public newZone(Dimension currentPanelSize1) {
currentPanelSize = currentPanelSize1;
Drawing = new MyDrawingTool();
Drawing.setBackground(Color.WHITE);
Drawing.setBounds( 10, 10,
(int) currentPanelSize.getWidth()*(widthPercentage)/100,
(int) currentPanelSize.getHeight()*(heightPercentage)/100 );
Drawing.setPreferredSize(new Dimension( (int) currentPanelSize.getWidth()*(widthPercentage)/100,
(int) currentPanelSize.getHeight()*(heightPercentage)/100));
Drawing.addMouseListener(this);
Drawing.addMouseMotionListener(this);
add(Drawing);
addKeyListener(new KeyAdapter()
{
public void keyPressed(KeyEvent e){
System.out.println( "Key type: "+e.getKeyChar());
if(e.getKeyChar() == 'c'){
Drawing.redraw();
}
}
});
setFocusable(true);
}
class MyDrawingTool extends JPanel{
void redraw(){
repaint();
}
#Override
public void paint(Graphics q){
//super.paint(q);
int j,k, width, height;
int startX = 10, startY = 10;
int boxSize = 50;
width = (int)currentPanelSize.getWidth()*(widthPercentage)/100;
height = (int)currentPanelSize.getHeight()*(heightPercentage)/100;
numberOfYboxes = (height-20)/50;
numberOfXboxes = (width-20)/50;
for ( j = 0; j < numberOfYboxes; j++)
{
startX = 10;
for ( k = 0; k < numberOfXboxes; k++)
{
q.setColor(Color.WHITE);
q.fillRect(startX, startY, boxSize, boxSize);
q.setColor(Color.BLUE); //Set line color
q.drawRect(startX, startY, boxSize, boxSize);
startX+=boxSize;
}
startY+=boxSize;
}
}
}
}
I don't know why you are using a ComponentListener. I don't see any reason to remove/add/invalidate/revalidat and do all the other stuff.
All you need to do is add the panel to the CENTER of a content pane of the frame. The panel will automatically increase/decrease in size as the frame resizes. There is no need for the ComponentListener.
Custom painting should be done in the paintComponent() method and don't forget to invoke super.paintComponent(...) at the start.
The KeyListener doesn't work because focus is now on the JFrame (not the panel) after you resize the frame. You should NOT be using a KeyListener for this. Instead you should be Key Bindings which work even when the panel doesn't have focus.
It seems, you do not need to switch content pane at all.
If you use some layout on default content pane, as #camickr suggested, you won't need to handle resize and other stuff manually.
Good luck there.