For homework, I'm trying to create a "CustomButton" that has a frame and in that frame, I draw two triangles, and a square over it. It's supposed to give the user the effect of a button press once it is depressed. So for starters, I am trying to set up the beginning graphics, drawing two triangles, and a square. The problem I have is although I set my frame to 200, 200, and the triangles I have drawn I think to the correct ends of my frame size, when I run the program, I have to extend my window to make the whole artwork, my "CustomButton," viewable. Is that normal? Thanks.
Code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CustomButton
{
public static void main(String[] args)
{
EventQueue.invokeLater(new Runnable()
{
public void run()
{
CustomButtonFrame frame = new CustomButtonFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
});
}
}
class CustomButtonFrame extends JFrame
{
// constructor for CustomButtonFrame
public CustomButtonFrame()
{
setTitle("Custom Button");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
CustomButtonSetup buttonSetup = new CustomButtonSetup();
this.add(buttonSetup);
}
private static final int DEFAULT_WIDTH = 200;
private static final int DEFAULT_HEIGHT = 200;
}
class CustomButtonSetup extends JComponent
{
public void paintComponent(Graphics g)
{
Graphics2D g2 = (Graphics2D) g;
// first triangle coords
int x[] = new int[TRIANGLE_SIDES];
int y[] = new int[TRIANGLE_SIDES];
x[0] = 0; y[0] = 0;
x[1] = 200; y[1] = 0;
x[2] = 0; y[2] = 200;
Polygon firstTriangle = new Polygon(x, y, TRIANGLE_SIDES);
// second triangle coords
x[0] = 0; y[0] = 200;
x[1] = 200; y[1] = 200;
x[2] = 200; y[2] = 0;
Polygon secondTriangle = new Polygon(x, y, TRIANGLE_SIDES);
g2.drawPolygon(firstTriangle);
g2.setColor(Color.WHITE);
g2.fillPolygon(firstTriangle);
g2.drawPolygon(secondTriangle);
g2.setColor(Color.GRAY);
g2.fillPolygon(secondTriangle);
// draw rectangle 10 pixels off border
g2.drawRect(10, 10, 180, 180);
}
public static final int TRIANGLE_SIDES = 3;
}
Try adding
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
to your CustomButtonSetup class.
And then do
setTitle("Custom Button");
//setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
CustomButtonSetup buttonSetup = new CustomButtonSetup();
this.add(buttonSetup);
pack();
(From the api-docs on pack():)
Causes this Window to be sized to fit the preferred size and layouts of its subcomponents.
You should get something like:
The DEFAULT_WIDTH and DEFAULT_HEIGHT that you set is for the entire frame, including borders, window titles, icons, etc. It's not the size of the drawing canvas itself. Thus, it is expected that if you draw something in a 200x200 canvas, it would not necessarily fit in a 200x200 window containing that canvas.
Related
I got two threads printing in a JFrame:
Thread-1: prints green squares
Thread-2: prints blue circles
To make both figures appear in the JFrame I set the layout for each thread as:
setLayout(new OverlayLayout(this));
setOpaque(false);
Problem: the blue circles appear like blank squares in the JFrame. Only the green squares appear correctly.
Here is the main class:
public class Main {
public static void main(String[] args) {
FigurePlacer circle = new FigurePlacer("circle");
FigurePlacer square = new FigurePlacer("square");
JFrame window = new JFrame();
window.add(circle);
window.add(square);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setTitle("Task");
window.setSize(700, 700);
window.setLocationRelativeTo(null);
window.setVisible(true);
}
}
And here is the Threaded Class:
public class FigurePlacer extends JPanel implements Runnable{
String figure;
final int width = 700;
final int height = 700;
int x_pos = 0;
int y_pos = 0;
int x_width = 50;
int y_height = 50;
public FigurePlacer(String str){
figure = str;
randomCoord();
Thread th = new Thread (this);
th.start();
}
private void randomCoord(){ //this ramdomize x,y coord to place a new object
Random random = new Random();
x_pos = random.nextInt(width);
y_pos = random.nextInt(height);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println(figure);
switch (figure){
case "square":
g.setColor(Color.GREEN);
g.fillRect(x_pos, y_pos, x_width, y_height);
break;
case "circle":
g.setColor(Color.BLUE);
g.fillOval(x_pos, y_pos, x_width, y_height);
break;
}
}
#Override
public void run(){ //paints the objects
while (true){
randomCoord();
paintImmediately(x_pos, y_pos, x_width, y_height);
try{
Thread.sleep (50);
}
catch (InterruptedException ex){}
}
}
}
setLayout(new OverlayLayout(this));
setOpaque(false);
Setting non-opaque on the panel containing the two child panels doesn't do anything.
window.add(circle);
window.add(square);
You need to make the "square" panel non-opaque, so you can see the "circle" panel. (or make both panels non-opaque). The point is when two components are painted on top of one another the top panel needs to be non-opaque).
And your painting code is still incorrect. Have you tried resizing the frame. If you do you will see all the squares disappear.
I recently made a drawing of a car and tried changing the .fillPolygon to .fillRect but it didnt work to change the car to look like a truck. How would I change my code below to make the drawing of the car into a drawing of a truck?I did this on Eclipse.
Car.java
import java.awt.*;
public class Car {
//Coordinates if car is drawn at position 0,0
private int [] x = {0, 0, 20, 25, 70, 80, 105, 110};
private int[] y = {35, 15, 15, 0, 0, 15, 15, 35};
private int [] xCurrent = new int [x.length];
private int [] yCurrent = new int [y.length];
private int xOffset = 0, yOffset =0;
private Color carColor;
//-----------------------------------------------------------------
// Sets up the graphical car with the specified offsets.
//-----------------------------------------------------------------
public Car(int xOff, int yOff, Color color)
{
xOffset = xOff;
yOffset = yOff;
carColor = color;
for (int i=0; i < x.length; i++)
{
xCurrent[i] = x[i] + xOffset;
yCurrent[i] = y[i] + yOffset;
}
}
//
public int getXOffset() {return xOffset;}
public int getYOffset() {return yOffset;}
//-----------------------------------------------------------------
// Draws the car at a particular x and y offset.
//-----------------------------------------------------------------
public void draw(Graphics page)
{
page.setColor(carColor);
page.fillPolygon(xCurrent, yCurrent, x.length);
page.setColor(Color.black);
page.fillOval(13+xOffset, 28+yOffset, 14, 14); // rear wheel
page.fillOval(83+xOffset, 28+yOffset, 14, 14); // front wheel
page.drawLine(15+xOffset, 18+yOffset, 15+xOffset, 3+yOffset);
}
}
CarPanel.java
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class CarPanel extends JPanel {
private Car car1, car2, car3;
private final int DELAY =20;
private int x, y;
private final int SPEED =2;
public CarPanel ()
{
car1 =new Car(200, 150, Color.BLUE);
car2 = new Car(50, 50, Color.RED);
car3 = new Car(0, 220, Color.GREEN);
x =0;
y= 220;
setPreferredSize(new Dimension(450,300));
ActionListener taskPerformer = new ActionListener()
{
public void actionPerformed(ActionEvent evt)
{
x = car3.getXOffset() + SPEED;
y = car3.getYOffset();
if (x > getWidth()) x = 0;
car3 = new Car(x, y, Color.GREEN);
x = car2.getXOffset() + SPEED + 5;
if (x > getWidth()) x = 0;
y = car2.getYOffset();
car2 = new Car(x, y, Color.RED);
repaint();
}
};
new Timer(DELAY, taskPerformer).start();
}
//-----------------------------------------------------------------
// Draws the car.
//-----------------------------------------------------------------
public void paint(Graphics page)
{
super.paint(page);
car1.draw(page);
car2.draw(page);
car3.draw(page);
}
}
GUITester.java
import javax.swing.JFrame;
public class GUITester {
//-----------------------------------------------------------------
// Sets up a frame containing a tabbed pane. The panel on each
// tab demonstrates a different layout manager.
//-----------------------------------------------------------------
public static void main(String[] args)
{
JFrame frame = new JFrame("GUI Tester");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
// add any panel into here
//ArrayReviews panel = new ArrayReviews();
//ArrayBetter panel = new ArrayBetter();
CarPanel panel = new CarPanel();
//NumericKeypadPanel panel = new NumericKeypadPanel();
//StarPanel panel = new StarPanel();
//SnowPanel panel = new SnowPanel();
//Draw1StarPanel panel = new Draw1StarPanel();
//Rain panel = new Rain();
//Cards panel = new Cards();
//SelectionSortPanel panel = new SelectionSortPanel();
//PersonPanel panel = new PersonPanel();
//SinPanel panel = new SinPanel();
//KochSnowFlake panel = new KochSnowFlake();
//CalculatorPanelSimple panel = new CalculatorPanelSimple();
frame.getContentPane().add(panel);
frame.pack();
frame.setVisible(true);
}
}
Yuk! You are declaring x[] y[] arrays, and then constructing xOffset[] yOffset[] arrays to translate you car/truck to different positions. You can let the GPU do the work for you:
void draw(Graphics g) {
Graphics2D g2d = (Graphics2D) g; // Every Graphics is actually a Graphics2D.
translate(xOffset, yOffset); // Move the graphic origin
g2d.fillPolygon(x, y, x.length); // Draw with original coordinates
translate(-xOffset, yOffset); // Restore graphic origin, for other drawing
}
It doesn't turn your car into a truck, but your code doesn't show your attempt, so it is hard to say where you went wrong.
But, if this simplifies your code, perhaps that will help.
As #MadProgrammer points out, the Shape API might also be useful.
static Shape CAR_SHAPE;
static {
// Initialize CAR_SHAPE
}
void draw(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
translate(xOffset, yOffset);
g2d.draw(CAR_SHAPE);
translate(-xOffset, yOffset);
}
See Trail: 2D Graphics
I am wanting to make a game that has each level loaded from an image.
I want to draw up the whole level in Photoshop, and then set it as the background and allow the player to walk over it.
I want another invisible image to go over top which will be black in all places that I want to collide with.
The reason I don't want to use tiles, which are much easier with rectangle collision and such, is because there will be complex corners and not everything will be rectangle.
Is this a good idea, and is it possible to do easily?
Would this be a big CPU hog or is there a better way to do this?
Level image
Obstacles shown in red
..there will be complex corners and not everything will be rectangle.
This could be achieved by drawing and dealing with Shape and Area instances. E.G.
Yellow is a little animated 'player'.
The bounds of the image represent walls that contain the path of the player (it bounces off them).
Obstacles are painted green when not in collision, red otherwise.
import java.awt.*;
import java.awt.event.*;
import java.awt.geom.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
class ShapeCollision {
private BufferedImage img;
private Area[] obstacles = new Area[4];
private Area walls;
int x;
int y;
int xDelta = 3;
int yDelta = 2;
/** A method to determine if two instances of Area intersect */
public boolean doAreasCollide(Area area1, Area area2) {
boolean collide = false;
Area collide1 = new Area(area1);
collide1.subtract(area2);
if (!collide1.equals(area1)) {
collide = true;
}
Area collide2 = new Area(area2);
collide2.subtract(area1);
if (!collide2.equals(area2)) {
collide = true;
}
return collide;
}
ShapeCollision() {
int w = 400;
int h = 200;
img = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
final JLabel imageLabel = new JLabel(new ImageIcon(img));
x = w/2;
y = h/2;
//circle
obstacles[0] = new Area(new Ellipse2D.Double(40, 40, 30, 30));
int[] xTriangle = {330,360,345};
int[] yTriangle = {60,60,40};
//triangle
obstacles[1] = new Area(new Polygon(xTriangle, yTriangle, 3));
int[] xDiamond = {60,80,60,40};
int[] yDiamond = {120,140,160,140};
//diamond
obstacles[2] = new Area(new Polygon(xDiamond, yDiamond, 4));
int[] xOther = {360,340,360,340};
int[] yOther = {130,110,170,150};
// other
obstacles[3] = new Area(new Polygon(xOther, yOther, 4));
walls = new Area(new Rectangle(0,0,w,h));
ActionListener animate = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
animate();
imageLabel.repaint();
}
};
Timer timer = new Timer(50, animate);
timer.start();
JOptionPane.showMessageDialog(null, imageLabel);
timer.stop();
}
public void animate() {
Graphics2D g = img.createGraphics();
g.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(Color.BLUE);
g.fillRect(0, 0, img.getWidth(), img.getHeight());
x+=xDelta;
y+=yDelta;
int s = 15;
Area player = new Area(new Ellipse2D.Double(x, y, s, s));
// Acid test of edge collision;
if (doAreasCollide(player,walls)) {
if ( x+s>img.getWidth() || x<0 ) {
xDelta *= -1;
}
if(y+s>img.getHeight() || y<0 ) {
yDelta *= -1;
}
}
g.setColor(Color.ORANGE);
for (Area obstacle : obstacles) {
if (doAreasCollide(obstacle, player)) {
g.setColor(Color.RED);
} else {
g.setColor(Color.GREEN);
}
g.fill(obstacle);
}
g.setColor(Color.YELLOW);
g.fill(player);
g.dispose();
}
public static void main(String[] args) {
Runnable r = new Runnable() {
#Override
public void run() {
new ShapeCollision();
}
};
// Swing GUIs should be created and updated on the EDT
// http://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(r);
}
}
Edit
make it detect all the red color and set that as the collision bounds
At start-up, use the source seen in the Smoothing a jagged path question to get an outline of the red pixels (see the getOutline(Color target, BufferedImage bi) method). Store that Area as the single obstacle on start-up.
I need to draw a Polygon - by connecting consecutive points and then connecting the last point to the first.
With this goal I tried to use drawPolygon(xPoints, yPoints, nPoints). To my mind it's much more convenience approach to achieve this aim
But the Graphics class is abstract class and I we can't create instance object and call drawPolygon() method?
Code:
public void draw() {
Graphics g = null;
int xPoints [] = new int[pointsList.size()];
int yPoints [] = new int[pointsList.size()];
int nPoints = pointsList.size();
for (int i = 0; i < pointsList.size(); i++) {
xPoints [i] = (int) pointsList.get(i).getX();
yPoints [i] = (int) pointsList.get(i).getY();
}
g.drawPolygon(xPoints, yPoints, nPoints);
}
Can we circumvent calling this method at any way?
Maybe exist some other ways to achieve this aim?
The reason the developers made Graphics abstract was that a graphics object needs to come from somewhere. For instance a JPanel or JFrame object have a graphics object associated with them since they render viewable areas to the screen. A graphics object is usually assigned with the getGraphics() method. Here is a quick example:
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
public class Polygon extends JFrame {
public static void main(String args[]){
Test a = new Test();
a.drawAPolygon();
}
public Polygon(){
setSize(300,300);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
void drawAPolygon(int[] xPoints, int[] yPoints, int numPoints){
Graphics g = getGraphics();
g.drawPolygon(xPoints, yPoints, numPoints);
}
//#override
public void paint(Graphics g){
super.paint(g);
//could also do painting in here.
}
}
I have had the same problem, this was how I circumvented it:
//assuming you are displaying your polygon in a JFrame with a JPanel
public class SomeDrawingFrame extends JPanel{
SomeDrawingFrame(){
}
#Override //JFrame has this method that must be overwritten in order to
display a rendered drawing.
public void paintComponent(Graphics g){
super.paintComponent(g);
Polygon square = new Polygon();
// these points will draw a square
square.addPoint((0, 0)); //use this.getWidth() method if you want to
create based on screen size
square.addPoint((0, 100));
square.addPoint((100, 100));
square.addPoint((100, 0));
int y1Points[] = {0, 0, 100, 100};
g.draw(polygon);
}
}
now just create an instance of this and add it to a JFrame of minimum height and width of 100 each. You can use JFrame's getWidth() method which will return the size of the JFrame and use this to set your points instead (which is better) because then the image will render relative to the size of the frame itself.
Painting is controlled by the RepaintManager. Painting in Swing is done via a series of methods which are called on your behalf when the RepaintManager decides your component needs to be update (you can, of course, request repaints, but the RepaintManager will decided when, what and how much).
In order to perform custom painting in Swing, you need to override one of the methods that are called as part of the paint cycle.
It is recommended that you override paintComponent
You can check out
Performing Custom Painting
Painting in AWT and Swing
For more details.
In your example, your Graphics is null...Graphics g = null; which isn't going to help...
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class SimplePloy {
public static void main(String[] args) {
new SimplePloy();
}
public SimplePloy() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new PloyPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PloyPane extends JPanel {
private int[] xPoints;
private int[] yPoints;
public PloyPane() {
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
public void invalidate() {
xPoints = null;
yPoints = null;
super.invalidate();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (xPoints == null || yPoints == null) {
int width = getWidth() - 1;
int height = getHeight() - 1;
int halfWidth = width / 2;
int halfHeight = height / 2;
int innerWidth = width / 8;
int innerHeight = height / 8;
xPoints = new int[9];
yPoints = new int[9];
xPoints[0] = halfWidth;
yPoints[0] = 0;
xPoints[1] = halfWidth - innerWidth;
yPoints[1] = halfHeight - innerHeight;
xPoints[2] = 0;
yPoints[2] = halfHeight;
xPoints[3] = halfWidth - innerWidth;
yPoints[3] = halfHeight + innerHeight;
xPoints[4] = halfWidth;
yPoints[4] = height;
xPoints[5] = halfWidth + innerWidth;
yPoints[5] = halfHeight + innerHeight;
xPoints[6] = width;
yPoints[6] = halfHeight;
xPoints[7] = halfWidth + innerWidth;
yPoints[7] = halfHeight - innerHeight;
xPoints[8] = halfWidth;
yPoints[8] = 0;
}
g2d.drawPolygon(xPoints, yPoints, xPoints.length);
g2d.dispose();
}
}
}
import java.awt.*;
import javax.swing.*;
import java.awt.geom.*;
public class Box {
public static void main(String[] args){
BoxFrame frame = new BoxFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
class BoxFrame extends JFrame{
public BoxFrame(){
setTitle("BoxGame");
setSize(DEFAULT_WIDTH, DEFAULT_HEIGHT);
DrawComponent[] component = new DrawComponent[4];
component[0] = new DrawComponent(0, 0, 20, 20);
component[1] = new DrawComponent(400, 0, 20, 20);
add(component[0]);
add(component[1]);//here the problem is
}
public static final int DEFAULT_WIDTH = 600;
public static final int DEFAULT_HEIGHT = 400;
}
class DrawComponent extends JComponent{
private double left;
private double top;
private double width;
private double height;
public DrawComponent(double l, double t, double w, double h){
left = l;
top = t;
width = w;
height = h;
}
public void paintComponent(Graphics g){
Graphics2D g2 = (Graphics2D) g;
Rectangle2D rect = new Rectangle2D.Double(left, top, width, height);
g2.draw(rect);
g2.setPaint(Color.BLUE);
g2.fill(rect);
}
}
here is my code, it is not complicated. but when i try to draw two component, the window only draw one. this piece of code,when i get rid of the first component, the window will draw the second one. and i have looked up JFrame.add method in javadocs, but did not find what the error is,plz help me
The problem is that you're using the default layout manager of a JFrame which is a BorderLayout. When you add your second component, it will replace the first. (Both are added to the CENTER cell.)
If you want to use components to show the boxes I suggest you lay them out without overlap.
Otherwise I'd suggest you draw all boxes on the same component.