I am building a small chess board for tactics, it would be a fun way to reflect upon on interests (programming and chess).
One problem I have currently face, although solved, is maintaining the board aspect ratio of 1:1.
The Board extends JPanel. Due to a problem with constraints, I have opted towards maintaining the board's physical size rather than it's rendered size. This would lead to faster operations when actually being used.
What I want it to look like, and have achieved:
The way I achieved this though seemed very hackish and is poor code by Skeet standards (thank you based Skeet).
public Frame() {
final JFrame frame = new JFrame("Chess");
final JPanel content = new JPanel(new BorderLayout());
final JPanel boardConfine = new JPanel(new BorderLayout());
final Board board = new Board();
boardConfine.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
int min = Math.min(boardConfine.getWidth(), boardConfine.getHeight());
int xBuffer = (boardConfine.getWidth() - min) / 2;
int yBuffer = (boardConfine.getHeight() - min) / 2;
board.setBounds(xBuffer, yBuffer, min, min);
}
});
boardConfine.add(board, BorderLayout.CENTER);
content.setBackground(new Color(205, 205, 205));
content.add(boardConfine, BorderLayout.CENTER);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setContentPane(content);
frame.pack();
frame.setVisible(true);
}
As seen above, I manually set the board's size and location. Even I have stated exhaustively that this shouldn't ever be done, but I couldn't find a solution to work. I need the board to fill the maximum possible area, yet maintain the aspect ratio.
If there are any suggestions (either code or concepts) you can provide, I really thank you for taking the time to help me with this elitist conundrum.
Although not a complete solution, the example below scales the board to fill the smallest dimension of the enclosing container. Resize the frame to see the effect.
Addendum: The ideal solution would be Creating a Custom Layout Manager, where you have access to the enclosing container's geometry, and setBounds() can maintain the desired 1:1 aspect ratio. A variation of GridLayout may be suitable. Grid coordinates can be calculated directly, as shown here.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/19531648/230513
*/
public class Test {
private static class MyPanel extends JPanel {
private static final int N = 8;
private static final int TILE = 48;
#Override
public Dimension getPreferredSize() {
return new Dimension(N * TILE, N * TILE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.gray);
int w = this.getWidth();
int h = this.getHeight();
int tile = Math.min(w, h) / N;
for (int row = 0; row < N; row++) {
for (int col = 0; col < N; col++) {
if ((row + col) % 2 == 0) {
g.fillRect(col * tile, row * tile, tile, tile);
}
}
}
}
}
private void display() {
JFrame f = new JFrame("Test");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new Test().display();
}
});
}
}
Related
This is a follow-up to this question. Any viable answer will also answer that one.
What layout may be used with as little modification as possible to replicate the aligning nature of a FlowLayout, but never linebreak and also be available in a from-top-to-bottom flavour?
The obvious candidate, BoxLayout, does not work nicely with JPanels. Consider the following two examples:
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
class App
{
public static void main(String[] args)
{
JFrame window = new JFrame();
Box box = new Box(BoxLayout.Y_AXIS);
for(int i = 0; i < 5; ++i)
{
JLabel label = new JLabel("XX");
box.add(label);
}
box.add(Box.createVerticalGlue());
window.add(box);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
This will properly display a vertical line of labels, beginning at the top and stretching as far towards the bottom as the labels take space. Good.
Modifying this, however, just a tiny bit:
public static void main(String[] args)
{
JFrame window = new JFrame();
Box box = new Box(BoxLayout.Y_AXIS);
for(int i = 0; i < 5; ++i)
{
JLabel label = new JLabel("XX");
JPanel panel = new JPanel();
panel.add(label);
box.add(panel);
}
box.add(Box.createVerticalGlue());
window.add(box);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
This will stretch all components of the Box to the same height, placing the labels far away from each other. Bad.
Overriding the JPanel's getPreferredSize and getMaximumSize methods (with getMinimumSize) has no effect and would be a bad way to fix it, because it relied on the components rather than the container and its layout.
Addendum:
Here is an already pretty successful attempt using GroupLayout. Unfortunately it did not seem to occur to the designer that among DEFAULT_SIZE and PREFERRED_SIZE a choice MINIMUM_SIZE would have been a good idea.
Furthermore if it is possible to invert the sequence of GroupLayout.SequentialGroup, the API is no help to figure out how. I for one certainly have no clue how to even extend that class.
import java.awt.Component;
import java.awt.ComponentOrientation;
import java.awt.Container;
import javax.swing.GroupLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
class LineLayout extends GroupLayout
{
public LineLayout(Container owner, int axis)
{
super(owner);
this.direction = axis;
this.direction |= owner.getComponentOrientation() != ComponentOrientation.LEFT_TO_RIGHT
? LineLayout.RIGHT_TO_LEFT : LineLayout.LEFT_TO_RIGHT;
this.setupGroups();
}
public LineLayout(Container owner, int axis, int orientation)
{
super(owner);
this.direction = axis;
this.direction |= orientation;
this.setupGroups();
}
#Override // to replicate FlowLayout functionality : this method is called from owner.add
public void addLayoutComponent(Component component, Object constraints)
{
if(constraints == null)
{
// REALLY surprised that this works, considering that overriding the JPanel's
// getMaximumSize method with getPreferredSize had no effect
this.horizontal.addComponent(component, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE);
this.vertical.addComponent (component, GroupLayout.DEFAULT_SIZE,
GroupLayout.DEFAULT_SIZE,
GroupLayout.PREFERRED_SIZE);
}
// TODO: else
}
protected void setupGroups()
{
super.setAutoCreateGaps(false); // does nothing
if((this.direction & LineLayout.AXIS) == LineLayout.Y_AXIS)
{
this.horizontal = super.createParallelGroup();
this.vertical = (this.direction & LineLayout.ORIENTATION) == LineLayout.RIGHT_TO_LEFT
? this.createSequentialInvertedGroup() : super.createSequentialGroup();
}
else
{
this.horizontal = (this.direction & LineLayout.ORIENTATION) == LineLayout.RIGHT_TO_LEFT
? this.createSequentialInvertedGroup() : super.createSequentialGroup();
this.vertical = super.createParallelGroup();
}
super.setHorizontalGroup(this.horizontal);
super.setVerticalGroup (this.vertical);
}
// How!?
// protected LineLayout.SequentialInvertedGroup createSequentialInvertedGroup() { return new LineLayout.SequentialInvertedGroup(); }
protected GroupLayout.SequentialGroup createSequentialInvertedGroup() { return super.createSequentialGroup(); } // placeholder
protected int direction;
protected GroupLayout.Group horizontal;
protected GroupLayout.Group vertical;
// not sure how reliable the constant field values of BoxLayout are, whether it's smart to assume them unchanging over the ages
public static final int AXIS = 0b1;
public static final int X_AXIS = 0b0; // = BoxLayout.X_AXIS;
public static final int Y_AXIS = 0b1; // = BoxLayout.Y_AXIS;
public static final int ORIENTATION = 0b10;
public static final int LEFT_TO_RIGHT = 0b00; // also top to bottom
public static final int RIGHT_TO_LEFT = 0b10; // also bottom to top
// No idea how; only has "add" methods; cannot actually do anything with the added components!?
//protected static class SequentialInvertedGroup extends GroupLayout.SequentialGroup
//{}
}
class Applikation
{
public static void main(String[] args)
{
JFrame window = new JFrame();
JPanel box = new JPanel();
box.setLayout(new LineLayout(box, LineLayout.Y_AXIS));
for(int i = 0; i < 5; ++i)
{
JLabel label = new JLabel("XX");
JPanel panel = new JPanel();
panel.add(label);
box.add(panel);
}
window.add(box);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setVisible(true);
}
}
If you try this out, you will note that there are still notable border spaces between the "XX" labels, taking up about 2/3 of an extra label per gap. While already much better than in the BoxLayout example, I do not think there is a good way to improve this spacing further.
private static int MAX_HEIGHT = 40;
private static final Dimension DIMENSION = new Dimension(Integer.MAX_VALUE, MAX_HEIGHT);
public static void main(String[] args)
{
JFrame window = new JFrame();
Box box = new Box(BoxLayout.Y_AXIS){
private static final long serialVersionUID = 1L;
#Override
public Component add(Component comp) {
comp.setMaximumSize(DIMENSION);
return super.add(comp);
}
};
for(int i = 0; i < 5; ++i)
{
JLabel label = new JLabel("XX");
JPanel panel = new JPanel();
panel.add(label);
box.add(panel);
}
window.add(box);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.pack();
window.setVisible(true);
}
You are using a Box for adding your components into. And the Documentation says:
a Box can use only a BoxLayout.
Now lets look into the Documentation for BoxLayout. It says:
BoxLayout pays attention to a component's requested minimum, preferred, and maximum sizes.
Now we have found the reason for the different outputs of your two examples. In your first example you are adding JLabels directly to your Box. Since they have a default maximumSize depending on their content they are not scaled by the Box.
In your second example you are adding JPanels to the Box that have your JLabels in it. A JPanel does not have a default maximumSize and so it is scaled by the Box.
So if you want to get the same output with JPanels as without you need your JPanels to have a maximumSize depending on their content means the JLabels.
So you could set a maximumSize manually. Something like that:
panel.setMaximumSize(new Dimension(100,20));
Or you use a different LayoutManager with your JPanels. One that calculates its size depending on its components. One that pays attention to a component's requested minimum, preferred, and maximum sizes.
Does this sound familiar to you? Right its from the Documentation of BoxLayout. So try to use a BoxLayout on your JPanels and you will get exactly the same result as your first example.
panel.setLayout(new BoxLayout(panel, BoxLayout.Y_AXIS));
I'm having a problem with GridLayout and the entire JPanel not being filled. I have a N * M Grid, and I'm filling it with N * M Tiles (They extend JPanel). After adding all these tiles there is still space on the bottom and the right side of the JPanel. I thought GridLayout was supposed to fill up the entire JPanel, and make everything in it evenly sized?
Edit: I didn't want to post all the code since there are multiple classes. Thought maybe there was a simple solution.
public class MainFrame extends JFrame {
private static final long serialVersionUID = 3985493842977428355L;
private final int FRAME_HEIGHT = 800;
private final int FRAME_WIDTH = 700;
private MainPanel mainPanel;
public MainFrame() {
super("Test");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(FRAME_HEIGHT, FRAME_WIDTH);
setLocationRelativeTo(null);
mainPanel = new MainPanel();
getContentPane().add(mainPanel);
setVisible(true);
}
}
public class MainPanel extends JPanel {
/**
*
*/
private static final long serialVersionUID = 3421071253693531472L;
private RoutePanel routePanel;
private ControlPanel controlPanel;
private GridBagConstraints gridBagConstraints;
private GridBagLayout gridBagLayout;
public MainPanel() {
routePanel = new RoutePanel();
controlPanel = new ControlPanel();
setBackground(Color.black);
gridBagLayout = new GridBagLayout();
setLayout(gridBagLayout);
gridBagConstraints = new GridBagConstraints();
createLayout();
}
private void createLayout() {
gridBagConstraints.weightx = 1;
gridBagConstraints.weighty = 1;
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 0;
gridBagConstraints.anchor = GridBagConstraints.NORTH;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagLayout.setConstraints(routePanel, gridBagConstraints);
add(routePanel);
gridBagConstraints.weightx = 1;
gridBagConstraints.weighty = .05;
gridBagConstraints.gridx = 0;
gridBagConstraints.gridy = 1;
gridBagConstraints.anchor = GridBagConstraints.SOUTH;
gridBagConstraints.fill = GridBagConstraints.BOTH;
gridBagLayout.setConstraints(controlPanel, gridBagConstraints);
add(controlPanel);
}
}
public class RoutePanel extends JPanel{
/**
*
*/
private static final long serialVersionUID = 4366703088208967594L;
private final int GRID_ROWS = 30;
private final int GRID_COLS = 47;
public RoutePanel() {
setLayout(new GridLayout(GRID_ROWS, GRID_COLS, 0, 0));
for(int i = 0; i < GRID_ROWS * GRID_COLS; i++) {
add(new Tile());
}
setBackground(Color.orange);
}
}
public ControlPanel() {
}
public Tile() {
setBackground(Color.gray);
Border blackline;
blackline = BorderFactory.createLineBorder(Color.black);
setBorder(blackline);
}
Since you're not posting code, we are forced to guess (and this is not good).
My guess: you may be setting the size or the preferred sizes of some of your components or the GUI, and this is causing gaps at the edges of your GUI.
Solution: don't do this. Let the components size themselves via their preferred sizes and the layout managers when pack() is called on the GUI.
For more helpful help, create and post your sscce. For a question like this, code is really mandatory, and I'm a bit surprised actually that you didn't post yours.
Edit 1
For example, note the difference in the GUI's produced:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.GridLayout;
import javax.swing.*;
public class GapExample {
private static final int M = 5;
private static final int N = 6;
private static final int PREF_W = 700;
private static final int PREF_H = 500;
#SuppressWarnings("serial")
private static void createAndShowGui() {
// *** attempt 1: set preferredSize of the mainPanel JPanel ***
JPanel mainPanel = new JPanel(new GridLayout(M, N));
mainPanel.setBorder(BorderFactory.createLineBorder(Color.red));
mainPanel.setPreferredSize(new Dimension(PREF_W, PREF_H));
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
String text = String.format("Foo [%d, %d]", i, j);
JLabel label = new JLabel(text, SwingConstants.CENTER);
label.setBorder(BorderFactory.createLineBorder(Color.blue));
mainPanel.add(label);
}
}
JOptionPane.showMessageDialog(null, mainPanel,
"Attempt 1, setPreferredSize of mainPane",
JOptionPane.PLAIN_MESSAGE);
// *** attempt 2: override the getPreferredSize of the JLabel cells in the
// grid ***
mainPanel = new JPanel(new GridLayout(M, N));
mainPanel.setBorder(BorderFactory.createLineBorder(Color.red));
final Dimension labelPrefSize = new Dimension(PREF_W / N, PREF_H / M);
for (int i = 0; i < M; i++) {
for (int j = 0; j < N; j++) {
String text = String.format("Foo [%d, %d]", i, j);
JLabel label = new JLabel(text, SwingConstants.CENTER) {
#Override
public Dimension getPreferredSize() {
return labelPrefSize;
}
};
label.setBorder(BorderFactory.createLineBorder(Color.blue));
mainPanel.add(label);
}
}
JOptionPane
.showMessageDialog(null, mainPanel,
"Attempt 2, override getPreferredSize of the JLabel cells in the grid",
JOptionPane.PLAIN_MESSAGE);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
In the second dialog, I have the JLabels return a preferred size and let the containers that hold them set the best size to display the GUI, and you'll with the second attempt, the grid fits much better.
This displays for the first (bad) attempt a gap between the inner and outer components:
And the second (good) attempt shows no such gap:
GridLayout was supposed to fill up the entire JPanel, and make
everything in it evenly sized
these two are opposing forces, the manager can do both at the same time only if the panel width is a multiple of the column width:
panel.width == column.width * column.count
If that's not possible, it favors the equal-size constraint, by sizing the children to the largest width to make them equal and positions the whole block in the center of parent, thus exposing the parent's background. Basically, you are seeing the effects of integer math :-)
As to the discussion in the other answer,
First off: my take on setXXSize is well known - it's always wrong to use in application code I think it's a design accident, should never have happened in the first place because most of the time it introduces more problems than it seems (superficially!) to solve.
Next-nearest solution seems to override getXXSize: if done to return an arbitrary hard-coded fixed size, it's only marginally better than calling setXXSize (in not propagating worst practices :): the return value should be related to internal state. It should keep internal calculation (if any) of super intact and can modify it. Plus strictly speaking, it should comply to super's contract which indeed states (a bit hidden in setXXSize) that the dimension applied via setXXSize takes precedence
Sets the preferred size of this component to a constant value.
Subsequent calls to getPreferredSize will always return this value.
In code:
// always wrong in application code
someComponent.setPreferredSize(labelPrefSize);
// suboptimal: arbitrary hard-coded fixed size
#Override
public Dimension getPreferredSize() {
return new Dimension(labelPrefSize);
}
// good: fixed value based on internal state
#Override
public Dimension getPreferredSize() {
return new Dimension(labelPrefSize);
}
// the size has some meaning f.i. when painting
#Override
protected void paintComponent(Graphics g) {
g.drawRect(0, 0, labelPrefSize.width, labelPrefSize.height);
}
// good: prefsize relative to super's calculation
#Override
public Dimension getPreferredSize() {
Dimension labelPrefSize = super.getPreferredSize();
int maxSide = Math.max(labelPrefSize.width, labelPrefSize.height);
labelPrefSize.setSize(maxSide, maxSide);
return labelPrefSize;
}
// better: prefsize relative to default calculation
// _and_ comply to super's contract
#Override
public Dimension getPreferredSize() {
Dimension labelPrefSize = super.getPreferredSize();
if (isPreferredSizeSet()) return labelPrefSize;
// any of the good thingies ...
}
Ideally, though, you would not touch the internal calculation of sizing hints at all. Instead, use a LayoutManager that allows to fine tune the layout to exactly suit your needs. Remember: the manager takes the hints and is free to size however she likes anyway :-)
Update
edited the above trying to emphasize the "related to internal state" thingy: sizing hints should return their own isolated, ego-centric requirement without caring about context. So overriding to return something that pleases an outside need is suboptimal.
I am making a simple Java Swing GUI chessboard where the player can drag and drop pieces. The problem is that, because of the border around the frame (with the title on top, maximize/minimize/close buttons, etc), the coordinates are skewed off - (0, 0) is the upper-left-hand corner of the frame, that is, a little above the X button, but the GUI starts building itself right below the title bar, so the GUI doesn't align with the coordinates, and things do not end up working the way they should. Additionally, when I set the size of the frame to, for instance, 100 x 100, the lower part and some of the right-hand part of my GUI is cut off because the frame doesn't compensate for its border. When I run it as an applet, I don't have this problem, but I don't want to do that. How can I either get rid of that border around my frame window so I can just have the plain GUI, or have the coordinates set themselves up properly?
sscce:
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.JFrame;
public class class1 extends JFrame{
public class1(){
addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent evt){
System.out.print(evt.getPoint());
}
});
}
public static void main(String[] args){
class1 c = new class1();
c.setTitle("Test");
c.setSize(320, 320);
c.setLocationRelativeTo(null);
c.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
c.setVisible(true);
}
}
It's hard to know what is wrong with your code without the code, but I do know that if you go the easy way by using various layout managers, and let these managers do the laying out of components for you and the sizing of things as well, including calling pack() on the JFrame, usually things fall easily and well into place. So again, don't set the size of anything, but rather let the components' preferred sizes and the layout managers do this for you.
If this advice doesn't help, please give us more information and code, preferably an sscce, a small compilable and runnable program that doesn't do anything other than demonstrate your problem.
Edit: I am assuming that this is a Swing GUI. Please verify if this is so.
Edit 2: One problem you're having is that you're setting the size of a JFrame not taking into account its "decorations" including the menu bar, the resize/maximize/close icon. Again, you shouldn't be setting sizes directly, but if you must better override the getPreferredSize() method of the JPanel that holds your grid.
Edit 3: For example:
import java.awt.*;
import javax.swing.*;
public class Grid extends JPanel {
public static final Color DARK_COLOR = Color.red.darker().darker().darker();
public static final Color LIGHT_COLOR = Color.lightGray.brighter();
public static final int SQUARE_SIDE = 60;
private static final int ROW_COUNT = 8;
#Override
public Dimension getPreferredSize() {
return new Dimension(ROW_COUNT * SQUARE_SIDE, ROW_COUNT * SQUARE_SIDE);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (int i = 0; i < ROW_COUNT; i++) {
for (int j = 0; j < ROW_COUNT; j++) {
Color c = (i % 2 == j % 2) ? LIGHT_COLOR : DARK_COLOR;
g.setColor(c);
int x = i * SQUARE_SIDE;
int y = j * SQUARE_SIDE;
g.fillRect(x, y, SQUARE_SIDE, SQUARE_SIDE);
}
}
}
public Grid() {
// TODO Auto-generated constructor stub
}
private static void createAndShowGui() {
Grid mainPanel = new Grid();
JFrame frame = new JFrame("Grid");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}
I've looked at many websites. Without the panels, the labels appear correctly, with the panels it give the the error:
Exception in thread "main" java.lang.NullPointerException
so what can I do to fix this?
here is the source code:
JLabel button[] = new JLabel[100];
JPanel[] panel = new JPanel[100];
for (int i = 0; i < button.length; i++) {
a = a + 50;
if (a > 549) {
b = b + 50;
a = 50;
}
button[i] = new JLabel("hi");
frame.add(button[i]); //is this necessary?
button[i].setVisible(true); // is this necessary?
button[i].setSize(50,50);
panel[i].add(button[i]);
panel[i].setVisible(true);
panel[i].setBounds(a, b, 50, 50);
frame.add(panel[i]);
}
Whats wrong with this, how can I fix it? just so you know, it should have 100 labels that say hi in a 10 by 10 array.
this is what it looks like:
Creating an array of JPanel only creates the array. It doesn't create any JPanel to fill the array. The arrays is thus filled with nulls. You must create a JPanel for each element of the array:
panel[i] = new JPanel();
panel[i].add(button[i]);
Moreover, a component may only have one ancestor. The button must be added to the frame or to the panel, but not both. If you want the button in the panel, it must be added to the panel.
Components are visible by default (except top-level ones like frames or dialogs which must be made visible). You don't need to call button.setVisible(true).
You should definitely learn to use layout managers rather than setting the size and bounds of your components explicitely. That's the only way to have good-looking, portable GUI apps. Read http://docs.oracle.com/javase/tutorial/uiswing/layout/using.html
don't use frame.setLayout(null); use frame.setLayout(new GridLayout(10,10,10,10)); instead, for example
import java.awt.*;
import javax.swing.*;
public class CustomComponent1 extends JFrame {
private static final long serialVersionUID = 1L;
public CustomComponent1() {
setTitle("Custom Component Test / GridLayout");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void display() {
setLayout(new GridLayout(10, 10, 10, 10));
for (int row = 0; row < 100; row++) {
add(new CustomComponents1());
}
//pack();
// enforces the minimum size of both frame and component
setMinimumSize(getMinimumSize());
setPreferredSize(getPreferredSize());
setVisible(true);
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
CustomComponent1 main = new CustomComponent1();
main.display();
}
};
javax.swing.SwingUtilities.invokeLater(r);
}
}
class CustomComponents1 extends JLabel {
private static final long serialVersionUID = 1L;
#Override
public Dimension getMinimumSize() {
return new Dimension(20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(20, 20);
}
#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);
}
}
I was using Java JRE 1.6.7 and had a JComponent and a JScrollPane. I couldn't get double buffering to work on this which always resulted in a flicker. I had the buffering taken care of if I used a Canvas, but this resulted in problems when used in conjunction with a JScrollPane.
So I downloaded JRE 1.6.18 in hopes that one of these problems would be fixed. Well now the JComponent inside the JScrollPane is not drawing properly at all. It draws only the outside region of the JComponent, as if the JScrollPane is drawing on top of it except for the borders.
Here is an example of code that is not drawing..this results in a 1-pixel-wide white outline of the area where drawing should occur:
public void paint(Graphics arg0) {
Graphics2D graphics = (Graphics2D) arg0;
graphics.setColor(Color.WHITE);
graphics.fillRect(0, 0, (int) getWidth(), (int) getHeight());
Any help is greatly appreciated!
-Craig
try to make an override from paintComponent(Graphics g) instead of paint(Graphics g).
paintComponent is the method you have to override for costum drawing.
Are you sure you can see the white rectangle, try to use red or something else you can see good.
It looks like you're making progress, but you might like to see the tutorial examples, too.
Martijn Courteaux's analysis is correct: you should override paintComponent(). Also, it's a bad idea to mix AWT and Swing components. Both ideas are discussed in Painting in AWT and Swing.
Scrolling shouldn't cause flickering. Here's an example that scrolls a grid of components and paints a checkerboard on the background.
import java.awt.*;
import javax.swing.*;
public class Scrolling extends JFrame {
private static final int MAX = 8;
private static final int SIZE = 480;
private static final Color light = new Color(0x40C040);
private static final Color dark = new Color(0x408040);
private static class MyPanel extends JPanel {
public MyPanel() {
super(true);
this.setLayout(new GridLayout(MAX, MAX, MAX, MAX));
this.setPreferredSize(new Dimension(SIZE, SIZE));
for (int i = 0; i < MAX * MAX; i++) {
this.add(new JLabel(String.valueOf(i), JLabel.HORIZONTAL));
}
}
#Override
public void paintComponent(final Graphics g) {
int w = this.getWidth()/MAX;
int h = this.getHeight()/MAX;
for (int row = 0; row < MAX; row++) {
for (int col = 0; col < MAX; col++) {
g.setColor((row + col) % 2 == 0 ? light : dark);
g.fillRect(col * w, row * h, w, h);
}
}
}
}
public Scrolling() {
this.setLayout(new BorderLayout());
final MyPanel panel = new MyPanel();
final JScrollPane scrollPane = new JScrollPane(panel);
scrollPane.getHorizontalScrollBar().setUnitIncrement(16);
scrollPane.getVerticalScrollBar().setUnitIncrement(16);
this.add(scrollPane, BorderLayout.CENTER);
this.pack();
this.setSize(SIZE - SIZE / 3, SIZE - SIZE / 3);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
}
public static void main(final String[] args) {
EventQueue.invokeLater(new Runnable() {
public void run() {
new Scrolling().setVisible(true);
}
});
}
}
Ok, I've come up with a slight answer.
Instead of calling
scrollPane.add(containerCanvas);
I'm calling
new JScrollPane(containerCanvas);
This works in a sense. However, it now is disallowing the JScrollPane bars from showing up. I have no idea why this is, but am currently looking into it. But at least the component is drawing again.