I have picture as a JLabel and what I want to do is when I click the JLabel, there would be another Jlabel in a form of rectangle would appear.
I tried using paintComponent or paint and unfortunately, it doesn't work for my program. So I have to think that there are other ways, other than paintComponent and/or paint.
The aim is tag to a certain part of an image with a name, like in Facebook.
Wow.
Based on Pace's description of the problem, you're going to have to do the following (I think):
The picture will have to be painted directly on a JPanel. This is so when someone clicks on the picture, you can get the mouse x and y coordinates through the JPanel action listener.
You will then create a JDialog with a transparent JPanel that has a border, a text box for the name, and an OK button together. (Might as well put everything together in one dialog window.) The JDialog will be movable, but you're going to have to create a JDialog listener that keeps track of the x and y coordinates of the top left edge or the center of the transparent JPanel in the JDialog.
The JDialog JPanel won't really be transparent. You'll have to create the illusion of transparency by noting the position of the JDialog JPanel on the picture JPanel, and copying the part of the image from the picture JPanel to the JDialog JPanel.
The rest should be rather straightforward, compared to getting the JDialog to work properly.
Edited to add: Here's an extension of JPanel that will draw a picture directly on the JPanel and process mouse pressed events.
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseEvent;
import javax.swing.JPanel;
import javax.swing.event.MouseInputAdapter;
public class PicturePanel extends JPanel {
private static final long serialVersionUID = 1L;
protected Image picture;
public PicturePanel(Image picture) {
this.picture = picture;
createPartControl();
}
protected void createPartControl() {
new JPanel();
int width = picture.getWidth(getParent());
int height = picture.getHeight(getParent());
addMouseListener(new CoordinateListener());
setPreferredSize(new Dimension(width, height));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
int width = picture.getWidth(getParent());
int height = picture.getHeight(getParent());
g.drawImage(picture, 0, 0, width, height, null);
}
public class CoordinateListener extends MouseInputAdapter {
#Override
public void mousePressed(MouseEvent event) {
int x = event.getX();
int y = event.getY();
System.out.println("(" + x + ", " + y + ")");
}
}
}
Related
I'm trying to make a program that can pretty much draw rectangles, shapes on the screen. So far I've made a GUI that has a menu bar and toolbar with some buttons (which aren't functioning yet).
Now I'm stuck with this problem that my rectangles aren't showing up on my drawing panel and I'm thinking it's due to JPanels who overlap on each other that my DrawRectangle class. But I'm not sure.
This is my DrawRectangle class where I wan't to draw rectangles and then put them in to my DrawingPanel (if that's possible).
package Shapes;
import java.awt.Graphics;
import javax.swing.JPanel;
public class DrawRectangle extends JPanel
{
private static final long serialVersionUID = 1L;
public int old_x;
public int old_y;
public int new_x;
public int new_y;
public DrawRectangle(int old_x, int old_y, int new_x, int new_y)
{
this.old_x = old_x;
this.old_y = old_y;
this.new_x = new_x;
this.new_y = new_y;
repaint();
}
public void paintComponent(Graphics g)
{
g.drawRect(old_x, old_y, new_x, new_y);
}
}
This is my DrawingPanel class where I would like to store all the painted graphics from my DrawRectangle class
package Panel;
import java.awt.Color;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import Shapes.DrawRectangle;
public class DrawingPanel
{
private JPanel drawPanel;
private int tool = 1;
int currentX, currentY, oldX, oldY;
public DrawingPanel()
{
drawPanel = new JPanel();
drawPanel.setBackground(Color.WHITE);
drawPanel.setBorder(BorderFactory.createLineBorder(Color.black));
// Testing, neither of them are showing up (trying without mouse)
new DrawRectangle(100,100,100,100);
new DrawRectangle(200,200,300,100);
drawPanel.addMouseListener(new MouseAdapter(){
public void mousePressed(MouseEvent evt){
drawPanelMousePressed(evt);
}
public void mouseReleased(MouseEvent evt){
drawPanelMouseReleased(evt);
}
});
drawPanel.addMouseMotionListener(new MouseMotionAdapter(){
public void mouseDragged(MouseEvent evt){
drawPanelMouseDragged(evt);
}
});
}
public JPanel getDrawPanel()
{
return drawPanel;
}
private void drawPanelMouseDragged(MouseEvent evt) {
if (tool == 1) {
currentX = evt.getX();
currentY = evt.getY();
oldX = currentX;
oldY = currentY;
System.out.println(currentX + " " + currentY);
System.out.println("PEN!!!!");
}
}
private void drawPanelMousePressed(MouseEvent evt) {
oldX = evt.getX();
oldY = evt.getY();
System.out.println(oldX + " " + oldY);
}
private void drawPanelMouseReleased(MouseEvent evt) {
currentX = evt.getX();
currentY = evt.getY();
}
}
Also I'm considering my JPanel container that might be responsible for my DrawingPanel content not showing up. Here's my ContainerPanel class
package Panel;
import java.awt.BorderLayout;
import javax.swing.JPanel;
public class ContainerPanel
{
private JPanel containerPanel;
public ContainerPanel()
{
containerPanel = new JPanel(new BorderLayout());
containerPanel.setOpaque(true);
containerPanel.add(new DrawingPanel().getDrawPanel(), BorderLayout.CENTER);
containerPanel.add(new ToolBarPanel().getToolBarPanel(), BorderLayout.NORTH);
}
public JPanel getContainerPanel()
{
return containerPanel;
}
}
First time in stackoverflow :)
drawPanel = new JPanel();
When you create a panel, you assign it to a variable so you can use the variable in the getDrawPanel() method.
new DrawRectangle(100,100,100,100);
Here you create a DrawRectangle but you don't do anything with it. You don't add it to the drawPanel or anything so it will never get painted.
and I'm thinking it's due to JPanels who overlap on each other that my DrawRectangle class.
Even if you fix the above problem and add your DrawRectangle class to the DrawPanel you will still have problems:
A JPanel is opaque so the last painted panel will cover the bottom panel.
Your DrawRectangle panel doesn't have a preferredSize since you didn't override the getPreferredSize() method so the size will be 0 so there is nothing to paint
A JPanel uses a FlowLayout so each component will be positioned on the panel based on the rules of the layout manager, so your Rectangle will not paint at the location you think it should be.
You need to start by reading the Swing Tutorial for Swing basics. Maybe section on:
Custom Painting - will show you how to properly override the paintCompnent() method and implement a getPreferredSize() method
Doing Without a Layout Manager - because you will want to place the components at a specific location (ie. the x/y of the Rectangle).
Having said all that an easier solution to do custom painting of all the Rectangle on a single panel. Check out Custom Painting Approaches. It has working examples to demonstrate the two common ways to do this:
Add object you want to paint to a List and then the painting method will iterate through the List to paint each object.
Paint directly to a BufferedImage and then just paint the BufferedImage either by using a JLabel of by custom painting.
I'm implementing a simple chat application, using Java. I want my chat application to have the "bubble" message style like modern message apps, so I've built 2 classes LeftArrowBubble and RightArrowBubble which extend JPanel to illustrate sender & receiver bubbles, like this:
This is the code for my LeftArrowBubble class (quite alike for RightArrowBubble):
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.awt.geom.Area;
import java.awt.geom.RoundRectangle2D;
import javax.swing.JPanel;
/**
* #author harsh
*/
public class LeftArrowBubble extends JPanel {
private static final long serialVersionUID = -5389178141802153305L;
private int radius = 10;
private int arrowSize = 12;
private int strokeThickness = 3;
private int padding = strokeThickness / 2;
#Override
protected void paintComponent(final Graphics g) {
final Graphics2D g2d = (Graphics2D) g;
g2d.setColor(new Color(0.5f, 0.8f, 1f));
int x = padding + strokeThickness + arrowSize;
int width = getWidth() - arrowSize - (strokeThickness * 2);
int bottomLineY = getHeight() - strokeThickness;
g2d.fillRect(x, padding, width, bottomLineY);
g2d.setRenderingHints(new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON));
g2d.setStroke(new BasicStroke(strokeThickness));
RoundRectangle2D.Double rect = new RoundRectangle2D.Double(x, padding, width, bottomLineY, radius, radius);
Polygon arrow = new Polygon();
arrow.addPoint(20, 8);
arrow.addPoint(0, 10);
arrow.addPoint(20, 12);
Area area = new Area(rect);
area.add(new Area(arrow));
g2d.draw(area);
}
}
Now I have a JFrame window with a JScrollPane on it, which looks like this:
What I want to do now is when I click on that CreateNewBubble button, a new Left(or Right)ArrowBubble JPanel will be created & displayed inside that JScrollPane (and this JScrollPane will be vertical scrollable if there're more bubbles inside of it). I've already tried this way:
private void jButton1ActionPerformed(java.awt.event.ActionEvent evt) {
BubbleTest.LeftArrowBubble leftArrowBubble = new BubbleTest.LeftArrowBubble();
jScrollPane1.add(leftArrowBubble);
}
But it didn't work as I expected: nothing shows up in the JScrollPane after clicking the button!
I've been stuck at this problem for hours, really appreciate if you guys can help!
You can't use .add that way on a JScrollPane. A JScrollPane can only scroll a single component, which is set by either passing it to its constructor, or by calling .setViewportView.
Instead, create a separate container for the bubbles, such as a vertical Box, and set that as the single component scrolled by the scroll pane:
Box box = new Box(BoxLayout.Y_AXIS);
JScrollPane jScrollPane1 = new JScrollPane(box);
When you add a bubble, add it to the box (and call .revalidate() to lay it out):
box.add(leftArrowBubble);
box.revalidate();
Edit: Also, your bubbles will not, by default, have any size, unless you give them a size such as by calling setPreferredSize or by overriding getPreferredSize or by putting components inside them.
With JScrollPane you should always add components to the scroll pane's JViewPort. Look at the documentation here, it explains the concept behind the class rather well.
Short summary: A JScrollPane holds the scroll bars and a view port. The view port is a component that displays only a portion of its content - in this case the part that is visible on screen. The scroll bars tell the view port which portion to show.
I have the following code. Basically I have a frame which has a background image. I also have three panels within the frame: panels 1, 2 and 3. 2 & 3 work fine as I haven't subclassed them. However, panel 1 as soon as I subclassed it i.e. put the logic inside the paintComponent method of JPanel stopped working as that method is never called and foo is never printed. I'm not able to figure out why. Would appreciate your help. I've tried a few suggestions from other similar threads and they haven't helped.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Toolkit;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.BorderFactory;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
public class Test {
public static void main(String[] args) throws IOException {
JFrame.setDefaultLookAndFeelDecorated(true);
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
int fooPanelX = 5;
int fooPanelY = 160;
int fooPanelWidth = 470;
int fooPanelHeight = 305;
int bar0PanelX = 5;
int bar0PanelY = 550;
int bar0PanelWidth = 230;
int bar0PanelHeight = 210;
int bar1PanelX = bar0PanelX * 2 + bar0PanelWidth + bar0PanelX;
int bar1PanelY = bar0PanelY;
int bar1PanelWidth = bar0PanelWidth;
int bar1PanelHeight = bar0PanelHeight;
JPanel panel1 = new Panel1(fooPanelX, fooPanelY, fooPanelWidth, fooPanelHeight);
JPanel panel2 = new JPanel();
panel2.setBackground(Color.WHITE);
panel2.setLocation(bar0PanelX, bar0PanelY);
panel2.setSize(bar0PanelWidth, bar0PanelHeight);
panel2.setOpaque(false);
panel2.setBorder(BorderFactory.createLineBorder(Color.WHITE));
panel2.setBounds(bar0PanelX, bar0PanelY, bar0PanelWidth, bar0PanelHeight);
JPanel panel3 = new JPanel();
panel3.setBackground(Color.WHITE);
panel3.setLocation(bar1PanelX, bar1PanelX);
panel3.setSize(bar1PanelWidth, bar1PanelHeight);
panel3.setOpaque(false);
panel3.setBorder(BorderFactory.createLineBorder(Color.WHITE));
panel3.setBounds(bar1PanelX, bar1PanelY, bar1PanelWidth, bar1PanelHeight);
JLabel imagePanel = new JLabel(new ImageIcon(ImageIO.read(new File("image.png"))));
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == 27) {
System.exit(0);
}
}
});
frame.setContentPane(imagePanel);
frame.getContentPane().add(panel1);
frame.getContentPane().add(panel2);
frame.getContentPane().add(panel3);
frame.setLocation((int) (screenSize.getWidth() * 0.75),
(int) (screenSize.getHeight() * 0.25));
frame.pack();
frame.setVisible(true);
}
#SuppressWarnings("serial")
static class Panel1 extends JPanel {
int x, y, w, h;
public Panel1(int x, int y, int w, int h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
#Override
public void paintComponent(Graphics graphics) {
System.out.println("foo");
super.paintComponents(graphics);
setBackground(Color.WHITE);
setLocation(x, y);
setSize(w, h);
setOpaque(true);
setBorder(BorderFactory.createLineBorder(Color.WHITE));
}
}
}
Update: If you aren't able to find the issue then please could you provide me with an alternative way of doing the following. I need a frame with a background image and three panels on top of the background image. The three panels have to have pixel perfect locations and sizes on the background image to look right. That's it pretty much. I'll be repainting the three panels but the background image will remain the same.
Well, the problem is that you are not using an appropriate LayoutManager.
JLabel does not come with any LayoutManager by default. So when you add your Panel1, it has a size of 0x0 and is located in (0,0) and since no LayoutManager will change that, it will keep that size and location. With empty bounds, your component is never painted, hence your paintComponent method is never called.
Now, you should NEVER do this in paintComponent:
setBackground(Color.WHITE);
setLocation(x, y);
setSize(w, h);
setOpaque(true);
setBorder(BorderFactory.createLineBorder(Color.WHITE));
Do that in the constructor or some other method. paintComponent is meant for "painting a component", not changing its properties.
I decided to tackle the problem in a very different way. This is how I did it. I startedc completely from scratch with my code. I created a JFrame instance and a Canvas instance (the canvas was subclassed). In the canvas I used drawImage() to apply the background image. Then for each of the three areas that I wanted to animate on the background image, instead of creating three JPanels, I simply used fillRect() within the canvas to fill the right areas on the image. That's it. Nice and simple. The repaint() every second does flickr on the three areas and that's the next challenge. I'm guessing I have to use double buffering but it's not something I've used before so I'll look into that next. Anyway, using a single canvas in place of three JPanels proved a heck of a lot simpler and the reason I was able to do that was because the background image provided everything else visually. All I had to do was drawImage() and fillRect(). Thanks for all contributions.
Update: I have now completed this task. There was one thing I changed about the above. While attempting to double buffer with Canvas I had a few issues: the usual "component must have valid peer" exception. While looking into that I learnt that one should not use Canvas in Swing and that the practice of using it was mixing AWT and Swing. So I swapped it out for JComponent (as I didn't need anything that JPanel offered). And as Swing is double buffered by default my work was complete. No flicker and simplified code.
What I want to do:
Create a JPanel's subclass to draw a simple overlay on top of contained components.
Why don't I use JLayeredPane?
See JComponent#isOptimizedDrawingEnabled().
When a JMenu is present in a JFrame, adding a JPanel with an overridden paintChildren(Graphics) method, an incorrect coordinate starting point is provided in the passed Graphics object, as observed with this code sample:
import java.awt.Color;
import java.awt.FontMetrics;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
public final class Sscce {
public static void main(String[] args) {
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
// a normal frame
JFrame f = new JFrame();
// set up a simple menu
JMenuBar mb = new JMenuBar();
JMenu m = new JMenu("Test");
JMenuItem mi = new JMenu("Whatever");
m.add(mi);
mb.add(m);
f.setJMenuBar(mb);
// a panel with a simple text overlay over components.
// works much faster than JLayeredPane, which doesn't have
// isOptimizedDrawingEnabled()
JPanel p = new JPanel() {
#Override
public void paint(Graphics g) {
// I'm not so stupid to draw stuff here
super.paint(g);
// JavaDoc: delegates to paintComponent, paintBorder, paintChildren
// in that order
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// it is common knowledge that children are painted after parent
Graphics tmp = g.create();
try {
tmp.setColor(Color.MAGENTA);
tmp.fillRect(0, 0, getWidth(), getHeight());
} finally {
tmp.dispose();
}
}
#Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
// draw some text
FontMetrics fm = g.getFontMetrics();
// will be drawn outside panel; under menu
g.drawString("TEST TOP/LEFT", 0 + getX(), 0 + getY());
final String s = "TEST BOTTOM/RIGHT";
// will be drawn noticeably above the bottom
g.drawString(s,
getWidth() - fm.charsWidth(s.toCharArray(), 0, s.length()),
getHeight() - fm.getHeight());
}
};
// add something to the panel
p.add(new JTextArea(10, 15));
f.add(p);
f.pack();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setVisible(true);
}
});
} catch (Throwable t) {
// this is a SSCCE
}
}
}
The first string is drawn outside of JPanel (under the JMenu), even though both coordinates are non-negative.
The second string is NOT drawn at the bottom right corner. It is pushed up by the height of the JMenu.
Image
Even though:
When AWT invokes this method, the Graphics object parameter is pre-configured with the appropriate state for drawing on this particular component:
The Graphics object's color is set to the component's foreground
property.
The Graphics object's font is set to the component's font
property.
The Graphics object's translation is set such that the
coordinate (0,0) represents the upper left corner of the component.
The Graphics object's clip rectangle is set to the area of the
component that is in need of repainting.
Programs must use this Graphics object (or one derived from it) to render output. They are free to change the state of the Graphics object as necessary.
What am I doing wrong?
The first string is drawn outside of JPanel (under the JMenu), even though both coordinates are non-negative. The second string is NOT drawn at the bottom right corner. It is pushed up by the height of the JMenu.
In both cases, note that drawString() expects the coordinates to represent the baseline of the String. The font;s ascent and descent are useful in this context. It may be a coincidence that mb.getHeight() and fm.getHeight() are of comparable magnitude.
#Override
protected void paintChildren(Graphics g) {
super.paintChildren(g);
// draw some text
FontMetrics fm = g.getFontMetrics();
// will be drawn outside panel; under menu
g.drawString("TEST TOP/LEFT", 0, fm.getAscent());
final String s = "TEST BOTTOM/RIGHT";
// will be drawn noticeably above the bottom
g.drawString(s, getWidth() - fm.stringWidth(s),
getHeight() - fm.getDescent());
}
I'm working on a game project for myself to improve my Java skills and I today I've hit a problem I cannot seem to solve with the aid of the internet. Most of this problem has been solved but this last little bit still remains defiant!
I've got an Object that extends Canvas and creates a map I want to display. I've added this Canvas to a JPanel, which is then added to the viewport of a JScrollPane (which is added to the JFrame of the game). A ChangeListener is added to the JScrollPane's viewport, which will perform the validate method on the JFrame and the repaint method on the viewport.
The JFrame itself has JMenuBar, where in the menu structure there is a 'New Game' option (among other not yet implemented JMenuItems) that will create a new Canvas object and replaces the old one and validates (using the validate methods) repaints the JScrollPane.
The result is that when I press the New Game JMenuItem, a new map is drawn out in my scollpane. It displays the map (the Canvas Object) correctly, with the scollbars showing up. The menu is on top of the Canvas and scrollpane, which is correct.
But when I slightly change the position of the scollbar (horizontal or vertical, using the bar or the buttons in the bar) it results in the Canvas Object being translated across the JFrame. Meaning it will overlap possibly a scollbar, the menubar (and also immediately is drawn over an opened menu) with the piece of the Canvas object out of view not being shown. Only when a scrollbar actually hits it's extreme points (cannot go any further), it repaints and validates the whole scene as it should. But obviously, I want it to do this too when I'm only nudging a scrollbar or something similar.
My question is: How can I make this work as intended?
I think I need to do something with the ChangeListener, but I can't seem to find a solution by myself.
As I noticed most of you guys ask for source code, I've provided this for you:
package otherFiles;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
import javax.swing.JMenu;
import javax.swing.JMenuBar;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
public class ProblemDemonstrator {
private static JScrollPane dispArea = null;
private static JPanel mapPanel = null;
private static JFrame thisFrame = null;
private static final int FRAME_WIDTH = 300;
private static final int FRAME_HEIGTH = 300;
private static boolean mode = false;
public static void main(String[] args){
JFrame mainMenu = myMenuFrame();
mainMenu.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
mainMenu.setVisible(true);
}
public static JFrame myMenuFrame(){
thisFrame = new JFrame("Problem Demonstrator");
thisFrame.setSize(FRAME_WIDTH, FRAME_HEIGTH);
thisFrame.setLayout(new BorderLayout());
//Create the menu bar and corresponding menu's.
JMenuBar menuBar = new JMenuBar();
thisFrame.setJMenuBar(menuBar);
menuBar.add(createFileMenu(),BorderLayout.NORTH);
//Create a scroll-able game display area
dispArea = new JScrollPane();
dispArea.setSize(thisFrame.getSize());
thisFrame.getContentPane().add(dispArea, BorderLayout.CENTER);
return thisFrame;
}
private static JMenu createFileMenu()
{
JMenu menu = new JMenu("File");
menu.add(createFile_NewGameItem()); //New game button
return menu;
}
/**
* The button for the creation of a new game.
* #return The JMenuItem for starting a new game.
*/
private static JMenuItem createFile_NewGameItem()
{
JMenuItem item = new JMenuItem("New Game");
class MenuItemListener implements ActionListener
{
#Override
public void actionPerformed(ActionEvent arg0)
{
System.out.println("actionPerformed - New Game [JMenuItem]");
if(mapPanel == null){
mapPanel = createGameMap(mode);
dispArea.setViewportView(mapPanel);
}else{
dispArea.getViewport().remove(mapPanel);
mapPanel = createGameMap(mode);
dispArea.setViewportView(mapPanel);
}
//'flip' mode
if(mode){
mode = false;
}else{
mode = true;
}
thisFrame.pack();
thisFrame.setSize(FRAME_WIDTH, FRAME_HEIGTH);
dispArea.repaint();
thisFrame.validate();
}
}
ActionListener l = new MenuItemListener();
item.addActionListener(l);
return item;
}
/**
* This creates the displayable map that has to go in the JScrollPane
* #param mode Just a variables to be able to create 'random' maps.
* #return The displayable map, a JPanel
*/
private static JPanel createGameMap(boolean mode){
/**
* This is a quick version of the planets I generate for the map.
* Normally this is a another class object, using another class called Planet
* to set parameters like the location, size and color in it's constructor.
* x = the x location on the map (center of the planet!)
* y = the y location on the map (also center)
* diam = the diameter of the planet
*/
#SuppressWarnings("serial")
class myPlanetComponent extends JComponent{
private int x,y,diam;
public myPlanetComponent(int x, int y, int diam){
this.x = x;
this.y =y;
this.diam = diam;
}
//Paint a circle on with the centre on (x,y)
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setColor(Color.BLUE);
Ellipse2D.Double circle =
new Ellipse2D.Double((x-diam/2), (y-diam/2), diam, diam );
g2.fill(circle);
g2.setColor(Color.DARK_GRAY);
g2.draw(circle); //I want a border around my planet
}
public int getX(){
return this.x;
}
public int getY(){
return this.y;
}
}
/**
* This is also a quick way version of how I create the map display
* I intend to use in my game. It's a collection of planets with lines
* between them, on a black background.
*/
#SuppressWarnings("serial")
class myMap extends JPanel{
private boolean modeOne;
private int sizeX, sizeY;
public myMap(boolean mode){
this.sizeX = 500;
this.sizeY = 500;
this.modeOne = mode;
//JPanel map = new JPanel();
this.setSize(this.sizeX, this.sizeY);
}
public int getSizeX(){
return this.sizeX;
}
public int getSizeY(){
return this.sizeY;
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
//Create the black background plane
//this.setBackground(Color.BLACK); //Tried it with this, but won't give any bakcground
int heightBG = this.getSizeX();
int widthBG = this.getSizeY();
Rectangle2D.Double SpaceBackGround = new Rectangle2D.Double(0,0,
heightBG, widthBG);
g2.setColor(Color.BLACK);
g2.fill(SpaceBackGround);
//Normally, I import this list from somewhere else, but the result
//is very similar.
ArrayList<myPlanetComponent> planetsList = new ArrayList<myPlanetComponent>(5);
//Need to be able to generate at least 2 different maps to demonstrate
//the effects of using the New game button. Normally this list is randomly
//generated somewhere else, but idea stays the same.
if(modeOne){
planetsList.add(new myPlanetComponent(20,20,20));
planetsList.add(new myPlanetComponent(70,30,20));
planetsList.add(new myPlanetComponent(130,210,20));
planetsList.add(new myPlanetComponent(88,400,20));
planetsList.add(new myPlanetComponent(321,123,20));
}else{
planetsList.add(new myPlanetComponent(40,40,20));
planetsList.add(new myPlanetComponent(140,60,20));
planetsList.add(new myPlanetComponent(260,420,20));
planetsList.add(new myPlanetComponent(176,200,20));
planetsList.add(new myPlanetComponent(160,246,20));
}
//for all planets
for(int i=0; i<planetsList.size()-1; i++){
//planet 1 coordinates
myPlanetComponent p1 = planetsList.get(i);
if(i == 0){
p1.paintComponent(g2); //Only draw all planets once
}
//start coordinates of the line
int x1 = p1.getX();
int y1 = p1.getY();
//Be smart, and don't do things double!
for(int j=i+1; j<planetsList.size(); j++){
myPlanetComponent p2 = planetsList.get(j);;
if( i == 0){
p2.paintComponent(g2); //Only Draw all planets once
}
//planet 2 coordinates, endpoint of the line
int x2 = p2.getX();
int y2 = p2.getY();
Line2D.Double tradeRoute =
new Line2D.Double(x1, y1, x2, y2);
g2.setColor(Color.GREEN);
g2.draw(tradeRoute);
}
}
}
}
return new myMap(mode);
}
}
Your problem may be due to your mixing AWT and Swing (heavy weight and light weight) components in the same program, and this is something that shouldn't be done, unless you have definite need of this and know what you're doing. I doubt that you need a ChangeListener as JScrollPanes should be able to handle this sort of thing out of the box. Have you tried having your class extend JPanel or JComponent instead of Canvas?
Also, when posting code, consider creating and posting an SSCCE, a small compilable runnable program that we can run, test, modify, and hopefully correct. If you create and post this type of code, you'll likely get a decent and complete solution quickly.
edit: I created a test program to see what effect Canvas has on JScrollPanes, and it's as I thought -- the Canvas covers over the scroll bars, and everything else. To see for yourself compile and run this code and then resize the JFrame by clicking and dragging. The Canvas is blue and is in a JScrollPane on the left while the JPanel is red and is in a JScrollPane on the right.
import java.awt.*;
import javax.swing.*;
#SuppressWarnings("serial")
public class CanvasInScrollPane extends JPanel {
private static final Dimension CANVAS_SIZE = new Dimension(300, 300);
private static final Dimension APP_SIZE = new Dimension(500, 250);
Canvas canvas = new Canvas();
JPanel panel = new JPanel();
public CanvasInScrollPane() {
canvas.setPreferredSize(CANVAS_SIZE);
canvas.setBackground(Color.blue);
panel.setPreferredSize(CANVAS_SIZE);
panel.setBackground(Color.red);
setPreferredSize(APP_SIZE);
setLayout(new GridLayout(1, 0, 5, 0));
add(new JScrollPane(canvas));
add(new JScrollPane(panel));
}
private static void createAndShowUI() {
JFrame frame = new JFrame("CanvasInScrollPane");
frame.getContentPane().add(new CanvasInScrollPane());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}