Why is JPanel size is wrong? - java

There is constructor of class extending JFrame:
import java.awt.*;
import javax.swing.*;
public class ChessFrame extends JFrame {
public ChessFrame () {
setSize(520, 520);
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setLayout(new GridLayout(1, 1));
// Add components
getContentPane().add(new Board());
pack();
setVisible(true);
}
}
And class extending JPanel:
import javax.swing.*;
public class Board extends JPanel {
public Board() {
setSize(new Dimension(520, 520));
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.fillRect(0, 0, 520, 520);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(520, 520);
}
}
As a result rectangle smaller then 520x520.
Size of black rectangle is about 496x510. There is more:
getWidth() and getHegiht() written inside the Board class, returns 0 and 0 (so size of this JPanel into JFrame is 0x0)
If I remove pack(), size of frame becomes 496x510 (black rectangle size)
It's actually copypaste of official java tutorial: https://docs.oracle.com/javase/tutorial/uiswing/painting/step2.html.
Do I do something wrong or it's something related with java? If it's second, why does this happen?
Any help would be appreciated.

This example only tries to establish the panel size once the frame (and panel) are visible on-screen. It returns the exact size (300 pixels) set in the code.
import java.awt.*;
import javax.swing.*;
public class ChessBoard extends JPanel {
int size = 300;
JLabel sizeLabel = new JLabel();
ChessBoard() {
setBackground(Color.CYAN);
setLayout(new GridBagLayout());
add(sizeLabel);
}
public void showSize() {
sizeLabel.setText(String.format("%1sx%1s", getWidth(), getHeight()));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(size,size);
}
public static void main(String[] args) {
Runnable r = () -> {
ChessBoard cb = new ChessBoard();
JFrame f = new JFrame(cb.getClass().getSimpleName());
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setLocationByPlatform(true);
f.setContentPane(cb);
f.pack();
f.setMinimumSize(f.getSize());
f.setVisible(true);
// delay showing size until app. is on-screen
Runnable r1 = cb::showSize;
SwingUtilities.invokeLater(r1);
};
SwingUtilities.invokeLater(r);
}
}

Related

Drawing not appearing java

I'm having a problem getting particular objects to appear in my frame/panels. In the code below I have objects of type "Drawing" such as light, red, yellow, and green. The program is supposed to create a traffic light but the other drawing don't appear and I'm not sure why. light is separate from the rest so that it won't be affected if the background draws over it, but this isn't the issue. The circles/lights don't draw and I don't see what I'm missing or what I'm doing wrong.
What the frame should look like
package lab8;
import oracle.jvm.hotspot.jfr.JFR;
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
enum shape{
circle,square;
}
public class TrafficLight2 extends JFrame {
JPanel panel = new JPanel();
JFrame frame = new JFrame();
TrafficLight2(){
frame.setPreferredSize(new Dimension(500, 500));
frame.setLayout(new FlowLayout());
panel.setLayout(new FlowLayout());
TrafficLight t = new TrafficLight();
t.setPreferredSize(new Dimension(500,500));
panel.setPreferredSize(new Dimension(500,500));
panel.add(t,BorderLayout.CENTER);
Drawing light = new Drawing();
light.colour=Color.RED;
light.s=shape.circle;
repaint();
panel.add(light);
frame.add(light);
frame.add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
public static void main(String[] args){
new TrafficLight2();
}
class ButtonListener implements ActionListener{
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
ButtonListener(){
}
}
class Drawing extends JPanel{
int width=50;
int height=50;
shape s;
Color colour;
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int verticalCenter = getParent().getHeight()/2;
int horizontalCenter = getParent().getWidth()/2;
int topLeftSquareCornerY = verticalCenter - (height/2);
int topLeftSquareCornerX = horizontalCenter - (width/2);
g.setColor(colour);
if (s==shape.square) {
g.fillRect(topLeftSquareCornerX, topLeftSquareCornerY, width, height);
}
else{
g.fillOval(topLeftSquareCornerX,topLeftSquareCornerY,width,height);
}
}
}
class TrafficLight extends Drawing{
Drawing red = new Drawing();
Drawing yellow = new Drawing();
Drawing green = new Drawing();
Drawing background = new Drawing();
TrafficLight(){
this.red.s=shape.circle;
this.red.colour=Color.RED;
this.yellow.s=shape.circle;
this.yellow.colour=Color.YELLOW;
this.green.s=shape.circle;
this.green.colour=Color.GREEN;
this.background.s=shape.square;
this.background.colour=Color.BLACK;
this.background.width=100;
this.background.height=300;
this.s=shape.square;
this.colour=Color.BLACK;
this.width=100;
this.height=300;
background.add(red,BorderLayout.NORTH);
background.add(yellow,BorderLayout.CENTER);
background.add(green,BorderLayout.SOUTH);
this.add(background)
repaint();
}
}
}
You forgot to add background to TrafficLight.
TrafficLight(){
.... other stuff
this.add(background);
}
Also, I find it odd that TrafficLight2 extends JFrame, but is not actually used...

Java Drawing to a JPanel (debugging)

I'm trying to draw a basic object to a JPanel
although it doesn't seem to be working.
I'm certain I am doing something wrong with where the paint method
is being called
import javax.swing.*;
import java.awt.*;
import java.awt.image.*;
public class testGui {
static colors gc_colors;
static gui gc_gui;
public static void main(String[] args) {
gc_colors = new colors();
gc_gui = new gui();
gc_gui.cv_frame.setVisible(true);
}
public static class colors {
Color cv_ltGrey;
Color cv_mdGrey;
Color cv_dkGrey;
public colors() {
cv_ltGrey = Color.decode("#DDDDDD");
cv_mdGrey = Color.decode("#CCCCCC");
cv_dkGrey = Color.decode("#111111");
}
}
public static class gui {
JFrame cv_frame;
JPanel cv_panel;
JPanel cv_content;
public gui() {
cv_frame = new JFrame();
cv_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cv_frame.setTitle("Test GUI");
cv_frame.setSize(600, 400);
cv_frame.setLayout(new FlowLayout());
cv_panel = new JPanel();
cv_panel.setBackground(gc_colors.cv_ltGrey);
cv_panel.setPreferredSize(new Dimension(500, 300));
cv_frame.add(cv_panel);
cv_content = new content();
cv_panel.add(cv_content);
}
}
public static class content extends JPanel {
public void paint(Graphics graphic) {
super.paint(graphic);
draw(graphic);
}
public void update() {
repaint();
}
public void draw(Graphics graphic) {
Graphics2D graphic2D = (Graphics2D) graphic;
graphic2D.setPaint(gc_colors.cv_ltGrey);
graphic2D.fillRect(10, 10, 100, 100);
}
}
}
I have a class for my gui which I am adding a JPanel to (a light grey one).
Which I am then trying to add my drawing to using a JPanel extended class
called content.
When I run it though it seems to create the grey JPanel which I want but
the drawing is just a tiny white square and I'm not sure why.
So, you content panel has a default preferred size of 0x0, FlowLayout honours the preferredSize of its components (with a little margin), hence the reason why you have a nice little small white rectangle.
What you need to do is override the getPreferredSize method of the content panel and return a suitable size, for example
public static class content extends JPanel {
#Override
public Dimension getPreferredSize() {
return new Dimension(120, 120);
}
public void paint(Graphics graphic) {
super.paint(graphic);
draw(graphic);
}
public void update() {
repaint();
}
public void draw(Graphics graphic) {
Graphics2D graphic2D = (Graphics2D) graphic;
graphic2D.setPaint(gc_colors.cv_ltGrey);
graphic2D.fillRect(10, 10, 100, 100);
}
}
I've decided to just leave out the second JPanel altogether.
It was too much of a hassle to put the JPanel inside of another JPanel
so instead I am only going to use a single JPanel
public static class gui {
JFrame cv_frame;
JPanel cv_panel;
JPanel cv_content;
public gui() {
cv_frame = new JFrame();
cv_frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
cv_frame.setTitle("Test GUI");
cv_frame.setSize(600, 400);
cv_frame.setLayout(new FlowLayout());
cv_content = new content();
cv_content.setBackground(gc_colors.cv_ltGrey);
cv_content.setPreferredSize(new Dimension(500, 300));
cv_frame.add(cv_content);
}
}

Prevent automatic JScrollPane viewport change on content resize

I have a JScrollPane with a custom JPanel as the content. The scrollpane has a mouse wheel handler, so when the user scrolls, the content is 'zoomed', and the JPanel becomes larger in height.
I need to ensure that the user's viewport maintains position on the current row of pixels on the JPanel that are currently under the mouse. However, when I manually try and set the viewport in the wheel handler, I can't ensure consistent focus, as sometimes the JScrollPane overrides my changes with the default behaviour for when JScrollPane's content becomes larger.
I've made a simple example showing the structure of my program as below, however the example only tries to ensure the viewport focus remains on the bottom of the JPanel when scrolling in as the panel doubles in size. The scroll wheel causes the JPanel to double/halve in height.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseWheelEvent;
import java.awt.event.MouseWheelListener;
import javax.swing.*;
public class Test extends JFrame{
public static void main(String[] args){
Test t= new Test();
t.show();
}
public Test()
{
BorderLayout blm = new BorderLayout();
setLayout(blm);
getContentPane().add(new MyScroller(), BorderLayout.CENTER);
this.setPreferredSize(new Dimension(300, 300));
this.setMinimumSize(new Dimension(300, 300));
pack();
setLocationRelativeTo(null);
setVisible(true);
}
static class MyScroller extends JScrollPane implements MouseWheelListener
{
static InnerPanel controlGrid = new InnerPanel();
int panelHeight = 200;
public MyScroller()
{
super(controlGrid, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
this.setBackground(Color.blue);
this.addMouseWheelListener(this);
this.setPreferredSize(new Dimension(300, 300));
this.setMinimumSize(new Dimension(300, 300));
}
#Override
public void revalidate()
{
super.revalidate();
if (controlGrid != null)
{
controlGrid.revalidate();
}
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
draw(g);
}
public void draw(Graphics g)
{
}
public void mouseWheelMoved(MouseWheelEvent e) {
String message;
int direction = 1;
int notches = e.getWheelRotation();
if (notches < 0) {
panelHeight += panelHeight;
} else {
panelHeight -= (panelHeight > panelHeight ? panelHeight : 0);
}
controlGrid.setPreferredSize(new Dimension(20, panelHeight));
controlGrid.setMinimumSize(new Dimension(20, panelHeight));
repaint();
this.getViewport().setViewPosition(new java.awt.Point(0, panelHeight));
}
static class InnerPanel extends JPanel
{
public InnerPanel(){
this.setBackground(Color.red);
this.setPreferredSize(new Dimension(100, 600));
this.setMinimumSize(new Dimension(100, 600));
}
}
}
}

How can I move my JButton to the right side of my JPanel?

For a programming class, I was asked to create a replica of an iCalendar app. I'm using JAVA to code it, and JFrame and JPanel to draw it. Here is a SSCCEE of my problem:
import java.awt.*;
import javax.swing.*;
public class Mainie extends JFrame {
private JButton back = new TriangleButton(true),
front = new TriangleButton(false);
public Mainie(){
super();
setSize(800, 400);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final JPanel months = new JPanel();
final JPanel days = new JPanel();
JLabel one = new JLabel("Hallo");
one.setHorizontalAlignment(JLabel.CENTER);
back.setHorizontalAlignment(SwingConstants.LEFT);
front.setHorizontalAlignment(SwingConstants.RIGHT);
months.setLayout(new BorderLayout());
months.add(back, BorderLayout.WEST);
months.add(one, BorderLayout.CENTER);
months.add(front, BorderLayout.EAST);
days.setLayout(new GridLayout(1,1));
days.add(new JButton("Meister Camickr"));
months.setAlignmentX(CENTER_ALIGNMENT);
add(months, BorderLayout.NORTH);
add(days, BorderLayout.CENTER);
setVisible(true);
}
public static void main(String[] args){
new Mainie();
}
}
class TriangleButton extends JButton {
private Shape triangle = createTriangle();
public void paintBorder(Graphics g) {
((Graphics2D) g).draw(triangle);
}
public void paintComponent(Graphics g) {
((Graphics2D) g).fill(triangle);
}
public Dimension getPreferredSize() {
return new Dimension(200, 100);
}
public boolean contains(int x, int y) {
return triangle.contains(x, y);
}
public TriangleButton(boolean pointLeft) {
super();
if (!pointLeft)
this.triangle = createRTriangle();
}
private Shape createTriangle() {
Polygon p = new Polygon();
p.addPoint(0, 20);
p.addPoint(20, 0);
p.addPoint(20, 40);
return p;
}
private Shape createRTriangle() {
Polygon p = new Polygon();
p.addPoint(20, 20);
p.addPoint(0, 0);
p.addPoint(0, 40);
return p;
}
}
If you compile this, you can see that my JButton is too far to the left. How can I move it to the right?
The basic problem with those buttons is that the button is very wide and the visual indicator is to the left. Put a border around them and it becomes obvious.
OTOH I would use an entirely different approach, using text for the buttons and a factory method to make them look as expected.
import java.awt.*;
import javax.swing.*;
public class CustomPointerButtons {
JPanel ui;
CustomPointerButtons() {
initUI();
}
private final void initUI() {
ui = new JPanel(new BorderLayout(4, 4));
JPanel topPanel = new JPanel(new BorderLayout(4, 4));
ui.add(topPanel, BorderLayout.PAGE_START);
ui.add(new JButton("Mister Mix")); //will default to CENTER
topPanel.add(new JLabel("Blah, Blah.."));
JButton back = getMinimalButton(new String(Character.toChars(9668)));
topPanel.add(back, BorderLayout.LINE_START);
JButton forward = getMinimalButton(new String(Character.toChars(9658)));
topPanel.add(forward, BorderLayout.LINE_END);
}
public JButton getMinimalButton(String text) {
JButton b = new JButton(text);
b.setFont(b.getFont().deriveFont(40f));
b.setMargin(new Insets(0,0,0,0));
b.setContentAreaFilled(false);
b.setBorder(null);
return b;
}
public final JComponent getUI() {
return ui;
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
JFrame f = new JFrame("Custom Pointer Buttons");
f.setDefaultCloseOperation(JFrame.DISPOSE_ON_CLOSE);
f.setContentPane(new CustomPointerButtons().getUI());
f.pack();
f.setLocationByPlatform(true);
f.setVisible(true);
}
};
SwingUtilities.invokeLater(r);
}
}
The problem is your random preferred size doesn't represent the real size of the triangle.
You can do something like:
public Dimension getPreferredSize() {
//return new Dimension(200, 100);
Rectangle bounds = triangle.getBounds();
return new Dimension(bounds.width, bounds.height);
}
You still have other problems because you will still have painting artifacts. You should always invoke super.paintComponent() when you override the method:
public void paintComponent(Graphics g) {
super.paintComponent(g);
((Graphics2D) g).fill(triangle);
}
However, this still doesn't really work because the button does other painting as well. So the real problem is that you should not be trying to do custom painting of the button. What you really want to do is custom painting of an Icon. Then you can just add the Icon to the button.
You may want to check out Playing With Shapes. You can use the ShapeIcon class to create your icons. This will provide a more flexible solution since I'm guess all shapes are not represented by an ascii character.

Java custom JComponent: Why is getMinimumSize() ignored?

I've overridden getPreferredSize() and getMinimumSize() in my custom JComponent. While getPreferredSize() is working as expected, getMinimumSize() instead is ignored as I am able to resize the JFrame until my custom component disappears. Why? How to avoid it?
Here is a complete runnable program that shows my point. What am I doing wrong?
Main.java
package swing.minimumsize;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class Main extends JFrame {
public Main() {
setTitle("Custom Component Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponent());
pack();
setVisible(true);
}
/**
* #param args
*/
public static void main(String[] args) {
Main main = new Main();
main.display();
}
}
CustomComponent.java
package swing.minimumsize;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
#SuppressWarnings("serial")
public class CustomComponent extends JComponent {
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#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);
}
}
..getMinimumSize() instead is ignored as I am able to resize the JFrame until my custom component disappears. Why? How to avoid it?
Check the 1st comment.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Main extends JFrame {
public Main() {
setTitle("Custom Component Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
add(new CustomComponent());
pack();
// enforces the minimum size of both frame and component
setMinimumSize(getSize());
setVisible(true);
}
public static void main(String[] args) {
Main main = new Main();
main.display();
}
}
class CustomComponent extends JComponent {
CustomComponent() {
setBackground(Color.YELLOW);
}
#Override
public Dimension getMinimumSize() {
return new Dimension(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
// paint the BG.
g.setColor(getBackground());
g.fillRect(0,0,getWidth(),getHeight());
int margin = 10;
Dimension dim = getSize();
g.setColor(Color.red);
g.fillRect(margin, margin, dim.width-margin*2, dim.height-margin*2);
}
}
What is ignored or listened to will depend on the layout of the container that holds the JComponent. Since you're adding your comopnent to a JFrame BorderLayout.CENTER, it makes sense that setMinimumSize would be ignored.
The preferred, minimum and maximum sizes are just "suggestions" that the layout manager can use or ignore when laying out components.
Edit:
In addition to Andrew's suggestion you can try using a layout manager that respects the minimum size:
Container contentPane = getContentPane();
contentPane.setLayout(new BoxLayout(contentPane, BoxLayout.X_AXIS));
add(new CustomComponent());
pack();
setMinimumSize(getMinimumSize());
setVisible(true);

Categories