Swing - Avoid line filling between different lines - java

I'm trying to get into Swing by developing a simple drawing application in Java.
The problem is that I'm using an array of Points to keep track on the mouseDragged event, but when I release the mouse button and I begin to draw a new line they get connected (previous line's last node to the first point of the new line).
I was thinking maybe to keep track of the Points listened by the mousePressed and mouseReleased listeners in another ArrayList, to know which are the begin and end points of every line and then, when it's time to draw the lines between the points, checking if they are in the ArrayList and avoid drawing that line.
What is your suggested approach? How can I implement this in my code? Thank you!
package ccdraw;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import javax.swing.BorderFactory;
import javax.swing.JPanel;
import javax.swing.event.MouseInputListener;
#SuppressWarnings("serial")
public class DrawingArea extends JPanel implements MouseInputListener {
private ArrayList<Point> pointList = new ArrayList<Point>();
//private ArrayList<Point> beginEndPoints = new ArrayList<Point>();
public DrawingArea() {
super();
super.setPreferredSize(new Dimension(400,400));
setBorder(BorderFactory.createLineBorder(Color.black));
addMouseListener(this);
addMouseMotionListener(this);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
doDrawing(g);
}
private void doDrawing(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.setStroke(new BasicStroke(1.5f));
for (int i=1;i<pointList.size()-1;i++){
// here i should check when it changes between old and new line
// and don't draw the line between those two points!
//if(pointList.get(i).x - pointList.get(i+1).x <= 1 && pointList.get(i).y - pointList.get(i+1).y <= 1)
g2d.drawLine(pointList.get(i).x, pointList.get(i).y, pointList.get(i+1).x, pointList.get(i+1).y);
}
}
#Override
public void mouseClicked(MouseEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void mousePressed(MouseEvent arg0) {
pointList.add(arg0.getPoint());
}
#Override
public void mouseReleased( MouseEvent arg0) {
pointList.add(arg0.getPoint());
repaint();
}
#Override
public void mouseDragged(MouseEvent arg0) {
pointList.add(arg0.getPoint());
repaint();
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
}

I was thinking maybe to keep track of the Points listened by the mousePressed and mouseReleased listeners in another arraylist,
Keep an ArrayList of ArrayLists.
So every time you do a mousePressed you start an new ArrayList for the series of Points to be added by the mouseDragged event.
Then in the painting code you iterate through the two ArrayLists. This should be easier than adding exception logic for the start/end points.
Another option is to do the drawing directly to a BufferedImage, so you don't need to keep track of the Points at all. Check out Custom Painting Approaches which compares these two general custom painting approaches to decide which approach is best for your requirment.

Related

Relation and implementation of Graphics and paint? [duplicate]

I tried few source codes of drawing in java and they were working fine, but when i tried to make one of my own I could not get the paint(Grahpics g) method to work! I looked again at the codes I have and checked some of the tutorials in Oracle's pages but i don't seem to be able to know why it would not work.
can someone please check it and tell me what is wrong here??
main method:
public class main
{
public static void main(String[] args)
{
new board();
}
}
board:
import java.awt.BorderLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class board implements ActionListener
{
private JFrame f = new JFrame("Speedy");
private JPanel gamePanel = new JPanel();
private Image bg = new ImageIcon(this.getClass().getResource("road.png")).getImage();
private Timer t;
private car myCar = new car();
public board()
{
t = new Timer(50,this);
t.start();
gamePanel.setSize(600,400);
gamePanel.setDoubleBuffered(true);
gamePanel.setFocusable(true);
gamePanel.addKeyListener(new TAdapter());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(gamePanel,BorderLayout.CENTER);
//f.addKeyListener(new TAdapter());
f.setBounds(200,100,600,400);
f.setVisible(true);
f.revalidate();
f.repaint();
}
public void paint(Graphics g) {
gamePanel.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(bg,0,0,null);
g2d.drawImage(myCar.getImg(), myCar.xPos, myCar.yPos, null);
System.out.println("Painted");
g.dispose();
}
public void actionPerformed(ActionEvent e)
{
gamePanel.repaint();
//System.out.println("Painting..");
}
private class TAdapter extends KeyAdapter {
public void keyReleased(KeyEvent e) {}
public void keyPressed(KeyEvent e)
{
myCar.keyPressed(e);
System.out.println("You pressed: "+e);
}
}
}
car.java:
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import javax.swing.ImageIcon
;
public class car
{
private Image image;
public int xPos,yPos;
public car()
{
image = new ImageIcon(this.getClass().getResource("car.png")).getImage();
xPos = 300;
yPos = 200;
System.out.println(image.getWidth(null));
}
public Image getImg() {return image;}
public void move() {}
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key == KeyEvent.VK_LEFT) xPos -= 1;
if (key == KeyEvent.VK_RIGHT)xPos += 1;
if (key == KeyEvent.VK_UP) yPos -= 1;
if (key == KeyEvent.VK_DOWN) yPos += 1;
}
}
There are no errors, it shows me the width of the image which is right, also the timer triggers the ActionListener, also KeyListener is working, but the images would not draw! the paint(Graphics g) method just does not want to get triggered!
Googling it did not help.. I thought this would be a common problem but nobody has the problem I have, all solutions failed me.
help please?
If someone can explain it would be most appreciated!
Your class Board does not extend the JPanel class. So the paint() method is never called by the Swing.
Also, the statement gamePanel.repaint() will only execute the default JPanel paint() method of gamePanel. Instead you want the overridden paint method to be executed, so might want to do this:
public class Board extends JPanel implements ActionListener {
....
public void paint(Graphics g) {
this.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(bg,0,0,null);
g2d.drawImage(myCar.getImg(), myCar.xPos, myCar.yPos, null);
System.out.println("Painted");
g2d.dispose();
}
....
}
Replace your action functionality with this:
public void actionPerformed(ActionEvent e) {
this.repaint();
}
Alternative solution:
If you do not want your Board class to extend JPanel, you can also override the paint() method of the gamePanel as you initialize it.
gamePanel = new JPanel() {
#Override
public void paint(Graphics g) {
this.paint(g);
Graphics2D g2d = (Graphics2D)g;
g2d.drawImage(bg,0,0,null);
g2d.drawImage(myCar.getImg(), myCar.xPos, myCar.yPos, null);
g2d.dispose();
}
};
However, I would recommend the first solution rather than this one with anonymous classes.
When you call repaint on some container, then what happens is that Swing looks at all the components in that container and calls their paint method.
However, your board class (you should be calling it Board, by the way. Class names should always start with a capital) is not a component of your JFrame. When you call repaint, Swing will attempt to call the paint method of the JPanel that is a component of that JFrame. But you didn't override that method. You just added a paint method to your board, and board is not a component of the JFrame.
For this reason, usually you are supposed to create a class that extnds JPanel or some other component, and then add the current object of that class as a component to the JFrame. This way, your paint method will be called when the JFrame is repainted.
Your "main" class (board) should extend JPanel to work as expected.
With your way, paint would never be called. it acts like any normal self-written function.
If you want to keep things as they are, you can do something ike that:
gamePanel = new JPanel()
{
#Override
public void paint(Graphics g)
{
//your code here
}
};
Please keep in mind that a Class name should start with a capital letter. It won't make any errors but it is a naming convention as you can see here:
http://www.oracle.com/technetwork/java/codeconventions-135099.html

How do I make a (keyPressed) KeyListener?

Im trying to write a very simple program that allows you to control a sprite with arrow keys and move around the screen. From what I understand, to do this I need a keyPressed() KeyListener. I believe Im doing this correctly based on the Java Doc but its not returning the correct output when I press the designated key. Can someone please tell me what is wrong with my code, and if possible, provide a simple example of the usage of this class? Thanks!
(Note, code is unfinished)
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.*;
public class Display extends JPanel implements ActionListener, KeyListener {
Display() {
// super();
loadImages();
initTimer();
this.addKeyListener(this);
}
BufferedImage sprite;
Timer timer;
int up = 0;
public void loadImages() {
File spriteImage = new File("Pacman_sprite.png");
try {
sprite = ImageIO.read(spriteImage);
} catch (IOException e) {
System.out.println("Sprite import failed");
}
}
public void initTimer() {
timer = new Timer(100, this);
timer.start();
this.addKeyListener(this);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
System.out.println("Key press registered"); //does not print this?
if (key == KeyEvent.VK_UP) {
System.out.println("sucess"); // does not print this?
up++;
repaint();
}
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("release");
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
setBackground(Color.WHITE);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(sprite, 500, 500 + up, null);
}
#Override
public void actionPerformed(ActionEvent e) {
// TODO Auto-generated method stub
repaint();
}
}
EDIT:
May have found the answer here. I moved the key handling code to a new class called KeyHandler then added these two lines to the constructor:
addKeyListener(new KeyHandler());
setFocusable(true);
It now appears to be working just fine (sort of, at least it is detecting when the up key is hit. My graphics aren't.)
You are not adding the listener to the JPanel.
Note: I would suggest you to change your design. Right now, Display class is both a JPanel and a Listener (which doesn't make sense in my opinion).
But to add the listener in your code do something like,
this.addKeyListener(this); // This looks awkward right. That's why you should change the design.
in your constructor.

motion of rectangle by keyboard

I made the following code to move a rectangle using arrowkeys of keyboard. The "keyPressed" function does not seem to be working properly.Infact, i don't think it is even getting called when a key is pressed bcz when i tried to print some text when a key is pressed, it was not getting printed.All i see in the output window is a stationary rectangle fixed at the top left corner of the window.Here is my code....pls help me...i need it desperately
import javax.swing.JFrame;
public class Main
{
public static void main(String args[])
{
JFrame window=new JFrame();
window.setSize(600,400);
window.setTitle("window");
window.setVisible(true);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
drawingComponent DC=new drawingComponent();
window.add(DC);
}
}
import java.awt.Color;
import java.awt.Graphics2D;
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 javax.swing.JComponent;
import javax.swing.Timer;
public class drawingComponent extends JComponent implements ActionListener,KeyListener
{
Timer t=new Timer(2000,this);//moving after 5 milliseconds
static int x=0;
static int y=0;
private static int velx=0;
private static int vely=0;
public drawingComponent()
{
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
System.out.println("tr1");
}
public void paintComponent(Graphics g)
{
Graphics2D g2=(Graphics2D) g;
Rectangle rect1=new Rectangle(x,y,50,30);
g2.setColor(Color.RED);
g2.fill(rect1);
System.out.println("tr2");
}
public void actionPerformed(ActionEvent e) //inbuilt fncn f actionListener(interface) which needs to be created
{
x+=velx; //changing values
y+=vely;
System.out.println("tr3");
repaint(); //inbuilt fncn to repeat the paintComponent method
}
public void keyPressed(KeyEvent e)
{
int code=e.getKeyCode();
if(code==KeyEvent.VK_UP)
{ velx=0; vely=-1;repaint(); }
if(code==KeyEvent.VK_DOWN)
{ velx=0; vely=1; repaint(); }
if(code==KeyEvent.VK_LEFT)
{vely=0; velx=-1; repaint(); }
if(code==KeyEvent.VK_RIGHT)
{vely=0; velx=1; repaint();}
}
public void keyTyped(KeyEvent e)
{}
public void keyReleased(KeyEvent e)
{}
}
Welcome to the wonderful world of KeyListeners...
While you have set the component focusable, you've not requested that the component be focused.
You could try calling requestFocusInWindow, but he this raises the question of when to call it.
You could call it within the constructor, but because the component doesn't belong to a valid visible component yet, the call may fail. You could override the components addNotify method and add the call to it, after you call super.addNotify, but the requestFocusInWindow method doesn't gurentee that focus will be given the component
Instead, you could simply avoid all this hassle and use the key bindings API instead, which will give you control over the level of focus require for key events to be triggered
As a side note, you should call setVisible on your frame after you've set up the UI completely

KeyListener not work in object [duplicate]

This question already has answers here:
java keylistener not called
(2 answers)
Closed 8 years ago.
I got problem with KeyListener. I create class gra where is full code need to play game Tetris in constructor i only addKeyListener. In other class Menu I create object Gra gra = new Gra() and coll it when condition is met gra.start(g); but when I am in class Gra KeyListener not work. Please help me sove this issue.
He is code from class gra
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.Random;
import javax.swing.JPanel;
//#SuppressWarnings("serial")
public class Gra extends JPanel implements KeyListener{
public Gra(){
addKeyListener(this);
setFocusable(true);
requestFocus();
repaint();
}
public void start(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
setFocusable(true);
requestFocus();
repaint();
}
#Override
public void keyPressed(KeyEvent arg0) {
// TODO Auto-generated method stub
System.err.println("sdfafs");
char c = arg0.getKeyChar();
System.err.println(c);
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
char c = arg0.getKeyChar();
System.err.println(c);
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
char c = arg0.getKeyChar();
System.err.println(c);
}
}
KeyListener is very fickle. The component it is registered to must e focusable and have focus. There are many ways a component can loss focus or, depending on the system, not receive focus when requested.
Instead, use the key bindings API, as it will allow you to configure the focus level which will trigger the key events regardless

Mouse Pointer Problem in Java Swing

I have created the following simple Java Swing program which outputs a 3*3 square in the window every time the user clicks their mouse. The squares remain in the window even if the user clicks more than once. The program compiles and runs just fine, however, when one clicks in the window the square is drawn far below where the mouse pointer is. I've been racking my brain over this one for a while -- what can I change here to get the square to appear exactly with the pointer on each click? Many thanks for any help!
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
import javax.swing.JComponent;
import javax.swing.JFrame;
public class ClickCloud extends JComponent {
final ArrayList<Point2D> points = new ArrayList<Point2D>();
public void addPoint(Point2D a) {
points.add(a);
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
for (int i = 0; i < points.size(); i++) {
Point2D aPoint = points.get(i);
g2.draw(new Rectangle2D.Double(aPoint.getX(), aPoint.getY(), 3, 3));
}
}
public static void main(String[] args) {
final ClickCloud cloud = new ClickCloud();
JFrame aFrame = new JFrame();
class ClickListen implements MouseListener {
#Override
public void mouseClicked(MouseEvent arg0) {
}
#Override
public void mouseEntered(MouseEvent arg0) {
}
#Override
public void mouseExited(MouseEvent arg0) {
}
public void mousePressed(MouseEvent arg0) {
cloud.addPoint(arg0.getPoint());
cloud.repaint();
}
#Override
public void mouseReleased(MouseEvent arg0) {
}
}
aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
aFrame.setSize(500, 500);
aFrame.add(cloud);
aFrame.addMouseListener(new ClickListen());
aFrame.setVisible(true);
}
}
You're adding the MouseListener to the JFrame, but displaying the results in the JComponent and relative to the JComponent. So the location of the Point clicked will be relative to the JFrame's coordinates, but then displayed relative to the JComponent's coordinates which will shift things down by the distance of the title bar. Instead simply add the MouseListener to the same component that is responsible for displaying the results so that the display and clicking coordinates match:
aFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
aFrame.setSize(500, 500);
aFrame.add(cloud);
//!! aFrame.addMouseListener(new ClickListen()); // !! Removed
cloud.addMouseListener(new ClickListen()); // !! added
aFrame.setVisible(true);
By the way: Thanks for creating and posting a decent SSCCE as this makes it so much easier to analyse and solve your problem.

Categories