Weird output for Graphics code - java

I am attempting to make a Connect Four game to improve my ability with Java Graphics and as a school project. The background for the game will be a blue JPanel and the game board will be a separate JPanel that will be placed on top of the background. See my classes below:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JPanel;
public class gameBoard extends JPanel {
private Board bored;
public gameBoard(){
setLayout(new BorderLayout());
bored = new Board();//does not appear in Center Region of gameBoard
add(bored, BorderLayout.CENTER);
}
public void paint(Graphics g){//This line is the one that is acting weird.
//blue rectangle board is here, but when repaint called
//JFrame turns blue and does not add new JPanel called above
g.setColor(Color.BLUE);
g.fillRect(0, 0, 1456, 916);
}
}
AND
import java.awt.BasicStroke;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Board extends JPanel {
/*
* 2d array will represent board and take 1's(red) and 2's(black) the nums
* represent pieces, with each redraw of the board, a check will be done to
* compare a sum against blackWin and redWin. Sum will be created by
* summing a continuous line of 1's or 2's going left -> right
*/
public int[][] boardEvalMatrix = new int[6][7];
private final int blackWin = 8, redWin = 4;
public Board() {//1200 x 764
BoardMethods a = new BoardMethods();
a.printBoard(getBoard());
JPanel panelLORDY = new JPanel(new FlowLayout());
repaint();
}
public int[][] getBoard(){
return boardEvalMatrix;
}
public void paint(Graphics g){
g.setColor(Color.BLUE);//Drawing background with actual board as a Test
g.fillRect(0, 0, 1456, 916);//will not remain like this
Graphics2D newG = (Graphics2D) g;
newG.setStroke(new BasicStroke(15));
g.setColor(Color.YELLOW);
for(int a = 0; a < 6; a++)//rows for board --- rowHeight is 127
g.drawRect(128, 68+ (a*127), 1200, 127);
//g.setColor(Color.BLACK);
//newG.setStroke(new BasicStroke(8));
//for(int a = 0; a < 7; a++)//columns for board --- columnWidth is 171
// g.drawRect(208, 152, 70, 10);
//g.drawLine(50,0, 1456, 916); //width 1456 length 916 - school computer monitors
}
}
So what happened is this:
PROBLEM 1:
When I include the public void paint(Graphics g) line in the gameBoard class, the display that appears when I run the driver is just a gray JFrame, even though there is no call to repaint() and the paint() method is empty. However, when I deleted the line creating the paint method, the problem disappeared and the proper display appeared.
PROBLEM 2:
Even when I placed the code to draw a blue rectangle in the paint method in the gameBoard class and called repaint() the JFrame was blue, which is partly right. I know that Java executes commands from top to bottom so I made sure that the code adding the actual game board to the gameBoard JPanelcame after drawing a blue rectangle, but it didnt work.
QUESTION:
What did I do wrong and how do I fix it?

To change the background color of a panel you just use:
setBackground( Color.BLUE );
on the panel. Then is no need for custom painting.
When you override paint() and forget the super.paint(), then you really mess up the painting process. The paint() method is responsible for painting the child components on the panel. Since you don't invoke super.paint() the children never get painted.
If you do need custom painting for some reason then you should override the paintComponent() method and don't forget to invoke super.paintComponent(). Don't override paint().
Read the Swing tutorial on 'Custom Painting`, especially the section on A Closer Look at the Paint Mechanism for more information and examples.

Related

How to dynamically center a Graphic

Although in this example, the X-Y values are hard-coded, lets assume the user entered the values dynamically and clicked a button to view the results on the screen.
It wouldn't make sense to calculate the frame based on the largest size as the Frame would be too large for the monitor.
What is required to take the X-Y values entered (not matter how large or small) and have the image appear centered within the frame?
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.Area;
import java.awt.geom.Path2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class ZoomToXY extends JPanel
{
int x = 0;
public void paint(Graphics g)
{
//Can't see this.
int[] xs2 = {5308, 5306, 4143, 4143, 4120, 4119, 4118, 4117, 4116, 4114, 4112};
int[] ys2 = {4474, 5329, 5306, 5171, 5171, 5173, 5175, 5177, 5179, 5181, 5182};
BasicStroke traceStroke = new BasicStroke (1); //Line thickness
Graphics2D gc = (Graphics2D) g.create();
gc.setColor(Color.RED);
gc.drawPolyline(xs2, ys2, 11);
gc.setStroke(traceStroke);
x++;
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.add(new ZoomToXY());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setBounds(20,20, 250,250);
frame.setVisible(true);
}
}
The reason we can't see the polygon or whatever you're making, is because it's outside the frame's bounds. Let's take a look.
frame.setBounds(20,20, 250,250);
This line indicates we will only see what's inside these bounds, though everything outside will also be drawn but not shown. Try drawing a rectangle inside the bounds and see.
g.fillRect(20, 20, 100, 100);
You will see a rectangle. But how can I solve this issue? Since having a frame being 5000px by 5000px isn't going to work on most monitors, either you work with smaller resolutions and therefore smaller coordinates, or you implement a camera. Having a camera you can have as big world as you want and being able to move around in it. But if your frame can only show 100 pixels and your polygon is 1000px, we will only see 10% of it, this problem can easily be solved with zooming. Here is a topic how to implement a gamecamera. With the gameCamera you can simply calculate the center of your image, then translate it, quite simple. If you need assistance just ask.
A frame that is 250x250 is quite small, consider it being a little bigger. Also why set the coordinates as (20, 20)? If you want to center the JFrame to the current monitor just call:
frame.setLocationRelativeTo(null);

Bubble Shooter, Drawing Bubbles

I try to make a bubble shooter game and I have problem with drawing bubbles on MyPanel which extend JPanel. Class bubble(extend JButton) has method paintComponent:
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
RenderingHints qualityHints = new RenderingHints(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
qualityHints.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY);
g2d.setRenderingHints(qualityHints);
g2d.setColor(c);
g2d.fillOval(this.x,this.y,this.r, this.r);
}
How I should make constructor of MyPanel and method paint(); in MyPanel class, if I want to display Bubbles in 20 columns, 10 rows?
It looks like you are struggling with these bubbles for some time now: Bubble shooter game on Graphics2D, only one bubble display [closed] and Painting balloons on Graphics2D appear to be about similar problems. Please include your previous code and/or links to your related questions when you ask a new question.
A custom panel for the bubbles could look like this:
import java.awt.Color;
import java.awt.Graphics;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JPanel;
public class BubblePanel extends JPanel {
private final List<Bubble> bubbles;
public BubblePanel() {
bubbles = new ArrayList<>();
for (int rowIndex = 0; rowIndex < 10; rowIndex++)
for (int columnIndex = 0; columnIndex < 20; columnIndex++)
bubbles.add(new Bubble(100 + columnIndex * 60, 100 + rowIndex * 60,
28, Color.YELLOW));
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (final Bubble bubble : bubbles)
bubble.paintComponent(g);
}
}
Note: as camickr already wrote in an answer to one of your previous questions, with Swing you override the paintComponent method to implement your own custom painting. You can read more about this topic in the official Java lesson about Performing Custom Painting. The paintComponent method is introduced in step 2.
You can try using GridBagLayout(). You can easily manage columns and rows using GridBagConstraints. To know more about GridBagLayout() checkout the documentation here.

Drawing in java with JPanel

I'm making a program where you have a server and a client, and the idea is that you draw on the client jpanel, and the coordinates will then be sent to the server, which will sort of mimic the drawing. I've done that, but the problem is now, that my drawing mechanism is pretty bad. Right now I'm just using an oval that gets drawn over and over again on the coordinate of the mouse, which sometimes leaves spaces between the ovals if you move the mouse too fast.
To better illustrate, here's an SS: http://gyazo.com/6ed1017e9efd6beaa4b5d56052fda260
As you can see, it's only consistent when you move the mouse relatively slow, but as soon as you move it a bit fast, it leaves spaces.
How do I prevent this from happening?
Right now the client just sends x and y coordinates, so here's the server side code:
package com.company;
import javax.swing.*;
import java.awt.*;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.net.ServerSocket;
import java.net.Socket;
public class Server extends JPanel{
static MouseData mouseReceive;
static Draw draw;
static int x;
static int y;
public static void main(String[] args) throws IOException, ClassNotFoundException, InterruptedException {
Server server = new Server();
JFrame frame = new JFrame("Server");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(server);
frame.setSize(1024, 600);
frame.setVisible(true);
draw = new Draw(x,y);
ServerSocket serverSock = new ServerSocket(1234);
Socket s = serverSock.accept();
ObjectInputStream in = new ObjectInputStream(s.getInputStream());
while(true) {
mouseReceive = (MouseData) in.readObject();
draw = new Draw(mouseReceive.mouseX,mouseReceive.mouseY);
}
}
public void paint(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
draw.display(g);
repaint();
}
}
And here's my draw class:
package com.company;
import java.awt.*;
/**
* Created by John on 21/04/2015.
*/
public class Draw {
int xLoc;
int yLoc;
Draw(int x, int y){
xLoc = x;
yLoc = y;
}
public void display(Graphics g){
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.fillOval(xLoc,yLoc,7,7);
}
}
I tried finding someone else having the same problem on this site through the search function, but I had no luck in doing so :( If I missed it however, please direct me to that topic!
If anyone could help me out, I'd appreciate it a whole lot! Have a nice day :)
Presuming the Client is using a MouseListener (or MouseMotionListener): the MouseListener can only fire as fast as a certain interval. For example when the mouse is constantly moved your listener will receive a MouseEvent for every interval rather than every pixel. As a result, moving the mouse fast may result in drawing items that are not adjacent to each other. AFAIK, you cannot increase the speed, but you can draw lines between two sequential points making them look continuous (eg by using a List of each event location and using g.drawLine on each two adjacent points in the List).
Other notes:
You should override paintComponent rather than the paint method.
I would recommend calling super.paintComponent in this method. This will clear the component (hence your code will then only draw the last point - see (3))
I would recommend keeping a List of locations to use for drawing, which you can iterate over and draw each circle (or draw a line between adjacent points)
Do NOT call repaint within your painting methods. The idea here is that when a new item is received from the Client, add it to the List in (3) and then call repaint.

Moving Java Swing rectangle leaves rectangles behind

When I finally figured out the repaint method, I came to a problem. I want to move a rectangle across the screen, rather than re-drawing it again. Redrawing is fine, but it leaves the older rectangle behind it! This is my code:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Tutorial3{
public static void main(String[] agrs){
createAndStartGui();
}
static void createAndStartGui(){
JFrame f = new JFrame("tutorial 3");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setPreferredSize(new Dimension(500, 300));
MyPanel panel = new MyPanel();
f.add(panel);
f.pack();
f.setVisible(true);
for (int i = 0; i < 10; i++){
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
Logger.getLogger(Tutorial3.class.getName()).log(Level.SEVERE, null, ex);
}
panel.user.move("right");
panel.repaint();
}
}
}
class MyRectangle{
int x;
int y;
public MyRectangle(int x, int y){
this.x = x;
this.y = y;
}
void move(String direction){
switch (direction){
case "up":
this.y -= 10;
break;
case "down":
this.y += 10;
break;
case "left":
this.x -= 10;
break;
case "right":
this.x += 10;
break;
default:
break;
}
}
}
class MyPanel extends JPanel{
MyRectangle user = new MyRectangle(10, 10);
public MyPanel(){
}
public void paintComponent(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g.drawRect(user.x, user.y, 10, 10);
}
}
How do I get the rectangle that is left behind disappear (I DO NOT WANT TO CLEAR THE FULL WINDOW)? Or even better yet, how do I get the rectangle to 'move' (if it is possible)?
My end result:
What I want in the end:
Note: simply drawing the rectangle in that point isn't what I want. I want to see it getting dragged across.
Your problem is that you are only painting the rectangle, rather than the whole panel, so the panel ends up full of rectangles as you call the method. You need to draw the background of the panel too. This will "erase" the previous rectangles so the panel only has whatever you paint in that particular call and not what you did previously. To accomplish this you need to call:
super.paintComponent(g);
at the beginning of your paintComponent method (before drawing anything else). This works because the only thing that paintComponent needs to do in an empty JPanel is painting the background.
So:
public void paintComponent(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g.drawRect(user.x, user.y, 10, 10);
}
EDIT:
To answer some of your comments:
I want to move a rectangle across the screen, rather than re-drawing it again.
There's no such thing as "moving" a rectangle. You can have things painted on the screen. If you want to see other things you have to paint those other things. There's no inherent "move the color of the pixels to the pixels...", that's not how it works. Do you want things? Draw them. Do you want them to move? Draw them repeatedly in different positions.
(I DO NOT WANT TO CLEAR THE FULL WINDOW)
But you do. You want to repaint the whole panel each time something has to change. If there are other things in the panel that you don't want "erased" then repaint them.
To be clear, it would be possible to only clear the "old" rectangle position and paint a new one, without affecting the rest of the panel. But that is unnecesarily tricky. When you override paintComponent calling super.paintComponent(g) in the first line is the standard procedure. Not putting it has to be a very conscious decision and you better are sure of what are you doing.
If your program is done in a way that part of your code misbehaves when you repaint the background of your panel, I can tell you with confidence that is those parts that aren't well designed and not that calling super.paintComponent(g) is a bad idea.
paintComponent has the responsibility of painting the whole component. The background is part of the component. It's natural, and good design within Swing, to do it when you override it.

java swing: Incorrect dimensions on rotated JComponent after resizing

Is there any way to correct the dimensions for a rotated component?
In my swing application I’d like to be able to rotate a single panel, and have it respond correctly to resize events. The rotation is straight-forward, but on resizing, the height and width dimensions are reversed. It could almost would work if a call to setSize was called from an overridden paint() call to swap the dimensions, but that doesn’t work, because setSize causes another call to paint to occur, and recursion ensues... I’ve also tried adding a ComponentAdapter to handle the swapping on resize events, but got the same result.
So, here’s a simplified example of what I’m working with. The components here are buttons, but the logic applies to a JComponent like a JPanel too. Button c is rotated with a JXTransformer, but this doesn’t resize (it’s commented out in the code, but you can add the JXTransformer class to the classpath if you wish). If you compile the sample, try resizing the window and see how the rotated button behaves. Screenshot:
(It said I can't post screenshots, but these links appear to be live..)
http://i.stack.imgur.com/S3qmb.png
If I add in a scale transformation, the resizing is correct, but the component is distorted beyond usability. Screenshot:
http://i.stack.imgur.com/K4l9e.png
I’ve seen lots of questions on here that discuss the rotating part, but nothing about the resizing issue. For instance, A rotated square panel in Java GUI
Thanks!
Code:
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class RotatingTest {
public static void main(String[] args) {
JFrame frame = new JFrame();
JPanel panel = new JPanel(new BorderLayout());
// The button rotates, but the height/width dimensions are incorrect
RotatedButton a = new RotatedButton("ROTATED CONTENTS!");
JButton b = new JButton("Normal Contents");
JButton c = new JButton("Transformer Contents");
// JXTransformer t = new JXTransformer(c);
// t.rotate(Math.toRadians(90));
panel.add(a, BorderLayout.CENTER);
panel.add(b, BorderLayout.WEST);
// panel.add(t, BorderLayout.EAST);
panel.setBorder(BorderFactory.createEmptyBorder(30, 30, 30, 30));
frame.add(panel);
frame.pack();
frame.setVisible(true);
}
}
class RotatedButton extends JButton{
private static final long serialVersionUID = -3355833063753337573L;
RotatedButton(String string){
super(string);
}
#Override
protected void paintComponent(Graphics g) {
int width = getWidth();
int height = getHeight();
Graphics2D graphics = (Graphics2D) g;
AffineTransform txOrig = graphics.getTransform();
AffineTransform transformer = new AffineTransform(txOrig);
transformer.translate(width/2, height/2);
transformer.rotate(Math.toRadians(90));
transformer.translate(-height/2, -width/2);
// this scaling fits the button to the window, but distorts the contents
// double coef1 = (double)width / (double)height;
// double coef2 = (double)height / (double)width;
// transformer.scale(coef2, coef1);
// this line sets the rotation, comment out to disable
graphics.setTransform(transformer);
super.paintComponent(graphics);
graphics.setTransform(txOrig);
graphics.dispose();
System.out.println("Parent size: "+getRootPane().getParent().getSize());
System.out.println("this size: "+getSize());
}
}
So, the problem here is that JComponents do not care what is actually on them when setting their size. When you modify something in paintComponent() it only affects superficial portions of the component, and not anything that will actually be returned with from the component. What you are going to want to do, is remodel your paintComponent() method, or change how you are resizing the component.
Think of it like when you place an image on a button that is too large. The button will simply display what it can, and the rest does not matter as far as the button is concerned. Rotating the graphics on a JComponent is the exact same way. You can create the graphics ahead of time, and set your size to that (this is the complicated way), or if you are only rotating 90 degrees, simply change what you put int for setSize(). I have always found the second to be very simple, as you can constantly change the size of it with no extra code, you just have to remember to switch all your dimensions.
If, however, you wish to put it at an angle that is not a multiple of 90 degrees, you will have to make a larger square for the component to sit in. (using the Pythagorean theory) This may seem silly, but think about how all coordinates are made, from the top left corner. that corner defines that object's location, but if it is not at an absolute (the highest leftmost point) then anything working with that component would have to manually look at every part of the component instead of just calculating it.
Hope this helps, late as it may be.

Categories

Resources