I have been creating a 2D camera in Swing. But then I discovered that if I use graphics.translate (to move the origin of coordinates) and set the position of the children using coordinates relative to the start of the world, unexpected behavior occurs:
In my original program, the JComponents don't redraw when they should.
In the published mcve, the image is still drawn where it was initially created, as well as being drawn on the correct side and not updating anymore.
The idea would be that, having the coordinates relative to the start of the "world", that the redraw process automatically converts that into coordinates relative to the JPanel.
What would be the correct way to achieve it without directly using the coordinates relative to the JPanel.
Application.java
package regexlang;
import java.awt.EventQueue;
import java.awt.Graphics;
import javax.swing.JFrame;
#SuppressWarnings("serial")
public class Application extends JFrame{
public Application() {
setSize(800, 600);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new Panel());
}
public static void main(String[] args) {
EventQueue.invokeLater(() -> {
Application ex = new Application();
ex.setVisible(true);
});
}
}
Panel.java
package regexlang;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.Timer;
#SuppressWarnings("serial")
public class Panel extends JPanel implements ActionListener, KeyListener{
private int x;
private int y;
private JLabel label;
public Panel() {
setLayout(null);
setBackground(Color.black);
addKeyListener(this);
setFocusable(true);
this.label = new JLabel("hola mundo");
this.label.setBounds(700, 0, 100, 100);
this.label.setOpaque(true);
add(this.label);
Timer timer = new Timer(1000, this);
timer.setRepeats(true);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.translate(-this.x, -this.y);
}
public void paintComponents(Graphics g) {
g.translate(-this.x, -this.y);
super.paintComponents(g);
}
#Override
public void actionPerformed(ActionEvent e) {
Random random = new Random();
Color color = new Color(random.nextInt(256),
random.nextInt(256),
random.nextInt(256));
this.label.setBackground(color);
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyPressed(KeyEvent e) {
this.x += 1;
repaint();
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Edit After some more thinking, I have a hypothesis that the coordinates used by swing are calculated before it gets to translate the origin of coordinates with Graphics.translate. I've tried to get around this by overriding the getGraphics method, to no avail (since it doesn't seem to be used much internally).
Related
This is a word-for-word example straight out of the book Java How to Program by Paul and Harvey Deitel.
PaintPanel.java
// Using class MouseMotionAdapter
import java.awt.Point;
import java.awt.Graphics;
import java.awt.Color;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JPanel;
public class PaintPanel extends JPanel {
private int pointCount = 0;
// array of 10,000 java.awt.Point references
private Point[] points = new Point[10000];
// set up gui and register mouse event handler
public PaintPanel() {
addMouseMotionListener(new MouseMotionAdapter() {
// store drag coordinates and repaint
public void MouseDragged(MouseEvent e) {
if (pointCount < points.length) {
points[pointCount] = e.getPoint();
pointCount++;
repaint();
} // end if
}
}
);
}
// draw ovals in a 4 x 4 bounding box at specified location on the window
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g); // clear drawing area
g.setColor(Color.BLUE);
// draw all points in the array
for(int i = 0; i < pointCount; i++)
g.fillOval(points[i].x, points[i].y, 4, 4);
}
}
Driver program Painter.java
import java.awt.BorderLayout;
import javax.swing.JFrame;
import javax.swing.JLabel;
public class Painter {
public static void main(String[] args) {
JFrame app = new JFrame("A Simple Paint Program");
PaintPanel pp = new PaintPanel();
app.add(pp, BorderLayout.CENTER);
app.add(new JLabel("Drag the rat to draw"), BorderLayout.SOUTH);
app.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
app.setSize(400, 200);
app.setVisible(true);
}
}
When I run it, it shows the drawing panel but nothing happens when I try to draw on it with the mouse. All code is copied verbatim from the book. What gives?
Java is case sensitive. It's mouseDragged, not MouseDragged. This is why you should always use #Override on methods you "think" you're overriding, this way you get compile time protection.
Runnable example...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new PaintPanel());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class PaintPanel extends JPanel {
private int pointCount = 0;
// array of 10,000 java.awt.Point references
private Point[] points = new Point[10000];
// set up gui and register mouse event handler
public PaintPanel() {
addMouseMotionListener(new MouseMotionAdapter() {
// store drag coordinates and repaint
#Override
public void mouseDragged(MouseEvent e) {
if (pointCount < points.length) {
points[pointCount] = e.getPoint();
pointCount++;
repaint();
} // end if
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(400, 200);
}
// draw ovals in a 4 x 4 bounding box at specified location on the window
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g); // clear drawing area
g.setColor(Color.BLUE);
// draw all points in the array
for (int i = 0; i < pointCount; i++) {
g.fillOval(points[i].x, points[i].y, 4, 4);
}
}
}
}
I made a program to displace a random number of rectangles on the screen with buttons to make fewer or more with either doubles or halves the number of rectangles. Then I was asked to modify the program to add a slider instead of buttons to the program to control the amount of rectangle on the screen. I am having some trouble.
I am getting a few errors. My professor insists on using IntelliJ IDE for the development environment, which I am not too familiar with. It's asking me to move package c16 and also to make public class Random Rectangles to main or not public any help would be good.
package c16;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JSlider;
import javax.swing.SwingUtilities;
import javax.swing.event.ChangeEvent;
import javax.swing.event.ChangeListener;
public class RandomRectangles extends JFrame {
private int rectangle_count=1;
private Random random = new Random();
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable()
{
public void run() {
RandomRectangles r = new RandomRectangles();
r.setTitle("Rectangles");
r.setSize(new Dimension(500, 500));
r.setVisible(true);
}
});
}
public RandomRectangles()
{
super();
setLayout(new BorderLayout());
#SuppressWarnings("unused")
JSlider slider = new JSlider(JSlider.HORIZONTAL, 0, 500, 1);
slider.addChangeListener(new ChangeListener() {
#Override
public void stateChanged(ChangeEvent arg0) {
rectangle_count = slider.getValue();
RandomRectangles.this.repaint();
}
});
/*
On clicking SHRINK(FEWER),
half the number of rectangles in the RectangleArea
with different co-ordinates.
*/
JPanel rectangleArea = new JPanel()
{
public void paintComponent(Graphics g) //in-built function.
{
super.paintComponent(g);
g.setColor(Color.BLUE);
/*Get the random co-ordinates for Rectangle.*/
for(int i=0;i<rectangle_count;i++){
int a= (int)Math.floor(random.nextDouble()*getWidth());
int b=(int)Math.floor(random.nextDouble()*getHeight());
int c = (int)Math.floor(random.nextDouble()*(getWidth()-a));
int d = (int)Math.floor(random.nextDouble()*(getHeight()-b));
g.drawRect(a,b,c,d);
}
}
};
/*Add the buttons and the panel to the BorderLayout
so that they are visible for demo.
*/
add(slider,BorderLayout.NORTH);
add(rectangleArea,BorderLayout.CENTER);
}
}
I am very new to JAVA and i am just a hobbyist,Just recently I have been following some tutorials on making UI and all thee looking in to learn about this, any code snippets are welcome but also more what is this function / process called so i can look deeper into different parts of swing from the ORACLE DOCS which is really informative, I am still working my way through the components.
I do not have an overall goal at present and am just finding my feet, but i want to try and make a small top-down 2d maze game to learn.
One part got my attention and it was this demo at the bottom of the page
https://docs.oracle.com/javase/tutorial/uiswing/painting/step3.html
Basically this is the code and I am trying to make it so when I click inside the frame the rectangle moves slowly towards the point of the mouse click rather than jumping to it (and later on if possible to click in another location and it would change its course before reaching its destination.
What kind of area do i need to be looking in to learn about this, any code snippets are welcome but also more what is this function / process called so i can look deeper into it.
WORKING DEMO CODE BELOW
package painting;
import javax.swing.SwingUtilities;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.BorderFactory;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseMotionListener;
import java.awt.event.MouseMotionAdapter;
public class SwingPaintDemo3 {
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() { I am Trying to make it so when I click inside the
public void run() {
createAndShowGUI();
}
});
}
private static void createAndShowGUI() {
System.out.println("Created GUI on EDT? "+
SwingUtilities.isEventDispatchThread());
JFrame f = new JFrame("Swing Paint Demo");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new MyPanel());
f.pack();
f.setVisible(true);
}
}
class MyPanel extends JPanel {
private int squareX = 50;
private int squareY = 50;
private int squareW = 20;
private int squareH = 20;
public MyPanel() {
setBorder(BorderFactory.createLineBorder(Color.black));
addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
addMouseMotionListener(new MouseAdapter() {
public void mouseDragged(MouseEvent e) {
moveSquare(e.getX(),e.getY());
}
});
}
private void moveSquare(int x, int y) {
int OFFSET = 1;
if ((squareX!=x) || (squareY!=y)) {
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
squareX=x;
squareY=y;
repaint(squareX,squareY,squareW+OFFSET,squareH+OFFSET);
}
}
public Dimension getPreferredSize() {
return new Dimension(250,200);
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawString("This is my custom Panel!",10,20);
g.setColor(Color.RED);
g.fillRect(squareX,squareY,squareW,squareH);
g.setColor(Color.BLACK);
g.drawRect(squareX,squareY,squareW,squareH);
}
}
Thanks for any advice ;)
I have written a Java code to draw a polygon on an image. When I put my cursor inside the polygon it prints "Inside" otherwise "Outside". So the detection of the points inside the polygon is working fine.
But I want to implement the effect of setToolTipText inside the polygon i.e. at the time of mouse hover inside the polygon, it will show the floating text "Inside".
Similar to the effect in this image:
http://www.java2s.com/Code/Java/Swing-JFC/WorkingwithTooltipText.htm
What are the minimal changes to be made in the following code to get the desired effect?
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import javax.swing.border.*;
import java.awt.image.*;
import java.awt.Graphics.*;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
class page1 extends JFrame implements MouseListener,MouseMotionListener ,ActionListener
{
JFrame f;
JLabel l;
JPanel p1;
ImageIcon ii;
Image img;
int height;
int width;
Container c;
int pixels[];
PixelGrabber pg;
JPanel panel;
Graphics2D gg;
Polygon pp1=new Polygon();
boolean startHovercurrent,startHoverprev=false;
page1()
{
f=new JFrame("Sample Page");
ii=new ImageIcon("sample.jpg");
img=ii.getImage();
height=ii.getIconHeight();
width=ii.getIconWidth();
pixels=new int[ii.getIconWidth()*ii.getIconHeight()];
pg=new PixelGrabber(img,0,0,ii.getIconWidth(),ii.getIconHeight(),pixels,0,ii.getIconWidth());
try
{
pg.grabPixels();
}
catch(InterruptedException k)
{
}
//add points of polygon
pp1.addPoint(300,300);
pp1.addPoint(380,300);
pp1.addPoint(380,220);
pp1.addPoint(300,220);
l=new JLabel(ii,JLabel.CENTER);
c=f.getContentPane();
JDesktopPane desk = new JDesktopPane();
JInternalFrame p = new JInternalFrame("Image Frame",false, false, true, false);
JScrollPane scroll = new JScrollPane(l);
p.setContentPane(scroll);
p.setBounds(0, 0, 740, 600);
desk.add(p);
p.setVisible(true);
l.addMouseListener(this);
l.addMouseMotionListener(this);
c.add(desk, BorderLayout.CENTER);
f.setSize(1024,738);
f.setVisible(true);
}
public static void main(String args[])
{
new page1();
}
public void mouseClicked(MouseEvent me)
{
}
public void mouseEntered(MouseEvent me)
{
}
public void mouseExited(MouseEvent me)
{
}
public void mousePressed(MouseEvent me)
{
}
public void mouseReleased(MouseEvent me)
{
}
public void mouseMoved(MouseEvent me)
{
boolean contain1;
int mx,my;
gg=(Graphics2D)l.getGraphics();
gg.setColor(new Color(255,0,0) );
gg.fillPolygon(pp1);
mx = me.getX();
my = me.getY();
//check if mouse cursor is inside polygon or not
// do not print anything if next cursor position is in same state
contain1=pp1.contains(mx,my);
if (contain1) {
startHovercurrent = true;
if(startHovercurrent!=startHoverprev)
System.out.println("Inside");
startHoverprev=startHovercurrent;
}
else {
startHovercurrent = false;
if(startHovercurrent!=startHoverprev)
System.out.println("Outside");
startHoverprev=startHovercurrent;
}
}
public void mouseDragged(MouseEvent me)
{
}
public void actionPerformed(ActionEvent ae)
{
}
}
For this usage, How to Use Tool Tips suggests overriding the getToolTipText() method of the enclosing JComponent. This answer outlines the approach for JMapViewer and ChartPanel. In the example below, getToolTipText() simply returns the name of any Shape that contains() the triggering mouse event. For comparison, the JLabel at window's bottom gets a conventional too tip via setToolTipText().
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.ToolTipManager;
/**
* #see https://stackoverflow.com/a/53609066/230513
* #see https://stackoverflow.com/a/25944439/230513
*/
public class ShapeToolTip {
private static class ShapePanel extends JPanel {
private final List<Shape> list = new ArrayList<>();
public ShapePanel() {
Polygon p = new Polygon();
p.addPoint(500, 100);
p.addPoint(500, 400);
p.addPoint(200, 400);
list.add(p);
list.add(new Ellipse2D.Double(100, 100, 200, 200));
ToolTipManager.sharedInstance().registerComponent(this);
}
#Override
public String getToolTipText(MouseEvent e) {
for (Shape shape : list) {
if (shape.contains(e.getX(), e.getY())) {
return shape.getClass().getName();
}
}
return "Outside";
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.blue);
Graphics2D g2d = (Graphics2D) g;
g2d.setStroke(new BasicStroke(2));
for (Shape shape : list) {
g2d.draw(shape);
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
}
private void display() {
JFrame f = new JFrame("ShapeToolTip");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new ShapePanel());
JLabel title = new JLabel("Shape Tool Tip", JLabel.CENTER);
title.setToolTipText("Title");
title.setFont(title.getFont().deriveFont(Font.BOLD, 24));
f.add(title, BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new ShapeToolTip()::display);
}
}
Copy and modify your canvas to implement the KeyListener interface, add itself as its own KeyListener , and call setFocusable(true) in order to receive keyboard focus. When a key is typed, your program should draw the corresponding character (see KeyEvent.getKeyChar() ) on the canvas at the location of the last mouse event. If another key is typed without the mouse being clicked, then draw the next character to
the right of the previous one as if you were typing in a text field. Again, think about what state you need to maintain to do this. It doesn’t have to be perfect (you can hard-code the width of the characters if you like).
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class Canvas05 extends JComponent implements KeyListener {
public Canvas05() {
addKeyListener(this);
setFocusable(true);
}
#Override
public void keyTyped(KeyEvent e) {
System.out.print(e.getKeyChar());
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
public static void main (String[] args){
Canvas05 c = new Canvas05();
JFrame frame = new JFrame("Q05");
frame.add(c);
frame.setSize(400, 400);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
}
}
Here you go. The location from mouseEvent is very easily acessable from the MouseListener event where you can declare both an x and a y variable from
MouseListener.getX() and MouseListener.getY();
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JPanel{
public static int WIDTH = 500,HEIGHT = 500;
public static char myChar = ' ';
public static Game game = new Game();
public static JFrame frame = new JFrame();
public Game() {
this.addKeyListener(new KeyListener(){
public void keyPressed(KeyEvent e) {
myChar = (char)e.getKeyCode();
repaint();
}
#Override
public void keyReleased(KeyEvent arg0) {
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
});
setFocusable(true);
}
public void paint(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g.clearRect(0, 0, WIDTH, HEIGHT);
g.setColor(Color.red);
g.setFont(new Font("Cambria",Font.BOLD,30));
g.drawString(String.valueOf(myChar),100, 100);
}
public static void main(String [] args){
frame.add(game);
frame.setSize(WIDTH, HEIGHT);
frame.setResizable(false);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
}