views:

126

answers:

4

I am trying to create a GUI that will coexist with several images.

There is one background image that will take up the most room. On top of the background image several other images will be displayed in varying locations. These locations will be updated every 5 seconds or so, so speed is not a huge factor.

So all I need is the ability to display or remove images from display at the coordinates of my choosing upon each update.

I am using netbeans to put it all together.

Here is the most reasonable code I have come across to get the job done so far, but I don't know how to put it into practice, or whether it can accomplish what I need.

A short simple explanation of what a JPanel is would also be helpful.

public class ImagePanel extends JPanel {

  private Image img;

  public void setImage(String img) {
    setImage(new ImageIcon(img).getImage());
  }

  public void setImage(Image img) {
    int width = this.getWidth();
    int height = (int) (((double) img.getHeight(null) / img.getWidth(null)) * width);
    this.img = img.getScaledInstance(width, height, Image.SCALE_SMOOTH);
  }

  @Override
  public void paintComponent(Graphics g) {
    g.drawImage(img, 0, 0, this);
  }
}

And here is the code from the main class that I have so far. I can easily add code to determine whether or not given images should be displayed, and where they need to be, but I don't know where to put what.

public class ClientWindow extends javax.swing.JFrame {

/** Creates new form ClientWindow */
public ClientWindow() {
    initComponents();
    imagePanel1.setImage("C:\\Documents and Settings\\Robert\\Desktop\\ClientServer\\Poker Table Art\\TableAndChairs.png");
}

/** This method is called from within the constructor to
 * initialize the form.
 * WARNING: Do NOT modify this code. The content of this method is
 * always regenerated by the Form Editor.
 */
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {

    jScrollPane1 = new javax.swing.JScrollPane();
    jTextField1 = new javax.swing.JTextField();
    jScrollPane2 = new javax.swing.JScrollPane();
    jTextArea1 = new javax.swing.JTextArea();
    jCheckBox2 = new javax.swing.JCheckBox();
    imagePanel1 = new Pokertable.ImagePanel();
    imagePanel3 = new Pokertable.ImagePanel();
    jPanel1 = new javax.swing.JPanel();

    setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
    setTitle("Not Logged In");
    getContentPane().setLayout(null);

    jTextField1.addKeyListener(new java.awt.event.KeyAdapter() {
        public void keyTyped(java.awt.event.KeyEvent evt) {
            jTextField1KeyTyped(evt);
        }
    });
    jScrollPane1.setViewportView(jTextField1);

    getContentPane().add(jScrollPane1);
    jScrollPane1.setBounds(0, 540, 170, 22);

    jTextArea1.setColumns(20);
    jTextArea1.setRows(5);
    jScrollPane2.setViewportView(jTextArea1);

    getContentPane().add(jScrollPane2);
    jScrollPane2.setBounds(0, 440, 166, 96);

    jCheckBox2.setText("Sit Out Next Hand");
    jCheckBox2.addActionListener(new java.awt.event.ActionListener() {
        public void actionPerformed(java.awt.event.ActionEvent evt) {
            jCheckBox2ActionPerformed(evt);
        }
    });
    getContentPane().add(jCheckBox2);
    jCheckBox2.setBounds(0, 410, 113, 23);

    imagePanel1.add(imagePanel3);
    imagePanel1.add(jPanel1);

    getContentPane().add(imagePanel1);
    imagePanel1.setBounds(0, 0, 520, 370);

    pack();
}// </editor-fold>

private void jCheckBox2ActionPerformed(java.awt.event.ActionEvent evt) {                                           
    // TODO add your handling code here:
}                                          

private void jTextField1KeyTyped(java.awt.event.KeyEvent evt) {                                     
    // TODO add your handling code here:
}                                    


/**
* @param args the command line arguments
*/
public static void main(String args[]) {
    java.awt.EventQueue.invokeLater(new Runnable() {
        public void run() {
            new ClientWindow().setVisible(true);

        }
    });
}

// Variables declaration - do not modify
private Pokertable.ImagePanel imagePanel1;
private Pokertable.ImagePanel imagePanel3;
private javax.swing.JCheckBox jCheckBox2;
private javax.swing.JPanel jPanel1;
private javax.swing.JScrollPane jScrollPane1;
private javax.swing.JScrollPane jScrollPane2;
private javax.swing.JTextArea jTextArea1;
private javax.swing.JTextField jTextField1;
// End of variables declaration

}

For simplicity, suppose that I want to have the TableAndChairs.png as a stable background image, and the Button.png image (the D with a circle around it) to move to a new location every so often.

alt text alt text

A: 

If each of your panels were one of these ImagePanels, and you simply had it take another pair of ints as argument, your final paint method would simply paint your n different image panels onto the main graphics object:

public class Example extends JApplet{
     ImagePanel[] images;

//whatever code you want for the rest of your applet

     public void paint(){
         for(ImagePanel i : images)
              images.paintComponent();
         }
     }
 }

and you would fix ImagePanel to be:

@Override
public void paintComponent(Graphics g) {
    g.drawImage(img, x, y, this);
}
piggles
this is an application, not an applet. will that affect the fact that you are overriding the paint() method?
Allen
I don't believe so. The underlying idea is the same though. I only have JApplet code, but if you post your main class, I can give you some more help
piggles
for the images themselves, there isn't really any logic to them, so events don't need to be handled. But i'm not sure WHERE code goes to update everything, and how to control the properties of the components being updated.
Allen
Yeah. You should read some tutorials or play about (I learned by playing about). but your main problem is that you don't seem to have overridden the paint method. The signature is public void paint(Graphics g) -- either that or you need to be calling repaint. I'm not entirely sure.Also, it might be easier to be using a JApplet and then put that into a JFrame to make it an application
piggles
A: 

Use your ImagePanel to display the background image.

Then set the panel to use a null layout manager. Now you can add an ImageIcon to a JLabel and add the JLabel to the panel. Make sure you use:

label.setSize( label.getPreferredSize() );

otherwise the label won't appear. By default the label will appear at location (0, 0) although you can change this using the setLocation(...) method.

To do the animation you can use a Swing Timer and set it to fire every 5 seconds. When the Timer fires you move the image by changing its location.

If you don't know how to use a Timer, then read the section from the Swing tutorial on "How to Use Timers".

camickr
They are being updated sporadically, not necessarily every x seconds. This is a game being written, so every time someone takes an action, the display needs to be updated.
Allen
It doesn't change my answer of course some "event" has to happen for the image to move. That event could be regularly scheduled (by using a Timer), or is could be a user controlled event (by clicking a button for example). The point is whenever the event occurs you have to do "something". Without knowing the details of what you are doing, I'm just suggestion that "something" should be setting the location of the icon so it looks like its moving around the board from player to player.
camickr
+1  A: 

Hi Allen,

A better approach doing this would be by extending JComponent and overriding the paintComponent method to draw your custom Table and Chairs component.

You could use the foundations of Circle Trigonometry to calculate each circle dynamically. I have done a quick example to show you how this could be simple done using Java2D. There is a lot of performance tweaks that could be done such as caching, clipping, etc.

The reason why did this code is for you to understand how simple it is to draw them yourself. The reasons why to do this, you have more control over what is being drawn and the location its being drawn.

Example

public class JTableChairs extends JComponent {
  private final int TABLE_DIAMETER = 300;
  private final int CHAIR_DIAMETER = 50;
  private final int CHAIR_TABLE_BOUND = 50;
  private final int CANVAS_WIDTH = 500;
  private final int NUMBER_OF_CHAIRS = 10;

  private final int TABLE_X = (CANVAS_WIDTH - TABLE_DIAMETER) / 2;
  private final int TABLE_Y = TABLE_X;
  private final double TABLE_CENTER_X = TABLE_DIAMETER / 2.0 + TABLE_X;
  private final double TABLE_CENTER_Y = TABLE_DIAMETER / 2.0 + TABLE_Y;
  private final double TABLE_RADIUS = TABLE_DIAMETER / 2.0 + CHAIR_TABLE_BOUND;

  public JTableChairs() {
    super();
  }

  @Override
  public void paintComponent(Graphics g) {
    g.setColor(Color.WHITE);
    g.fillRect(0, 0, getSize().width, getSize().height);

    // Draw Table.
    g.setColor(Color.BLACK);
    g.fillOval(TABLE_X, TABLE_Y, TABLE_DIAMETER, TABLE_DIAMETER);

    // Draw each chair using normal circle trig functions.
    for (int i = 1; i <= NUMBER_OF_CHAIRS; i++) {
      Rectangle r = getSeatCoordinates(i);
      g.drawOval(r.x, r.y, r.width, r.height);
      g.drawString(String.valueOf(i), r.x, r.y);
    }
  }

  public Rectangle getSeatCoordinates(int seat) {
    // Clamp from 0 to number of chairs to make sure it doesn't
    // go over/under bounds.
    seat = seat < 1 ? 1 :
           (seat > NUMBER_OF_CHAIRS ? NUMBER_OF_CHAIRS : seat) - 1;

    // Calculate the coordinates using trig functions.
    double arcAngle = Math.toRadians(360 / NUMBER_OF_CHAIRS * seat);
    int x = (int)(TABLE_CENTER_X + TABLE_RADIUS * Math.cos(arcAngle) -
            CHAIR_DIAMETER / 2.0);
    int y = (int)(TABLE_CENTER_Y + TABLE_RADIUS * Math.sin(arcAngle) -
            CHAIR_DIAMETER / 2.0);
    return new Rectangle(new Point(x, y),
                         new Dimension(CHAIR_DIAMETER, CHAIR_DIAMETER));
  }
}

Say, you need to do some listener so when you press any circle, you want to highlight it. Using collision detection in 2D, you can do that. Note, the ugly way doing it is the following:

  public boolean setSeat(Point coord) {
    // Quick collision detection. This is bad performance since its
    // checking each point.
    for (int i = 1; i <= NUMBER_OF_CHAIRS; i++) {
      Rectangle r = getSeatCoordinates(i);
      if (r.contains(coord)) {
        Graphics g = getGraphics();
        g.setColor(Color.RED);
        g.drawRect(r.x, r.y, r.width, r.height);
        g.dispose();
        return true;
      }
    }
    return false;
  }

And in the constructor, you can add a mouse listener:

addMouseListener(new MouseAdapter() {
  @Override
  public void mouseReleased(MouseEvent e) {
    setSeat(e.getPoint());
  }
});

I hope you learned a thing or two :x

Mohamed Mansour
+1  A: 

just an idea: use an additional class for holding the images and corresponding coordinates, and put them into a list.

public class MyImage {    // or SubImage?
    private Image image;
    private int x;
    private int y;

    public MyImage(Image image, int x, int y) {
        this.image = image;
        this.x = x;
        this.y = y;
    }

    public void paint(Graphics g, ImageObserver obs) {
        g.drawImage(image, x, y, obs);
    }

    // getters and setters
} 


public class ImagePanel extends JPanel {

    private Image img;
    private final List<MyImage> images = new ArrayList<MyImage>();

    // setImage methods

    public void addMyImage(MyImage image) {
        images.add(image);
    }
    // or/and
    public void addMyImage(Image image, int x, int y) {
        images.add(new MyImage(image, x, y));
    }

    @Override
    public void paintComponent(Graphics g) {
        g.drawImage(img, 0, 0, this);    // check if img is null?
        for (MyImage image : images) {
            image.paint(g, this);
    }
}
Carlos Heuberger
Why the downvote? not much helpful downvoting without comment... or didn't you like the comment I did?
Carlos Heuberger