I am trying to draw circles to JFrame with different positions, in this case in a grid-like pattern.
I don't seem to have trouble drawing a single circle with an x,y position, however, when I try to draw multiple circles with different positions the other circles' positions come out obscured.
In the code below, I have two arrays: XPlacements and YPlacements that contain different X and Y positions to form a grid. For each X position there should be circles created at that X position with varying Y positions (vice versa).
When I only make the below code draw one circle, where XPlacements = {10} YPlacements = {100}, it draws a circle that appears to be at some (10,100) from the top left. Similarly when I add multiple X positions but a single Y position nothing seems to be obscured. However, when I add multiple Y positions, every descending row of circles seems to be more and more pushed to the right.
When I run it, it obscures like this: http://imgur.com/HHjhvPD
I can't seem to make it less obscured by changing the values of the JFrame. All the JFrame circle tutorials I can find only deal with 1 circle, and I don't have any issues when I use 1 circle (or only 1 row of circles).
Does anyone know why the code below isn't producing a grid like pattern?
Thanks.
public class Circle extends Canvas {
int XPos;
int YPos;
public void SetPosition(int x, int y) {
this.XPos = x;
this.YPos = y;
repaint();
}
public void paint(Graphics g) {
System.out.println("Filling oval with position ("+XPos+","+YPos+")");
g.fillOval(XPos, YPos, 15, 15);
}
}
public class MainFile extends JFrame {
static int[] XPlacements = {10,20,30};
static int[] YPlacements = {100,200,300};
static Circle[] Circles = new Circle[1000];
static int Circle_Count = 0;
public static void main(String[] args) {
JFrame frame = new JFrame("Grid of circles");
frame.setLayout(new GridLayout(1,1));
frame.setSize(800, 800);
for(int x=0;x<XPlacements.length;x++) {
for(int y=0;y<YPlacements.length;y++) {
System.out.println("Creating new circle "+Circle_Count+" with position "+XPlacements[x]+","+YPlacements[y]);
Circles[Circle_Count] = new Circle();
Circles[Circle_Count].SetPosition(XPlacements[x],YPlacements[y]);
frame.add(Circles[Circle_Count]);
Circle_Count++;
}
}
frame.setVisible(true);
}
}
First of all this is a Swing application so custom painting should be done by overriding paintComponent() of a JPanel. A Canvas is an AWT component that should not be used in a Swing application.
If you want to draw on a component then the custom painting is always done relative to offset (0, 0). So in your case the code would be:
//g.fillOval(XPos, YPos, 15, 15);
g.fillOval(0, 0, 15, 15);
Then you need to override the getPreferredSize() method of your class to set the size of the component:
#Override
public Dimension getPreferredSize()
{
return new Dimension(15, 15);
}
Read the section from the Swing tutorial on Custom Painting for more informations and working examples.
Now the layout manager has information about the component and can position each component on the panel:
So the code to add the Circles to the frame would be something like:
frame.setLayout( new GridLayout(3, 3, 50, 50) );
for (int i = 0; i < 9; i++)
{
frame.add( new Circle() );
}
This will create a 3x3 grid with a gap of 50 pixels between each component.
Related
I am trying to teach myself more about graphics in Java. To do this I'm trying to build a chess game. I've hit my first roadblock at making the board. My thought here is that I would have a extension of JComponent called "Square" that would be my container for both the color of the board square and the piece on that square (if any). To start with I haven't attempted to include any representation of the piece yet, just the square colors. Later on I hope to have an abstract "Pieces" class that is extended by multiple subclasses representing all the different types of pieces, and add those to each Square as applicable.
When I execute the following, I only get one black square in the upper left hand corner.
ChessBoardTest.java
public class ChessBoardTest {
public static void main(String[] args) {
ChessBoard Board = new ChessBoard();
Board.Display();
}
}
ChessBoard.java
public class ChessBoard extends JFrame {
public static final int FRAME_WIDTH = 500;
public static final int FRAME_HEIGHT = 500;
// Declare instance variables
private Square[][] square = new Square[rows][cols];
private final static int rows = 8;
private final static int cols = 8;
public ChessBoard() {
}
public void Display() {
JPanel Board_Layout = new JPanel();
Board_Layout.setLayout(new GridLayout(8,8));
for(int i=0;i<8;i++)
{
for(int j=0;j<8;j++)
{
if((i+j) % 2 == 0) {
square[i][j] = new Square(1);
Board_Layout.add(square[i][j]);
} else {
square[i][j] = new Square(0);
Board_Layout.add(square[i][j]);
}
}
}
setTitle("Chess Mod");
setSize(FRAME_WIDTH, FRAME_HEIGHT);
setResizable(false);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(Board_Layout);
setVisible(true);
}
public void messageBox(String pMessage) {
JOptionPane.showMessageDialog(this, pMessage, "Message", JOptionPane.PLAIN_MESSAGE);
}
}
Square.java
public class Square extends JComponent {
private int color;
public Square(int c) {
this.color=c;
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
if (this.color == 1) {
g.setColor(new Color(0,0,0));
} else {
g.setColor(new Color(255,255,255));
}
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
}
}
I only get one black square in the upper left hand corner.
That's mainly because of the following call:
g.fillRect(this.getX(), this.getY(), this.getWidth(), this.getHeight());
getX() returns the horizontal pixel offset/location of the Component which is invoked upon, relative to the Container that contains that Component. getY() accordingly returns the vertical pixel offset/location of the Component which is invoked upon, relative to the Container that contains the Component.
getWidth() and getHeight() return the size of the Component.
So imagine that the Component at row with index 2 and column with index 3, will have its coordinates at about x == 3 * w / 8 and y == 2 * h / 8 where w and h is the size (width and height respectively) of the parent Container (ie the Board_Layout panel). Let's assume that Board_Layout has a size of 300x300 when you show the graphical user interface... This means that the Square at the location I mentioned will only paint the region which starts at x == 112 and y == 75 and expands at one 8th of the width (and height) of Board_Layout (because there are 8 rows and 8 columns in the grid). But the size of the Square itself is also at one 8th of the width (and height) of Board_Layout, ie about 37x37. So the painted region which starts and expands from the location 112,75 will not be shown at all (because it lies completely outside the Square's size).
Only the top left Square will have some paint on it because its bounds in the parent happen to intersect the drawn region.
To fix this, the location given at the Graphics object should be relative to each Square and not its parent Board_Layout. For example:
g.fillRect(0, 0, getWidth(), getHeight());
I've never written a program with anything more than a bare-bones GUI, so I've undertaken a personal project to write a chess application.
One of my goals with this project was to make the board rescale to fit the window, and I managed to do this without too much trouble. However, in the process I ran into the issue that my pieces (which were represented as Icons on JButtons) did not rescale with the rest of the board.
I decided to represent them with the Image class instead, and made a custom class called ScalingJButton which overrode paintComponent. This actually worked quite well... for the last piece to be drawn. The rest of the pieces are not drawn, and as a result the program is broken. Here is my ScalingJButton class:
public class ScalingJButton extends JButton{
private Image image;
ScalingJButton (){
this.image = null;
}
ScalingJButton (Image image){
this.image = image;
}
public void setImage(Image image){
this.image = image;
}
public Image getImage(){
return image;
}
#Override
public void paintComponent(Graphics g){
//super.paintComponent(g);
int x = getX();
int y = getY();
int width = getWidth();
int height = getHeight();
if (image != null) {
g.drawImage(image, x, y, width, height, this);
}
}}
Additionally, here is the code responsible for instantiating the ScalingJButtons (VisualBoard is a class extending JPanel and this is its constructor).
public VisualBoard (){
white = Color.WHITE;
black = Color.GRAY;
loadPieceImages();
setLayout(new GridLayout(8, 8));
for (int i = 0; i < 8; i++){
for (int j = 0; j < 8; j++){
squares[i][j] = new ScalingJButton();
if ((i + j) % 2 != 0) {
squares[i][j].setBackground(white);
}
else{
squares[i][j].setBackground(black);
}
add(squares[i][j]);
}
}
initializeStandardBoard();
}
Finally, since the layout may be relevant, here is the code that makes the board autoscale:
public class Chess {
public static void main (String[] args){
final VisualBoard board = new VisualBoard();
final JPanel container = new JPanel(new GridBagLayout());
container.add(board);
container.addComponentListener(new ComponentAdapter() {
#Override
public void componentResized(ComponentEvent e) {
drawResizedBoard(board, container);
}
});
JFrame frame = new JFrame("Chess");
frame.setSize(1000,1000);
frame.add(container);
frame.setLocationRelativeTo(null);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
private static void drawResizedBoard(JPanel innerPanel, JPanel container) {
int w = (int)Math.round(container.getWidth()*0.9);
int h = (int)Math.round(container.getHeight()*0.9);
int size = Math.min(w, h);
innerPanel.setPreferredSize(new Dimension(size, size));
container.revalidate();
}}
I have done extensive debugging, but most of it has lead nowhere. One thing to note is that the "image" variable in ScalingJButton holds the correct picture when drawImage is called, so that's not the issue. Another interesting point is that the square with the piece in it is the the last square to be drawn, and depending on what order I add squares to the Board, different pieces will be drawn (so there is no issue loading pieces).
Strangely, if I do away with super in paintComponent, rolling my mouse over the drawn pieces causes the other squares to fill with that piece when I roll my mouse over them.
I'm completely lost and don't know what to do, so any help would be greatly appreciated!
First, you need to keep the super.paintComponent(g); line. If you don’t, artifacts will appear at seemingly random times.
Second, getX() and getY() return to the component’s position in its parent. When you paint, you are given a coordinate system where 0,0 is the top left corner of the component. So you should ignore getX() and getY().
The simplest alternative is to use the upper-left corner of the button:
int x = 0;
int y = 0;
You may want to account for the button’s border:
if (image != null) {
Rectangle inner = SwingUtilities.calculateInnerArea(this, null);
g.drawImage(image, inner.x, inner.y, inner.width, inner.height, this);
}
And if you want the button to really look like a normal button, you can also account for its margin:
if (image != null) {
Rectangle inner = SwingUtilities.calculateInnerArea(this, null);
Insets margin = getMargin();
inner.x += margin.left;
inner.y += margin.top;
inner.width -= (margin.left + margin.right);
inner.height -= (margin.top + margin.bottom);
g.drawImage(image, inner.x, inner.y, inner.width, inner.height, this);
}
However, in the process I ran into the issue that my pieces (which were represented as Icons on JButtons) did not rescale
The Stretch Icon may be a simpler solution. You can use this Icon on any component that can display an Icon. The image will dynamically change size to fill the space available to the Icon.
There are two suspicious lines in your code. First one is
frame.add(container);
This line adds your Swing element to an AWT container. Instead your should replace it with
frame.setContentPane(container);
The second line is the method
public void paintComponent(Graphics g) {
Try replacing it with
public void paint(Graphics g) {
Hope this helps.
EDIT:
Also change this
g.drawImage(image, x, y, width, height, this);
to
public void paint(Graphics g) {
super.paint(g);
int width = getWidth();
int height = getHeight();
if (image != null) {
g.drawImage(image, 0, 0, width, height, this);
}
}
Your images were simply outside of the grid.
Is there a Java-only way to show larger pictures in a JScrollPane? I don't want to reinvent the wheel and I'm already struggling at showing 32768x400 images using the ImageIcon in a JLabel trick because there seem to be limits regarding the ImageIcon that are platform dependent. Ubuntu 16.10 won't show any ImageIcon of the size 32768x400, though it shows smaller ones. Win10 shows them all.... and there is not even any error output of any sorts, which is terrible because I just wasted time searching for the problem.
So is there any easy solution to this that does not require me to reinvent the wheel?
In particular, I want to display waveforms, ie. an array of floats, so there is actually no need to have an overall image at all.
I believe this shows how to do what you want. Note how the Graph component has a width of 65535. This could be further optimized by only drawing the visible part of the graph as you scroll, but it's fairly fast as it is.
import java.awt.*;
import javax.swing.*;
import java.util.function.Function;
class Graph extends JComponent {
private Function<Double, Double> fun;
public Graph(Function<Double, Double> fun) {
this.fun = fun;
setPreferredSize(new Dimension(65535, 300));
}
public void paintComponent(Graphics g) {
// clear background
g.setColor(Color.white);
Rectangle bounds = getBounds();
int w = bounds.width;
int h = bounds.height;
g.fillRect(bounds.x, bounds.y, w, h);
// draw the graph
int prevx = 0;
int prevy = fun.apply((double)prevx).intValue();
g.setColor(Color.black);
for (int i=1; i<w; i++) {
int y = fun.apply((double)i).intValue();
g.drawLine(prevx, prevy, i, y);
prevx = i;
prevy = y;
}
}
}
public class Wf {
public static void main(String[] args) {
JFrame f = new JFrame();
// we're going to draw A sine wave for the width of the
// whole Graph component
Graph graph = new Graph(x -> Math.sin(x/(2*Math.PI))*100+200);
JScrollPane jsp = new JScrollPane(graph);
f.setContentPane(jsp);
f.setSize(800, 600);
f.setVisible(true);
}
}
Hey Guys I have succesfully made a GUI in java that will scale polygons and circles using a slider. Everything works but I was wondering if there is a way to change the Origin point(Where it scales from). Right now it scales from the corner and I would like it to scale from the middle so I can start it in the middle and it scales out evenly. Also, If anyone could tell me an easy way to replace the Rectangle I have with an Image of some kind so you can scale the Picture up and down would be great! Thank you! Here is my code:
import javax.swing.*;
public class Fred
{
public static void main(String[] args)
{
TheWindow w = new TheWindow();
w.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); //X wont close the window with out this line
w.setSize(375,375);
w.setVisible(true);
}
}
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
public class TheWindow extends JFrame
{
private JSlider slider; //declare slider
private drawRect myPanel; //declare/ create panel
public TheWindow()
{
super("Slider Example"); //make title
myPanel = new drawRect();
myPanel.setBackground(Color.green); //change background color
slider = new JSlider(SwingConstants.VERTICAL, 0, 315, 10);// restrains the slider from scaling square to 0-300 pixels
slider.setMajorTickSpacing(20); //will set tick marks every 10 pixels
slider.setPaintTicks(true); //this actually paints the ticks on the screen
slider.addChangeListener
(
new ChangeListener()
{
public void stateChanged(ChangeEvent e)
{
myPanel.setD(slider.getValue()); //Wherever you set the slider, it will pass that value and that will paint on the screen
}
}
);
add(slider, BorderLayout.WEST); //similar to init method, adds slider and panel to GUI
add(myPanel, BorderLayout.CENTER);
}
}
import java.awt.*;
import javax.swing.*;
public class drawRect extends JPanel
{
private int d = 25; //this determines the beginning length of the rect.
public void paintComponent(Graphics g)//paints circle on the screen
{
super.paintComponent(g); //prepares graphic object for drawing
g.fillRect(15,15, d, d); //paints rectangle on screen
//x , y, width, height
}
public void setD(int newD)
{
d = (newD >= 0 ? newD : 10); //if number is less than zero it will use 10 for diameter(compressed if statement)
repaint();
}
public Dimension getPrefferedSize()
{
return new Dimension(200, 200);
}
public Dimension getMinimumSize()
{
return getPrefferedSize();
}
}
Changing the "origin point" so it becomes the center of the "zoom" is basically just the process of subtract half of d from the center point.
So, assuming the the center point is 28 ((25 / 2) + 15), you would simply then subtract d / 2 (25 / 2) from this point, 28 - (25 / 2) = 15 or near enough...
I modified the paintComponent method for testing, so the rectangle is always at the center of the panel, but you can supply arbitrary values in place of the originX and originY
#Override
public void paintComponent(Graphics g)//paints circle on the screen
{
super.paintComponent(g); //prepares graphic object for drawing
int originX = getWidth() / 2;
int originY = getHeight() / 2;
int x = originX - (d / 2);
int y = originY - (d / 2);
System.out.println(x + "x" + y);
g.fillRect(x, y, d, d); //paints rectangle on screen
//x , y, width, height
}
As for scaling an image, you should look at Graphics#drawImage(Image img, int x, int y, int width, int height, ImageObserver observer), beware though, this will scaling the image to the absolute size, it won't keep the image ratio.
A better solution might be to use a double value of between 0 and 1 and multiple the various elements by this value to get the absolute values you want
Hello all I have this problem that I can't seem to fix. I've been given some code and have to make a "tic tac toe" game. Fairly primitive. At the moment what it want's me to do is take user input (it just asks for what row / column you want to place the marker) and it is meant to draw an oval on the appropriate square of the board. My current code is as following the work has specified that I make a new class to deal with user input.
I am currently just mucking around with trying to get it to add new items to the JFrame but am having little success. I have a dummy call for input, it doesn't check to see what I've typed it just calls an oval that SHOULD sit in the first square in the top left corner. I can get the object to draw onto the JFrame (albeit it takes up the whole frame) but it is always BEHIND the actual board (ie: if I stretch the frame I can see the circle). I've tried adding JPanels and so forth so that they sit on top of the board but so far I am having little luck.
Here is the code for creating the Oval which I was given for the task. All I am doing is instantiating a new oval with position (0,0,10,10). When it draws however it takes up the WHOLE JFrame but it is also BEHIND the actual board...... any ideas?
package lab4;
import javax.swing.*;
import java.awt.*;
/** Oval Supplier Class
* Author: David D. Riley
* Date: April, 2004
*/
public class Oval extends JComponent {
/** post: getX() == x and getY() == y
* and getWidth() == w and getHeight() == h
* and getBackground() == Color.black
*/
public Oval(int x, int y, int w, int h) {
super();
setBounds(x, y, w, h);
setBackground(Color.black);
}
/** post: this method draws a filled Oval
* and the upper left corner of the bounding rectangle is (getX(), getY())
* and the oval's dimensions are getWidth() and getHeight()
* and the oval's color is getBackground()
*/
public void paint(Graphics g) {
g.setColor( getBackground() );
g.fillOval(0, 0, getWidth()-1, getHeight()-1);
paintChildren(g);
}
}
EDIT: THIS IS NOW THE CODE WE ARE LOOKING AT--
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package lab4;
/**
*
* #author Scott
*/
import java.awt.*;
import java.util.Scanner;
import javax.swing.*;
public class GameBoard {
private JFrame win;
private int count = 1;
//Create new GUI layout
GridLayout layout = new GridLayout(3, 3);
JPanel panel = new JPanel(layout);
//Create a new Array of Rectangles
Rectangle[][] rect = new Rectangle[3][3];
public GameBoard() {
//Create new JFrame + Set Up Default Behaviour
win = new JFrame("Tic Tac Toe");
win.setBounds(0, 0, 195, 215);
win.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
win.setResizable(true);
//Loop goes through each line of the array. It creates a new rectangle
//determines it's colour based on modulus division
//Add's the rectangle to the JPanel.
for (int i = 0; i < rect.length; i++) {
for (int j = 0; j < rect[i].length; j++) {
rect[i][j] = new Rectangle(0, 0, 1, 1);
if (count % 2 != 0) {
rect[i][j].setBackground(Color.black);
} else {
rect[i][j].setBackground(Color.red);
}
panel.add(rect[i][j]);
count++;
}
}
//Sets the game to be visible.
win.add(panel);
win.setVisible(true);
//userInput();
}
private void userInput() {
Scanner scan = new Scanner(System.in);
}
}
Let's start with your oval class...
This is a VERY bad idea...
public void paint(Graphics g) {
// No super.paint(g) call, hello to a world of pain...
// Paint some stuff
g.setColor( getBackground() );
g.fillOval(0, 0, getWidth()-1, getHeight()-1);
// Then draw the children over it...???
paintChildren(g);
}
This is a better approach.
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor( getBackground() ); // Might want to pick a better color...
g.fillOval(0, 0, getWidth()-1, getHeight()-1);
}
At a guess, I suggest that your window is using a layout manager that is overriding your setBounds call
Answer ended up being that I need to stick with the flow layout, manually size the rectangles and use a JLayeredPane instead. Was not aware this existed, talked to my lecturer who said that my thought process was right and that was the way he intended for me to do it......what a pain but all done thanks to those that helped.