I am completely new to Java and I am learning how to write a program that detects and reacts to collision based on a professor's lecture video. Here is a link to the video.
All of my code should be similar to what is in his lecture. My error appears to be in the NewJFrame.java file. Why is ActionListener not working? Thanks in advance for the help.
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.event.ActionListener;
import javafx.event.ActionEvent;
import javax.swing.Timer;
/**
*
* #author PC
*/
public class NewJFrame extends javax.swing.JFrame {
/**
* Creates new form NewJFrame
*/
public NewJFrame() {
initComponents();
bf = new BallField(getWidth(), getHeight());
add(bf);
pack();
njTimer = new Timer(1,
new ActionListener() {
public void actionPerformed(ActionEvent e) {
bf.detectCollision();
}
});
njTimer.start();
}
BallField bf;
Timer njTimer;
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
#SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">
private void initComponents() {
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 400, Short.MAX_VALUE)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGap(0, 300, Short.MAX_VALUE)
);
pack();
}// </editor-fold>
/**
* #param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(NewJFrame.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new NewJFrame().setVisible(true);
}
});
}
// Variables declaration - do not modify
// End of variables declaration
}
The line saying "new ActionListener() {" is underlined and I get an error message saying:
Exception in thread "main" java.lang.RuntimeException: Uncompilable source code - is not abstract and does not override abstract method actionPerformed(java.awt.event.ActionEvent) in java.awt.event.ActionListener
at collisiondetection.NewJFrame.(NewJFrame.java:28)
at collisiondetection.Main.main(Main.java:20)
NewJFrame.java:28 refers to the line that says "njTimer = new Timer(1," in the code above.
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Here is my code for the other .java files for reference.
Main file:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
/**
*
* #author PC
*/
public class Main {
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
NewJFrame njf = new NewJFrame();
njf.setVisible(true);
}
}
BallField.java:
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.util.LinkedList;
import java.util.ListIterator;
import javax.swing.JPanel;
/**
*
* #author PC
*/
public class BallField extends JPanel {
public BallField(int width, int height)
{
setSize(new Dimension(width, height));
setMinimumSize(new Dimension(width, height));
setMaximumSize(new Dimension(width, height));
bfList = new LinkedList<Shape>();
fillList();
}
public void detectCollision()
{
if(bfList.size() == 0)
{
fillList();
}
bfBall.move();
ListIterator iter = bfList.listIterator();
boolean collision = false;
while ((collision == false && iter.hasNext()))
{
Shape sh = (Shape) iter.next();
if(sh.collide(bfBall))
{
iter.remove();
collision = true;
}
}
}
public void PaintComponent(Graphics gfx)
{
int bWidth = getWidth();
int bHeight = getHeight();
gfx.setColor(bfBackground);
gfx.fillRect(0, 0, bWidth, bHeight);
ListIterator iter = bfList.listIterator();
while(iter.hasNext())
{
Shape sh = (Shape) iter.next();
sh.drawShape(gfx);
}
bfBall.drawBall(gfx);
}
private void fillList() {
int bWidth = getWidth();
int bHeight = getHeight();
int size = Math.min(bWidth, bHeight);
size -= Math.max(bfNumRow, bfNumCol) * brickGap;
size = size / (Math.max(bfNumRow, bfNumCol) + bfEmptyRow);
// add more margin
Shape.setSize(size);
if (bfBall == null) {
bfBall = new MovingBall(size, bWidth, bHeight);
} else {
bfBall.reset();
}
for (int rowCnt = 0; rowCnt < bfNumRow; rowCnt++) {
int xloc = bWidth / 2 - (bfNumRow / 2 - rowCnt) * (size + brickGap);
for (int colCnt = 0; colCnt < bfNumCol; colCnt++) {
double rand = Math.random();
Float cR = new Float(Math.random());
Float cG = new Float(Math.random());
Float cB = new Float(Math.random());
Color bc = new Color(cR.floatValue(), cG = cG.floatValue(), cB.floatValue());
int yloc = bHeight / 2 - (bfNumCol / 2 - colCnt) * (size + brickGap);
if (rand > .5) {
Circle cb = new Circle(xloc, yloc, bc);
bfList.add(cb);
} else {
Square sb = new Square(xloc, yloc, bc);
bfList.add(sb);
}
}
}
}
LinkedList<Shape> bfList;
MovingBall bfBall;
static private final Color bfBackground = Color.white;
static private final int bfNumRow = 6;
static private final int bfNumCol = 6;
static private final int bfEmptyRow = 4;
static private final int brickGap = 4;
}
Vector2D.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
/**
*
* #author PC
*/
public class Vector2D {
public Vector2D(double x, double y, boolean u)
{
xVal = x;
yVal = y;
unit = u;
if(unit)
{
makeUnit();
}
}
public void add(Vector2D vec)
{
xVal += vec.xVal;
yVal += vec.yVal;
if(unit)
{
makeUnit();
}
}
public double getX()
{
return xVal;
}
public double getY()
{
return yVal;
}
public void reflect(Vector2D nor)
{
if((unit == false) || (nor.unit == false))
{
System.out.println("ERROR, not unit vector");
}
double ip = innerProduct(nor);
xVal += -2 * ip * nor.xVal;
yVal += -2 * ip * nor.yVal;
makeUnit();
}
private double innerProduct(Vector2D vec)
{
return (xVal * vec.xVal + yVal * vec.yVal);
}
private void makeUnit()
{
double mag = xVal * xVal + yVal * yVal;
if(mag == 0)
{
System.out.println("ERROR, zero vector");
}
else
{
mag = Math.sqrt(mag);
xVal /= mag;
yVal /= mag;
}
}
private double xVal;
private double yVal;
private boolean unit;
}
MovingBall.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
/**
*
* #author PC
*/
public class MovingBall {
public MovingBall(int sz, int bw, int bh)
{
mbSize = sz;
bWidth = bw;
bHeight = bh;
reset();
}
public Rectangle getRect()
{
return (new Rectangle((int) Math.round(mbLoc.getX() - mbSize/2), (int) Math.round(mbLoc.getY() - mbSize/2), mbSize, mbSize));
}
public void reset()
{
mbLoc = new Vector2D(mbSize, mbSize, false);
mbVel = new Vector2D(Math.random() + .1, Math.random() + .1, true);
}
public double getX()
{
return mbLoc.getX();
}
public double getY()
{
return mbLoc.getY();
}
public int getSize()
{
return mbSize;
}
public void reflect(Vector2D nor)
{
mbVel.reflect(nor);
}
public void move()
{
mbLoc.add(mbVel);
// hit a wall?
if(mbLoc.getX() >= (bWidth - mbSize/2))
{
// hit right wall
Vector2D nor = new Vector2D(-1, 0, true);
reflect(nor);
}
if(mbLoc.getY() >= (bHeight - mbSize/2))
{
Vector2D nor = new Vector2D(0, -1, true);
reflect(nor);
}
}
public void drawBall(Graphics gfx)
{
int x = (int) Math.round(mbLoc.getX() - mbSize/2);
int y = (int) Math.round(mbLoc.getY() - mbSize/2);
gfx.setColor(bColor);
gfx.fillOval(x, y, mbSize, mbSize);
}
private Vector2D mbLoc;
private Vector2D mbVel;
private int mbSize;
private Color bColor = Color.black;
int bWidth;
int bHeight;
}
Shape.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.Color;
import java.awt.Graphics;
/**
*
* #author PC
*/
abstract public class Shape {
public Shape(int x, int y, Color c)
{
s_X = x;
s_Y = y;
s_Color = c;
}
public static void setSize(int sz)
{
s_Size = sz;
}
abstract public boolean collide(MovingBall ball);
abstract public void drawShape(Graphics gfx);
protected int s_X;
protected int s_Y;
protected Color s_Color;
protected static int s_Size;
}
Square.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import static collisiondetection.Shape.s_Size;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
/**
*
* #author PC
*/
public class Square extends Shape {
public Square(int x, int y, Color c)
{
super(x, y, c);
}
public boolean collide(MovingBall ball)
{
Rectangle r1 = new Rectangle(s_X - s_Size / 2, s_Y - s_Size, s_Size, s_Size);
Rectangle r2 = ball.getRect();
Rectangle r3 = r1.intersection(r2);
if (r3.isEmpty())
{
// no collision
// note thatr3 is not null
return false;
}
if (r3.getWidth() < r3.getHeight())
{
// hit horizontally
if (ball.getX() < s_X) {
// hit the left side
Vector2D nor = new Vector2D(-1, 0, true);
ball.reflect(nor);
} else {
Vector2D nor = new Vector2D(1, 0, true);
ball.reflect(nor);
}
} else {
if (ball.getY() < s_Y) {
// hit the top
Vector2D nor = new Vector2D(0, -1, true);
ball.reflect(nor);
} else {
Vector2D nor = new Vector2D(0, 1, true);
ball.reflect(nor);
}
}
return true;
}
public void drawShape(Graphics gfx)
{
gfx.setColor(s_Color);
gfx.fillRect(s_X - s_Size/2, s_Y - s_Size/2, s_Size, s_Size);
}
}
Cirlce.java
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package collisiondetection;
import java.awt.Color;
import java.awt.Graphics;
/**
*
* #author PC
*/
public class Circle extends Shape{
public Circle(int x, int y, Color c)
{
super(x, y, c);
}
public boolean collide(MovingBall ball)
{
double deltaX = ball.getX() - s_X;
double deltaY = ball.getY() - s_Y;
double centerDistance = Math.sqrt(deltaX * deltaX + deltaY * deltaY);
if(centerDistance * 2 > s_Size + ball.getSize())
{
// no collision
// size is the diameter, not radius
return false;
}
Vector2D nor = new Vector2D(deltaX, deltaY, true);
ball.reflect(nor);
return true;
}
public void drawShape(Graphics gfx)
{
gfx.setColor(s_Color);
gfx.fillOval(s_X - s_Size/2, s_Y - s_Size/2, s_Size, s_Size);
}
}
You're importing the wrong ActionEvent.
import javafx.event.ActionEvent;
should be
import java.awt.event.ActionEvent;
... and welcome to this site, and thanks for providing the offending code, the error message, and the line that causes the error. I predict that you will go far with your coding.
Related
So I working on adding sound to an Android Snake game. What I am required to do is to add sound in for 3 events...
When the snake moves
When the snake eats
When the snake collides w/ itself or the wall
In order to do this I am using the SoundPool class and to sort of clean things up I just made separate class could SoundPlayer that sets up everything and has 3 methods to address the events. However, I am am trying not to restructure the code we were given too much but the primary code regarding movement and events is not in onCreate(). Where all examples I have practiced with say to generate the instantiate and add call the sound. Instead it is in its own .java file that is called in onCreate() so I am at my wits end on how and where I can instantiate the SoundPlayer class.
Can anyone out there help with how I can achieve the requirements and understand where I am going wrong with trying to use this class and its methods?
Snake.java
package com.example.android.snake;
import android.app.Activity;
import android.media.AudioAttributes;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.SoundPool;
import android.os.Build;
import android.os.Bundle;
import android.view.Window;
import android.widget.TextView;
/**
* Snake: a simple game that everyone can enjoy.
*
* This is an implementation of the classic Game "Snake", in which you control a
* serpent roaming around the garden looking for apples. Be careful, though,
* because when you catch one, not only will you become longer, but you'll move
* faster. Running into yourself or the walls will end the game.
*
*/
public class Snake extends Activity {
private SnakeView mSnakeView;
private static String ICICLE_KEY = "snake-view";
//U01A1: Initialize Sound class
SoundPlayer sound;
public SoundPlayer getSound() {
return sound;
}
/**
* Called when Activity is first created. Turns off the title bar, sets up
* the content views, and fires up the SnakeView.
*
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.snake_layout);
mSnakeView = (SnakeView) findViewById(R.id.snake);
mSnakeView.setTextView((TextView) findViewById(R.id.text));
//sound
sound = new SoundPlayer(this); //sound
if (savedInstanceState == null) {
// We were just launched -- set up a new game
mSnakeView.setMode(SnakeView.READY);
} else {
// We are being restored
Bundle map = savedInstanceState.getBundle(ICICLE_KEY);
if (map != null) {
mSnakeView.restoreState(map);
} else {
mSnakeView.setMode(SnakeView.PAUSE);
}
}
}
#Override
protected void onPause() {
super.onPause();
// Pause the game along with the activity
mSnakeView.setMode(SnakeView.PAUSE);
}
#Override
public void onSaveInstanceState(Bundle outState) {
//Store the game state
outState.putBundle(ICICLE_KEY, mSnakeView.saveState());
}
}
SnakeView.java
package com.example.android.snake;
import java.util.ArrayList;
import java.util.Random;
import android.content.Context;
import android.content.res.Resources;
import android.os.Handler;
import android.os.Message;
import android.util.AttributeSet;
import android.os.Bundle;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.TextView;
/**
* SnakeView: implementation of a simple game of Snake
*
*
*/
public class SnakeView extends TileView {
private static final String TAG = "SnakeView";
//private SoundPlayer sound;
/**
* Current mode of application: READY to run, RUNNING, or you have already
* lost. static final ints are used instead of an enum for performance
* reasons.
*/
private int mMode = READY;
public static final int PAUSE = 0;
public static final int READY = 1;
public static final int RUNNING = 2;
public static final int LOSE = 3;
/**
* Current direction the snake is headed.
*/
private int mDirection = NORTH;
private int mNextDirection = NORTH;
private static final int NORTH = 1;
private static final int SOUTH = 2;
private static final int EAST = 3;
private static final int WEST = 4;
/**
* Labels for the drawables that will be loaded into the TileView class
*/
private static final int RED_STAR = 1;
private static final int YELLOW_STAR = 2;
private static final int GREEN_STAR = 3;
/**
* mScore: used to track the number of apples captured mMoveDelay: number of
* milliseconds between snake movements. This will decrease as apples are
* captured.
*/
private long mScore = 0;
private long mMoveDelay = 600;
/**
* mLastMove: tracks the absolute time when the snake last moved, and is used
* to determine if a move should be made based on mMoveDelay.
*/
private long mLastMove;
/**
* mStatusText: text shows to the user in some run states
*/
private TextView mStatusText;
/**
* mSnakeTrail: a list of Coordinates that make up the snake's body
* mAppleList: the secret location of the juicy apples the snake craves.
*/
private ArrayList<Coordinate> mSnakeTrail = new ArrayList<Coordinate>();
private ArrayList<Coordinate> mAppleList = new ArrayList<Coordinate>();
/**
* Everyone needs a little randomness in their life
*/
private static final Random RNG = new Random();
/**
* Create a simple handler that we can use to cause animation to happen. We
* set ourselves as a target and we can use the sleep()
* function to cause an update/invalidate to occur at a later date.
*/
private RefreshHandler mRedrawHandler = new RefreshHandler();
class RefreshHandler extends Handler {
#Override
public void handleMessage(Message msg) {
SnakeView.this.update();
SnakeView.this.invalidate();
}
public void sleep(long delayMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), delayMillis);
}
};
/**
* Constructs a SnakeView based on inflation from XML
*
* #param context
* #param attrs
*/
public SnakeView(Context context, AttributeSet attrs) {
super(context, attrs);
initSnakeView();
}
public SnakeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initSnakeView();
}
private void initSnakeView() {
setFocusable(true);
Resources r = this.getContext().getResources();
resetTiles(4);
loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
}
private void initNewGame() {
mSnakeTrail.clear();
mAppleList.clear();
// For now we're just going to load up a short default eastbound snake
// that's just turned north
mSnakeTrail.add(new Coordinate(7, 7));
mSnakeTrail.add(new Coordinate(6, 7));
mSnakeTrail.add(new Coordinate(5, 7));
mSnakeTrail.add(new Coordinate(4, 7));
mSnakeTrail.add(new Coordinate(3, 7));
mSnakeTrail.add(new Coordinate(2, 7));
mNextDirection = NORTH;
// Two apples to start with
addRandomApple();
addRandomApple();
mMoveDelay = 600;
mScore = 0;
}
/**
* Given a ArrayList of coordinates, we need to flatten them into an array of
* ints before we can stuff them into a map for flattening and storage.
*
* #param cvec : a ArrayList of Coordinate objects
* #return : a simple array containing the x/y values of the coordinates
* as [x1,y1,x2,y2,x3,y3...]
*/
private int[] coordArrayListToArray(ArrayList<Coordinate> cvec) {
int count = cvec.size();
int[] rawArray = new int[count * 2];
for (int index = 0; index < count; index++) {
Coordinate c = cvec.get(index);
rawArray[2 * index] = c.x;
rawArray[2 * index + 1] = c.y;
}
return rawArray;
}
/**
* Save game state so that the user does not lose anything
* if the game process is killed while we are in the
* background.
*
* #return a Bundle with this view's state
*/
public Bundle saveState() {
Bundle map = new Bundle();
map.putIntArray("mAppleList", coordArrayListToArray(mAppleList));
map.putInt("mDirection", Integer.valueOf(mDirection));
map.putInt("mNextDirection", Integer.valueOf(mNextDirection));
map.putLong("mMoveDelay", Long.valueOf(mMoveDelay));
map.putLong("mScore", Long.valueOf(mScore));
map.putIntArray("mSnakeTrail", coordArrayListToArray(mSnakeTrail));
return map;
}
/**
* Given a flattened array of ordinate pairs, we reconstitute them into a
* ArrayList of Coordinate objects
*
* #param rawArray : [x1,y1,x2,y2,...]
* #return a ArrayList of Coordinates
*/
private ArrayList<Coordinate> coordArrayToArrayList(int[] rawArray) {
ArrayList<Coordinate> coordArrayList = new ArrayList<Coordinate>();
int coordCount = rawArray.length;
for (int index = 0; index < coordCount; index += 2) {
Coordinate c = new Coordinate(rawArray[index], rawArray[index + 1]);
coordArrayList.add(c);
}
return coordArrayList;
}
/**
* Restore game state if our process is being relaunched
*
* #param icicle a Bundle containing the game state
*/
public void restoreState(Bundle icicle) {
setMode(PAUSE);
mAppleList = coordArrayToArrayList(icicle.getIntArray("mAppleList"));
mDirection = icicle.getInt("mDirection");
mNextDirection = icicle.getInt("mNextDirection");
mMoveDelay = icicle.getLong("mMoveDelay");
mScore = icicle.getLong("mScore");
mSnakeTrail = coordArrayToArrayList(icicle.getIntArray("mSnakeTrail"));
}
/*
* handles key events in the game. Update the direction our snake is traveling
* based on the DPAD. Ignore events that would cause the snake to immediately
* turn back on itself.
*
* (non-Javadoc)
*
* #see android.view.View#onKeyDown(int, android.os.KeyEvent)
*/
#Override
public boolean onKeyDown(int keyCode, KeyEvent msg) {
if (keyCode == KeyEvent.KEYCODE_DPAD_UP) {
if (mMode == READY | mMode == LOSE) {
/*
* At the beginning of the game, or the end of a previous one,
* we should start a new game.
*/
initNewGame();
setMode(RUNNING);
update();
return (true);
}
if (mMode == PAUSE) {
/*
* If the game is merely paused, we should just continue where
* we left off.
*/
setMode(RUNNING);
update();
return (true);
}
if (mDirection != SOUTH) {
mNextDirection = NORTH;
}
return (true);
}
if (keyCode == KeyEvent.KEYCODE_DPAD_DOWN) {
if (mDirection != NORTH) {
mNextDirection = SOUTH;
}
return (true);
}
if (keyCode == KeyEvent.KEYCODE_DPAD_LEFT) {
if (mDirection != EAST) {
mNextDirection = WEST;
}
return (true);
}
if (keyCode == KeyEvent.KEYCODE_DPAD_RIGHT) {
if (mDirection != WEST) {
mNextDirection = EAST;
}
return (true);
}
return super.onKeyDown(keyCode, msg);
}
/**
* Sets the TextView that will be used to give information (such as "Game
* Over" to the user.
*
* #param newView
*/
public void setTextView(TextView newView) {
mStatusText = newView;
}
/**
* Updates the current mode of the application (RUNNING or PAUSED or the like)
* as well as sets the visibility of textview for notification
*
* #param newMode
*/
public void setMode(int newMode) {
int oldMode = mMode;
mMode = newMode;
if (newMode == RUNNING & oldMode != RUNNING) {
mStatusText.setVisibility(View.INVISIBLE);
update();
return;
}
Resources res = getContext().getResources();
CharSequence str = "";
if (newMode == PAUSE) {
str = res.getText(R.string.mode_pause);
}
if (newMode == READY) {
str = res.getText(R.string.mode_ready);
}
if (newMode == LOSE) {
str = res.getString(R.string.mode_lose_prefix) + mScore
+ res.getString(R.string.mode_lose_suffix);
}
mStatusText.setText(str);
mStatusText.setVisibility(View.VISIBLE);
}
/**
* Selects a random location within the garden that is not currently covered
* by the snake. Currently _could_ go into an infinite loop if the snake
* currently fills the garden, but we'll leave discovery of this prize to a
* truly excellent snake-player.
*
*/
private void addRandomApple() {
Coordinate newCoord = null;
boolean found = false;
while (!found) {
// Choose a new location for our apple
int newX = 1 + RNG.nextInt(mXTileCount - 2);
int newY = 1 + RNG.nextInt(mYTileCount - 2);
newCoord = new Coordinate(newX, newY);
// Make sure it's not already under the snake
boolean collision = false;
int snakelength = mSnakeTrail.size();
for (int index = 0; index < snakelength; index++) {
if (mSnakeTrail.get(index).equals(newCoord)) {
collision = true;
}
}
// if we're here and there's been no collision, then we have
// a good location for an apple. Otherwise, we'll circle back
// and try again
found = !collision;
}
if (newCoord == null) {
Log.e(TAG, "Somehow ended up with a null newCoord!");
}
mAppleList.add(newCoord);
}
/**
* Handles the basic update loop, checking to see if we are in the running
* state, determining if a move should be made, updating the snake's location.
*/
public void update() {
if (mMode == RUNNING) {
long now = System.currentTimeMillis();
if (now - mLastMove > mMoveDelay) {
clearTiles();
updateWalls();
updateSnake();
updateApples();
mLastMove = now;
}
mRedrawHandler.sleep(mMoveDelay);
}
}
/**
* Draws some walls.
*
*/
private void updateWalls() {
for (int x = 0; x < mXTileCount; x++) {
setTile(GREEN_STAR, x, 0);
setTile(GREEN_STAR, x, mYTileCount - 1);
}
for (int y = 1; y < mYTileCount - 1; y++) {
setTile(GREEN_STAR, 0, y);
setTile(GREEN_STAR, mXTileCount - 1, y);
}
}
/**
* Draws some apples.
*
*/
private void updateApples() {
for (Coordinate c : mAppleList) {
setTile(YELLOW_STAR, c.x, c.y);
}
}
/**
* Figure out which way the snake is going, see if he's run into anything (the
* walls, himself, or an apple). If he's not going to die, we then add to the
* front and subtract from the rear in order to simulate motion. If we want to
* grow him, we don't subtract from the rear.
*
*/
private void updateSnake() {
boolean growSnake = false;
// grab the snake by the head
Coordinate head = mSnakeTrail.get(0);
Coordinate newHead = new Coordinate(1, 1);
mDirection = mNextDirection;
switch (mDirection) {
case EAST: {
newHead = new Coordinate(head.x + 1, head.y);
break;
}
case WEST: {
newHead = new Coordinate(head.x - 1, head.y);
break;
}
case NORTH: {
newHead = new Coordinate(head.x, head.y - 1);
break;
}
case SOUTH: {
newHead = new Coordinate(head.x, head.y + 1);
break;
}
}
// Collision detection
// For now we have a 1-square wall around the entire arena
if ((newHead.x < 1) || (newHead.y < 1) || (newHead.x > mXTileCount - 2)
|| (newHead.y > mYTileCount - 2)) {
setMode(LOSE);
return;
}
// Look for collisions with itself
int snakelength = mSnakeTrail.size();
for (int snakeindex = 0; snakeindex < snakelength; snakeindex++) {
Coordinate c = mSnakeTrail.get(snakeindex);
if (c.equals(newHead)) {
setMode(LOSE);
return;
}
}
// Look for apples
int applecount = mAppleList.size();
for (int appleindex = 0; appleindex < applecount; appleindex++) {
Coordinate c = mAppleList.get(appleindex);
if (c.equals(newHead)) {
mAppleList.remove(c);
addRandomApple();
mScore++;
mMoveDelay *= 0.9;
growSnake = true;
}
}
// push a new head onto the ArrayList and pull off the tail
mSnakeTrail.add(0, newHead);
// except if we want the snake to grow
if (!growSnake) {
mSnakeTrail.remove(mSnakeTrail.size() - 1);
}
int index = 0;
for (Coordinate c : mSnakeTrail) {
if (index == 0) {
setTile(YELLOW_STAR, c.x, c.y);
} else {
setTile(RED_STAR, c.x, c.y);
}
index++;
}
}
/**
* Simple class containing two integer values and a comparison function.
* There's probably something I should use instead, but this was quick and
* easy to build.
*
*/
private class Coordinate {
public int x;
public int y;
public Coordinate(int newX, int newY) {
x = newX;
y = newY;
}
public boolean equals(Coordinate other) {
if (x == other.x && y == other.y) {
return true;
}
return false;
}
#Override
public String toString() {
return "Coordinate: [" + x + "," + y + "]";
}
}
}
SoundPlayer.java
package com.example.android.snake;
import android.content.Context;
import android.media.AudioManager;
import android.media.SoundPool;
public class SoundPlayer {
// U01A1: variables for SoundPool
private SoundPool soundPool; //var for soundPool
private int sound1, sound2, sound3; //var for sound files, are int because ids are ints
public SoundPlayer(Context context) {
//SoundPool (int maxStreams, int streamType, int srcQuality)
soundPool = new SoundPool (3, AudioManager.STREAM_MUSIC, 0);
//loading the sounds; priority set to 1
sound1 = soundPool.load(context, R.raw.sound1, 1);
sound2 = soundPool.load(context, R.raw.sound2, 1);
sound3 = soundPool.load(context, R.raw.sound3, 1);
}
//method to play sound1
public void playMoveSound() {
// play(int soundID, float leftVolume, float rightVolume, int priority, int loop, float rate)
soundPool.play(sound1, 1,1,0,0,1);
}
//method to play sound2
public void playEatSound() {
soundPool.play(sound2, 1,1,0,0,1);
}
//method to play sound3
public void playHitsound() {
soundPool.play(sound3, 1,1,0,0,1);
}
}
I'm trying to make a Conway's game of life game in java swing, and I need to operate a game loop in SwingWorker, and end the thread when The user decides to stop the loop from running. However, when I run the program, The UI freezes up, and I am forced to force quit the program. Could someone tell me what I'm doing wrong? Here is my code.
import java.awt.*;
import java.awt.Canvas;
import javax.swing.*;
import java.*;
import java.awt.event.*;
import java.util.*;
import java.awt.image.BufferedImage;
import javax.swing.Timer;
// use swing timer for thread stuff
/**
* Write a description of class GameLife here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class GameLife extends JPanel
{
// instance variables - replace the example below with your own
private int x;
private static final Dimension screen = Toolkit.getDefaultToolkit().getScreenSize();
private static final int w = (int)screen.getWidth();
private static final int h = (int)screen.getHeight();
private Color co1 = new Color(221,62,76);
private Color co2 = new Color(0,200,235);
private Color co3 = new Color(229,126,215);
private int square = 20;
private boolean flag = false;
private double mousex;
private double mousey;
private ArrayList<ArrayList<Integer>> selected;
private boolean running = false;
private GameWorker game;
/**
* Constructor for objects of class GameLife
*/
public GameLife()
{
game = new GameWorker();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent me) {
if(!running)
{
flag = true;
mousex = me.getX();
mousey = me.getY();
repaint();
}
}
});
addKeyListener(new KeyAdapter() {
#Override
public void keyPressed(KeyEvent e) {
int id = e.getID();
int keyCode = e.getKeyCode();
char space = ' ';
if(id == KeyEvent.KEY_PRESSED)
{
if(keyCode == KeyEvent.VK_SPACE)
{
running = !running;
if(running)
{
game.doInBackground();
}
else
{
game.cancel(true);
}
}
if(keyCode == KeyEvent.VK_Q)
{
square+=5;
repaint();
}
if(keyCode == KeyEvent.VK_E)
{
square-=5;
repaint();
}
}
}
});
setFocusable(true);
selected = new ArrayList<ArrayList<Integer>>();
mousex = 0;
mousey = 0;
}
/**
* An example of a method - replace this comment with your own
*
* #param y a sample parameter for a method
* #return the sum of x and y
*/
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setColor(co1);
g2.setStroke(new BasicStroke(2));
for(int i = 0; i < square;i++)
{
for(int j = 0; j< square; j++)
{
g2.setColor(co1);
g2.drawRect((w/square) * j, (h/square) * i,(w/square),(h/square));
if(!(selected.contains(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square))))))
{
g2.setColor(co2);
g2.fillRect((w/square) * j,(h/square) * i,(w/square),(h/square));
}
else
{
g2.setColor(co3);
g2.fillRect((w/square) * j,(h/square) * i,(w/square),(h/square));
}
}
}
if(flag)
{
draw(g2, mousex, mousey);
flag = false;
}
}
public void draw(Graphics2D g, double x , double y)
{
int mx = (int) x;
int my = (int) y;
GameLife aiden = new GameLife();
for(int i = 0; i < square;i++)
{
for(int j = 0; j< square; j++)
{
if( x > (w/square) * j && x < (w/square) * j + (w/square) && y > (h/square) * i && y < (h/square) * i + (h/square) && !(selected.contains(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square))))))
{
selected.add(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square))));
}
else if( x > (w/square) * j && x < (w/square) * j + (w/square) && y > (h/square) * i && y < (h/square) * i + (h/square) && selected.contains(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square)))))
{
selected.remove(new ArrayList<Integer>(Arrays.asList((w/square) * j,(h/square) * i,(w/square),(h/square))));
}
}
}
repaint();
}
public static void main()
{
JFrame frame = new JFrame("Conley's Game");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
GameLife canvas = new GameLife();
frame.getContentPane().add(canvas);
canvas.setSize(w,h);
frame.pack();
frame.setVisible(true);
}
}
Here is my SwingWorker Class
import java.util.*;
import javax.swing.*;
/**
* Write a description of class GameWorker here.
*
* #author (your name)
* #version (a version number or a date)
*/
public class GameWorker extends SwingWorker<Void,Void>
{
/**
* Constructor for objects of class GameWorker
*/
public GameWorker()
{
}
public Void doInBackground()
{
while(true)
{
System.out.println("hi");
try{
Thread.sleep(100);
}
catch(InterruptedException e)
{
return null;
}
}
}
}
I am trying to take screeshots of any file open currently on screen. After google all i got is to take screenshot of scene only in javafx. I don't want to use any AWT or Swing component. So is there any way ?
Currently javafx.scene.robot.Robot is available, which can take screen captures. I don’t know if it was when the question was originally asked. The method is:
javafx.scene.robot.Robot robot = new Robot();
WritableImage imgReturn = robot.getScreenCapture(imgOut, new Rectangle2D());
The optional rectangle specifies the region of the screen to capture. If not supplied, the whole screen is included. imgReturn can be assigned the resulting writable image. imgOut is an writable image output parameter that can also store the result, if you’ve defined it previously and it’s the correct dimensions. Otherwise, leave it null.
The method must be run on the JavaFX Application thread (you can use Platform.runLater() to ensure this if it’s invoked from another thread).
If you’re looking to take a screenshot of a specific application window, you could analyze the capture of the whole screen and look for the region containing the window you’re looking for.
If you know the window coordinates and dimensions, you can plug that in for the second argument in getScreenCapture.
You can also use the semitransparent overlay stage method from #GOXR3PLUS in which the user can define a rectangle (by clicking and dragging to define upper-left and lower-right corners, for example). Then you can hide the overlay stage and take a screenshot of the region within that rectangle.
This is the main mechanism I have used: A transparent Stage with Transparent Canvas which has a BorderPane with Opacity 0.1
Here is the simple example (it is just for selecting areas...):
WHEN YOU START THE APP THE ONLY WAY TO CLOSE IT IS USING ESCAPE
Tester class:
import javafx.application.Application;
import javafx.stage.Screen;
import javafx.stage.Stage;
public class Tester extends Application{
#Override
public void start(Stage primaryStage) throws Exception {
CaptureWindow window = new CaptureWindow(Screen.getPrimary().getBounds().getWidth(),Screen.getPrimary().getBounds().getHeight(), primaryStage);
window.show();
primaryStage.show();
}
public static void main(String[] args) {
launch(args);
}
}
Capture Window class:
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
/**
* Is used to capture an area of the screen.
*
* #author GOXR3PLUS
*/
public class CaptureWindow extends Stage {
/** The border pane. */
// BorderPane and Canvas
BorderPane borderPane = new BorderPane();
/** The canvas. */
Canvas canvas = new Canvas();
/** The gc. */
GraphicsContext gc = canvas.getGraphicsContext2D();
/** The stage. */
Stage stage;
/** The width. */
// Variables
int width;
/** The height. */
int height;
/** The x pressed. */
int xPressed = 0;
/** The y pressed. */
int yPressed = 0;
/** The x now. */
int xNow = 0;
/** The y now. */
int yNow = 0;
/** The foreground. */
Color foreground = Color.rgb(255, 167, 0);
/** The background. */
Color background = Color.rgb(0, 0, 0, 0.3);
/**
* Constructor.
*
* #param screenWidth the screen width
* #param screenHeight the screen height
* #param primary the primary
*/
public CaptureWindow(double screenWidth, double screenHeight, Stage primary) {
stage = primary;
setX(0);
setY(0);
setWidth(screenWidth);
setHeight(screenHeight);
initOwner(primary);
initStyle(StageStyle.TRANSPARENT);
setAlwaysOnTop(true);
// BorderPane
borderPane.setStyle("-fx-background-color:rgb(0,0,0,0.1);");
// Canvas
canvas.setWidth(screenWidth);
canvas.setHeight(screenHeight);
canvas.setOnMousePressed(m -> {
xPressed = (int) m.getScreenX();
yPressed = (int) m.getScreenY();
});
canvas.setOnMouseDragged(m -> {
xNow = (int) m.getScreenX();
yNow = (int) m.getScreenY();
repaintCanvas();
});
borderPane.setCenter(canvas);
// Scene
setScene(new Scene(borderPane, Color.TRANSPARENT));
getScene().setCursor(Cursor.CROSSHAIR);
getScene().setOnKeyReleased(key -> {
if (key.getCode() == KeyCode.B) {
close();
System.out.println("Key Released....");
}else if(key.getCode() == KeyCode.ESCAPE)
close();
});
// gc
gc.setLineDashes(6);
gc.setFont(Font.font("null", FontWeight.BOLD, 14));
}
/**
* Repaints the canvas *.
*/
protected void repaintCanvas() {
gc.clearRect(0, 0, getWidth(), getHeight());
gc.setStroke(foreground);
gc.setFill(background);
gc.setLineWidth(3);
if (xNow > xPressed && yNow > yPressed) { // Right and Down
calculateWidthAndHeight(xNow - xPressed, yNow - yPressed);
gc.strokeRect(xPressed, yPressed, width, height);
gc.fillRect(xPressed, yPressed, width, height);
} else if (xNow < xPressed && yNow < yPressed) { // Left and Up
calculateWidthAndHeight(xPressed - xNow, yPressed - yNow);
gc.strokeRect(xNow, yNow, width, height);
gc.fillRect(xNow, yNow, width, height);
} else if (xNow > xPressed && yNow < yPressed) { // Right and Up
calculateWidthAndHeight(xNow - xPressed, yPressed - yNow);
gc.strokeRect(xPressed, yNow, width, height);
gc.fillRect(xPressed, yNow, width, height);
} else if (xNow < xPressed && yNow > yPressed) { // Left and Down
calculateWidthAndHeight(xPressed - xNow, yNow - yPressed);
gc.strokeRect(xNow, yPressed, width, height);
gc.fillRect(xNow, yPressed, width, height);
}
}
/**
* Show the window.
*/
public void showWindow() {
xNow = 0;
yNow = 0;
xPressed = 0;
yPressed = 0;
repaintCanvas();
show();
}
/**
* Calculates the width and height of the rectangle.
*
* #param w the w
* #param h the h
*/
private final void calculateWidthAndHeight(int w, int h) {
width = w;
height = h;
}
/**
* Selects whole Screen.
*/
public void selectWholeScreen() {
xPressed = 0;
yPressed = 0;
xNow = (int) getWidth();
yNow = (int) getHeight();
}
/**
* Return an array witch contains the (UPPER_LEFT) Point2D of the rectangle
* and the width and height of the rectangle.
*
* #return the int[]
*/
public int[] calculatedRectangle() {
if (xNow > xPressed) { // Right
if (yNow > yPressed) // and DOWN
return new int[] { xPressed, yPressed, xNow - xPressed, yNow - yPressed };
else if (yNow < yPressed) // and UP
return new int[] { xPressed, yNow, xNow - xPressed, yPressed - yNow };
} else if (xNow < xPressed) { // LEFT
if (yNow > yPressed) // and DOWN
return new int[] { xNow, yPressed, xPressed - xNow, yNow - yPressed };
else if (yNow < yPressed) // and UP
return new int[] { xNow, yNow, xPressed - xNow, yPressed - yNow };
}
return new int[] { xPressed, yPressed, xNow, yNow };
}
Here is a full advanced example.It is part of a GitHub Project Here . You can clone the project and modify on your needs.
import java.awt.AWTException;
import java.awt.Rectangle;
import java.awt.Robot;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.concurrent.CountDownLatch;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.imageio.ImageIO;
import org.controlsfx.control.Notifications;
import application.Main;
import application.SFileChooser;
import javafx.animation.AnimationTimer;
import javafx.application.Platform;
import javafx.concurrent.Service;
import javafx.concurrent.Task;
import javafx.fxml.FXML;
import javafx.scene.Cursor;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.image.Image;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseButton;
import javafx.scene.layout.StackPane;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
/**
* This is the Window which is used from the user to draw the rectangle
* representing an area on the screen to be captured.
*
* #author GOXR3PLUS
*/
public class CaptureWindowController extends Stage {
/** The stack pane. */
#FXML
private StackPane stackPane;
/** The main canvas. */
#FXML
private Canvas mainCanvas;
// -----------------------------
/**
* The Model of the CaptureWindow
*/
CaptureWindowModel model = new CaptureWindowModel();
/** The file saver. */
SFileChooser fileSaver = new SFileChooser();
/** The capture service. */
final CaptureService captureService = new CaptureService();
/** The graphics context of the canvas */
GraphicsContext gc;
/**
* When a key is being pressed into the capture window then this Animation
* Timer is doing it's magic.
*/
AnimationTimer yPressedAnimation = new AnimationTimer() {
private long nextSecond = 0L;
// private static final long ONE_SECOND_NANOS = 1_000_000_000L
private long precisionLevel;
#Override
public void start() {
nextSecond = 0L;
precisionLevel = (long) ( settingsWindowController.getPrecisionSlider().getValue() * 1_000_000L );
super.start();
}
#Override
public void handle(long nanos) {
System.out.println("TimeStamp: " + nanos + " Current: " + nextSecond);
System.out.println("Milliseconds Delay: " + precisionLevel / 1_000_000);
if (nanos >= nextSecond) {
nextSecond = nanos + precisionLevel;
// With special key pressed
// (we want [LEFT] and [DOWN] side of the rectangle to be
// movable)
// No Special Key is Pressed
// (we want [RIGHT] and [UP] side of the rectangle to be
// movable)
// ------------------------------
if (model.rightPressed.get()) {
if (model.shiftPressed.get()) { // Special Key?
if (model.mouseXNow > model.mouseXPressed) { // Mouse gone Right?
model.mouseXPressed += 1;
} else {
model.mouseXNow += 1;
}
} else {
if (model.mouseXNow > model.mouseXPressed) { // Mouse gone Right?
model.mouseXNow += 1;
} else {
model.mouseXPressed += 1;
}
}
}
if (model.leftPressed.get()) {
if (model.shiftPressed.get()) { // Special Key?
if (model.mouseXNow > model.mouseXPressed) { // Mouse gone Right?
model.mouseXPressed -= 1;
} else {
model.mouseXNow -= 1;
}
} else {
if (model.mouseXNow > model.mouseXPressed) { // Mouse gone Right?
model.mouseXNow -= 1;
} else {
model.mouseXPressed -= 1;
}
}
}
if (model.upPressed.get()) {
if (model.shiftPressed.get()) { // Special Key?
if (model.mouseYNow > model.mouseYPressed) { // Mouse gone UP?
model.mouseYNow -= 1;
} else {
model.mouseYPressed -= 1;
}
} else {
if (model.mouseYNow > model.mouseYPressed) { // Mouse gone UP?
model.mouseYPressed -= 1;
} else {
model.mouseYNow -= 1;
}
}
}
if (model.downPressed.get()) {
if (model.shiftPressed.get()) { // Special Key?
if (model.mouseYNow > model.mouseYPressed) { // Mouse gone UP?
model.mouseYNow += 1;
} else {
model.mouseYPressed += 1;
}
} else {
if (model.mouseYNow > model.mouseYPressed) { // Mouse gone UP?
model.mouseYPressed += 1;
} else {
model.mouseYNow += 1;
}
}
}
repaintCanvas();
}
}
};
/**
* This AnimationTimer waits until the canvas is cleared before it can
* capture the screen.
*/
AnimationTimer waitFrameRender = new AnimationTimer() {
private int frameCount = 0;
#Override
public void start() {
frameCount = 0;
super.start();
}
#Override
public void handle(long timestamp) {
frameCount++;
if (frameCount >= 5) {
stop();
// Capture the Image
BufferedImage image;
int[] rect = getRectangleBounds();
try {
image = new Robot().createScreenCapture(new Rectangle(rect[0], rect[1], rect[2], rect[3]));
} catch (AWTException ex) {
Logger.getLogger(getClass().getName()).log(Level.INFO, null, ex);
return;
} finally {
mainCanvas.setDisable(false);
}
// System.out.println("Starting Service")
// Start the Service
captureService.startService(image);
}
}
};
/** The counting thread. */
Thread countingThread;
/** The main window controller. */
MainWindowController mainWindowController;
/** The settings window controller. */
SettingsWindowController settingsWindowController;
/**
* Constructor.
*/
public CaptureWindowController() {
setX(0);
setY(0);
getIcons().add(new Image(getClass().getResourceAsStream("/image/icon.png")));
initStyle(StageStyle.TRANSPARENT);
setAlwaysOnTop(true);
}
/**
* Add the needed references from the other controllers.
*
* #param mainWindowController the main window controller
* #param settingsWindowController the settings window controller
*/
#SuppressWarnings("hiding")
public void addControllerReferences(MainWindowController mainWindowController ,
SettingsWindowController settingsWindowController) {
this.mainWindowController = mainWindowController;
this.settingsWindowController = settingsWindowController;
}
/**
* Will be called as soon as FXML file is loaded.
*/
#FXML
public void initialize() {
// System.out.println("CaptureWindow initialized")
// Scene
Scene scene = new Scene(stackPane, model.screenWidth, model.screenHeight, Color.TRANSPARENT);
scene.setCursor(Cursor.NONE);
setScene(scene);
addKeyHandlers();
// Canvas
mainCanvas.setWidth(model.screenWidth);
mainCanvas.setHeight(model.screenHeight);
mainCanvas.setOnMousePressed(m -> {
if (m.getButton() == MouseButton.PRIMARY) {
model.mouseXPressed = (int) m.getScreenX();
model.mouseYPressed = (int) m.getScreenY();
}
});
mainCanvas.setOnMouseDragged(m -> {
if (m.getButton() == MouseButton.PRIMARY) {
model.mouseXNow = (int) m.getScreenX();
model.mouseYNow = (int) m.getScreenY();
repaintCanvas();
}
});
// graphics context 2D
gc = mainCanvas.getGraphicsContext2D();
gc.setLineDashes(6);
gc.setFont(Font.font("null", FontWeight.BOLD, 14));
// HideFeaturesPressed
model.hideExtraFeatures.addListener((observable , oldValue , newValue) -> repaintCanvas());
}
/**
* Adds the KeyHandlers to the Scene.
*/
private void addKeyHandlers() {
// -------------Read the below to understand the Code-------------------
// the default prototype of the below code is
// 1->when the user is pressing RIGHT ARROW -> The rectangle is
// increasing from the RIGHT side
// 2->when the user is pressing LEFT ARROW -> The rectangle is
// decreasing from the RIGHT side
// 3->when the user is pressing UP ARROW -> The rectangle is increasing
// from the UP side
// 4->when the user is pressing DOWN ARROW -> The rectangle is
// decreasing from the UP side
// when ->LEFT KEY <- is pressed
// 1->when the user is pressing RIGHT ARROW -> The rectangle is
// increasing from the LEFT side
// 2->when the user is pressing LEFT ARROW -> The rectangle is
// decreasing from the LEFT side
// 3->when the user is pressing UP ARROW -> The rectangle is increasing
// from the DOWN side
// 4->when the user is pressing DOWN ARROW -> The rectangle is
// decreasing from the DOWN side
// kemodel.yPressed
getScene().setOnKeyPressed(key -> {
if (key.isShiftDown())
model.shiftPressed.set(true);
if (key.getCode() == KeyCode.LEFT)
model.leftPressed.set(true);
if (key.getCode() == KeyCode.RIGHT)
model.rightPressed.set(true);
if (key.getCode() == KeyCode.UP)
model.upPressed.set(true);
if (key.getCode() == KeyCode.DOWN)
model.downPressed.set(true);
if (key.getCode() == KeyCode.H)
model.hideExtraFeatures.set(true);
});
// keyReleased
getScene().setOnKeyReleased(key -> {
if (key.getCode() == KeyCode.SHIFT)
model.shiftPressed.set(false);
if (key.getCode() == KeyCode.RIGHT) {
if (key.isControlDown()) {
model.mouseXNow = (int) getWidth();
repaintCanvas();
}
model.rightPressed.set(false);
}
if (key.getCode() == KeyCode.LEFT) {
if (key.isControlDown()) {
model.mouseXPressed = 0;
repaintCanvas();
}
model.leftPressed.set(false);
}
if (key.getCode() == KeyCode.UP) {
if (key.isControlDown()) {
model.mouseYPressed = 0;
repaintCanvas();
}
model.upPressed.set(false);
}
if (key.getCode() == KeyCode.DOWN) {
if (key.isControlDown()) {
model.mouseYNow = (int) getHeight();
repaintCanvas();
}
model.downPressed.set(false);
}
if (key.getCode() == KeyCode.A && key.isControlDown())
selectWholeScreen();
if (key.getCode() == KeyCode.H)
model.hideExtraFeatures.set(false);
if (key.getCode() == KeyCode.ESCAPE || key.getCode() == KeyCode.BACK_SPACE) {
// Stop Counting Thread
if (countingThread != null)
countingThread.interrupt();
// Stop MaryTTS
Main.textToSpeech.stopSpeaking();
// Deactivate all keys
deActivateAllKeys();
// show the appropriate windows
Main.stage.show();
close();
} else if (key.getCode() == KeyCode.ENTER || key.getCode() == KeyCode.SPACE) {
// Stop MaryTTS
Main.textToSpeech.stopSpeaking();
// Deactivate all keys
deActivateAllKeys();
// Capture Selected Area
prepareImage();
}
});
model.anyPressed.addListener((obs , wasPressed , isNowPressed) ->
{
if (isNowPressed.booleanValue()) {
yPressedAnimation.start();
} else {
yPressedAnimation.stop();
}
});
}
/**
* Deactivates the keys contained into this method.
*/
private void deActivateAllKeys() {
model.shiftPressed.set(false);
model.upPressed.set(false);
model.rightPressed.set(false);
model.downPressed.set(false);
model.leftPressed.set(false);
model.hideExtraFeatures.set(false);
}
/**
* Creates and saves the image.
*/
public void prepareImage() {
// return if it is alive
if ( ( countingThread != null && countingThread.isAlive() ) || captureService.isRunning())
return;
countingThread = new Thread(() -> {
mainCanvas.setDisable(true);
boolean interrupted = false;
// CountDown
if (!mainWindowController.getTimeSlider().isDisabled()) {
for (int i = (int) mainWindowController.getTimeSlider().getValue(); i > 0; i--) {
final int a = i;
// Lock until it has been refreshed from JavaFX
// Application Thread
CountDownLatch count = new CountDownLatch(1);
// Repaint the Canvas
Platform.runLater(() -> {
gc.clearRect(0, 0, getWidth(), getHeight());
gc.setFill(model.background);
gc.fillRect(0, 0, getWidth(), getHeight());
gc.setFill(Color.BLACK);
gc.fillOval(getWidth() / 2 - 90, getHeight() / 2 - 165, 250, 250);
gc.setFill(Color.WHITE);
gc.setFont(Font.font("", FontWeight.BOLD, 120));
gc.fillText(Integer.toString(a), getWidth() / 2, getHeight() / 2);
// Unlock the Parent Thread
count.countDown();
});
try {
// Wait JavaFX Application Thread
count.await();
// MaryTTS
if (settingsWindowController.getMarryTTSToggle().isSelected())
Main.textToSpeech.speak(i);
// Sleep 1 seconds after that
Thread.sleep(980);
} catch (InterruptedException ex) {
interrupted = true;
mainCanvas.setDisable(false);
countingThread.interrupt();
Logger.getLogger(getClass().getName()).log(Level.INFO, null, ex);
break;
}
}
}
// !interrupted?
if (!Thread.interrupted()) {
// MaryTTS
if (settingsWindowController.getMarryTTSToggle().isSelected())
Main.textToSpeech.speak("Select where the image will be saved.");
Platform.runLater(() -> {
// Clear the canvas
gc.clearRect(0, 0, getWidth(), getHeight());
// Wait for frame Render
waitFrameRender.start();
});
} // !interrupted?
});
countingThread.setDaemon(true);
countingThread.start();
}
/**
* Repaint the canvas of the capture window.
*/
protected void repaintCanvas() {
gc.clearRect(0, 0, getWidth(), getHeight());
gc.setFont(model.font);
// draw the actual rectangle
gc.setStroke(Color.AQUA);
gc.setFill(model.background);
gc.setLineWidth(1);
// smart calculation of where the mouse has been dragged
model.rectWidth = ( model.mouseXNow > model.mouseXPressed ) ? model.mouseXNow - model.mouseXPressed // RIGHT
: model.mouseXPressed - model.mouseXNow // LEFT
;
model.rectHeight = ( model.mouseYNow > model.mouseYPressed ) ? model.mouseYNow - model.mouseYPressed // DOWN
: model.mouseYPressed - model.mouseYNow // UP
;
model.rectUpperLeftX = // -------->UPPER_LEFT_X
( model.mouseXNow > model.mouseXPressed ) ? model.mouseXPressed // RIGHT
: model.mouseXNow// LEFT
;
model.rectUpperLeftY = // -------->UPPER_LEFT_Y
( model.mouseYNow > model.mouseYPressed ) ? model.mouseYPressed // DOWN
: model.mouseYNow // UP
;
gc.strokeRect(model.rectUpperLeftX - 1.00, model.rectUpperLeftY - 1.00, model.rectWidth + 2.00, model.rectHeight + 2.00);
gc.fillRect(model.rectUpperLeftX, model.rectUpperLeftY, model.rectWidth, model.rectHeight);
// draw the circles
if (!model.hideExtraFeatures.getValue()) {
// Show the Size
double middle = model.rectUpperLeftX + model.rectWidth / 2.00;
gc.setLineWidth(1);
gc.setStroke(Color.AQUA);
gc.strokeRect(middle - 78, model.rectUpperLeftY < 25 ? model.rectUpperLeftY + 2 : model.rectUpperLeftY - 26.00, 79, 25);
gc.setFill(Color.rgb(0, 0, 00, 0.9));
gc.fillRect(middle - 77, model.rectUpperLeftY < 25 ? model.rectUpperLeftY + 2 : model.rectUpperLeftY - 25.00, 77, 23);
gc.setFill(Color.WHITE);
gc.fillText(model.rectWidth + "," + model.rectHeight, middle - 77 + 9,
model.rectUpperLeftY < 25 ? model.rectUpperLeftY + 17.00 : model.rectUpperLeftY - 6.00);
}
}
/**
* Selects whole Screen.
*/
private void selectWholeScreen() {
model.mouseXPressed = 0;
model.mouseYPressed = 0;
model.mouseXNow = (int) getWidth();
model.mouseYNow = (int) getHeight();
repaintCanvas();
}
/**
* Prepares the Window for the User.
*/
public void prepareForCapture() {
show();
repaintCanvas();
Main.stage.close();
settingsWindowController.close();
if (settingsWindowController.getMarryTTSToggle().isSelected())
Main.textToSpeech.speak("Select an area of the screen dragging your mouse and then press Enter or Space");
}
/**
* Return an array witch contains the (UPPER_LEFT) Point2D of the rectangle
* and the width and height of the rectangle.
*
* #return An array witch contains the (UPPER_LEFT) Point2D of the
* rectangle
* and the width and height of the rectangle
*/
public int[] getRectangleBounds() {
return new int[]{ model.rectUpperLeftX , model.rectUpperLeftY , model.rectWidth , model.rectHeight };
}
/**
* The work of the Service is to capture the Image based on the rectangle
* that user drawn of the Screen.
*
* #author GOXR3PLUS
*/
public class CaptureService extends Service<Boolean> {
/** The file path. */
String filePath;
/** The image. */
BufferedImage image;
/**
* Constructor.
*/
public CaptureService() {
setOnSucceeded(s -> done());
setOnCancelled(c -> done());
setOnFailed(f -> done());
}
/**
* Starts the Service.
*
* #param image2 The image to be saved.
*/
public void startService(BufferedImage image2) {
if (!isRunning()) {
this.image = image2;
// Show the SaveDialog
fileSaver.get().setInitialFileName("ScreenShot" + model.random.nextInt(50000));
File file = fileSaver.get().showSaveDialog(CaptureWindowController.this);
if (file != null) {
filePath = file.getAbsolutePath();
reset();
start();
} else
repaintCanvas();
}
}
/**
* Service has been done.
*/
private void done() {
Main.stage.show();
close();
if (getValue()) // successful?
Notifications.create().title("Successfull Capturing").text("Image is being saved at:\n" + filePath)
.showInformation();
else
Notifications.create().title("Error").text("Failed to capture the Screen!").showError();
}
/* (non-Javadoc)
* #see javafx.concurrent.Service#createTask() */
#Override
protected Task<Boolean> createTask() {
return new Task<Boolean>() {
#Override
protected Boolean call() throws Exception {
boolean written = false;
// Try to write the file to the disc
try {
written = ImageIO.write(image, fileSaver.get().getSelectedExtensionFilter().getDescription(),
new File(filePath));
} catch (IOException ex) {
Logger.getLogger(getClass().getName()).log(Level.WARNING, null, ex);
return written;
}
return written;
}
};
}
}
}
I am having trouble. I need to rotate an equilateral triangle around it's centre by using the drag listener and click listener. The triangle should grow but now change angles and be rotated by a point while being centred at the middle of the triangle. This is my problem, it is currently dragging by the point 3 and rotating around point 1. I have an array of values x and y and it stores 4 values each containing the initial point first at ordinal value 0 and point 1 2 and 3 at the corresponding values.
`
public class DrawTriangle extends JFrame {
enter code here
/** The Constant NUMBER_3. */
private static final int NUMBER_3 = 3;
/** The Constant EQUL_ANGLE. */
#SuppressWarnings("unused")
private static final double EQUL_ANGLE = 1;
/** The Constant TRIANGLE_POINTS. */
private static final int TRIANGLE_POINTS = 4;
/** The Constant _400. */
private static final int SIZE = 400;
/** The x points. */
private int [] xPoints = new int[TRIANGLE_POINTS];
/** The y points. */
private int [] yPoints = new int[TRIANGLE_POINTS];
private int xInitial;
private int yInitial;
/** The x. */
private double x = EQUL_ANGLE;
/** The new x. */
private double newX;
/** The new y. */
private double newY;
/**
* Instantiates a new draw triangle.
*/
public DrawTriangle() {
super("Dimitry Rakhlei");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(new DrawTrianglePanel());
setSize(SIZE, SIZE); // you can change this size but don't make it HUGE!
setVisible(true);
}
/**
* The Class DrawTrianglePanel.
*/
private class DrawTrianglePanel extends JPanel implements MouseListener,
MouseMotionListener {
/**
* Instantiates a new draw triangle panel.
*/
public DrawTrianglePanel() {
addMouseListener(this);
addMouseMotionListener(this);
}
/**
* Drawing the triangle.
*
* #param g
* the g
* #see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
public void paintComponent(Graphics g) {
super.paintComponent(g);
// DRAWING CODE HERE
g.drawPolygon(xPoints, yPoints, 3);
System.out.println("Paint called");
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mousePressed
* (java.awt.event.MouseEvent)
*/
public void mousePressed(MouseEvent e) {
System.out.println("Mouse pressed called");
e.getPoint();
xPoints[0] = e.getPoint().x;
yPoints[0] = e.getPoint().y;
repaint();
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mouseReleased
* (java.awt.event.MouseEvent)
*/
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse released called");
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseMotionListener#mouseDragged
* (java.awt.event.MouseEvent)
*/
public void mouseDragged(MouseEvent e) {
System.out.println("Mouse dragged called");
newX = e.getPoint().x;
newY = e.getPoint().y;
xPoints[1] = (int) newX;
yPoints[1] = (int) newY;
newX = xPoints[0] + (xPoints[1]-xPoints[0])*Math.cos(x) - (yPoints[1]-yPoints[0])*Math.sin(x);
newY = yPoints[0] + (xPoints[1]-xPoints[0])*Math.sin(x) + (yPoints[1]-yPoints[0])*Math.cos(x);
xPoints[2] = (int) newX;
yPoints[2] = (int) newY;
newX = xPoints[0] + (xPoints[1]-xPoints[0])*Math.cos(x) - (yPoints[1]-yPoints[0])*Math.sin(x);
newY = yPoints[0] + (xPoints[1]-xPoints[0])*Math.sin(x) + (yPoints[1]-yPoints[0])*Math.cos(x);
xPoints[3] = (int) newX;
yPoints[3] = (int) newY;
repaint();
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mouseEntered
* (java.awt.event.MouseEvent)
*/
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse Entered.");
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mouseExited
* (java.awt.event.MouseEvent)
*/
public void mouseExited(MouseEvent e) {
System.out.println("Mouse exited.");
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseListener#mouseClicked
* (java.awt.event.MouseEvent)
*/
public void mouseClicked(MouseEvent e) {
}
/**
* (non-Javadoc).
*
* #param e
* the e
* #see java.awt.event.MouseMotionListener#mouseMoved
* (java.awt.event.MouseEvent)
*/
public void mouseMoved(MouseEvent e) {
}
}
/**
* The main method.
*
* #param args
* the arguments
*/
public static void main(String[] args) {
new DrawTriangle();
}
};`
My issue is that this code basically runs correctly but I am told the vertex point of rotation has to be in the middle of the triangle. Mine is the first point.
Start by taking a look at 2D Graphics, in particular Transforming Shapes, Text, and Images.
Basically, your "polygon" will have a definable size (the maximum x/y point), from this, you can determine the center position of the "polygon", for example...
protected Dimension getTriangleSize() {
int maxX = 0;
int maxY = 0;
for (int index = 0; index < xPoints.length; index++) {
maxX = Math.max(maxX, xPoints[index]);
}
for (int index = 0; index < yPoints.length; index++) {
maxY = Math.max(maxY, yPoints[index]);
}
return new Dimension(maxX, maxY);
}
This just returns the maximum x and y bounds of your polygon. This allows you to calculate the center position of the polygon. You'll see why in a second why you don't need to actually specify the origin point...
Next, we calculate a AffineTransform, which is the applied to the Graphics context directly...
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform at = new AffineTransform();
Dimension size = getTriangleSize();
int x = clickPoint.x - (size.width / 2);
int y = clickPoint.y - (size.height / 2);
at.translate(x, y);
at.rotate(Math.toRadians(angle), clickPoint.x - x, clickPoint.y - y);
g2d.setTransform(at);
g2d.drawPolygon(xPoints, yPoints, 3);
// Guide
g2d.setColor(Color.RED);
g2d.drawLine(size.width / 2, 0, size.width / 2, size.height / 2);
g2d.dispose();
This not only translates the triangle position, but will also rotate it. What this means you can create a normalised polygon (whose origin point is 0x0) and allow the Graphics context to place it where you want it, this makes life SO much easier...
Now, the rotation calculation is based on calculating the angle between two points, the "click" point and the "drag" point...
angle = -Math.toDegrees(Math.atan2(e.getPoint().x - clickPoint.x, e.getPoint().y - clickPoint.y)) + 180;
Which is based on the solution in this question
For example...
The red line is simple a guide to show that the tip of the triangle is point towards the mouse...
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.AffineTransform;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class DrawTriangle extends JFrame {
/**
* The x points.
*/
private int[] xPoints = new int[]{0, 25, 50};
/**
* The y points.
*/
private int[] yPoints = new int[]{50, 0, 50};
double angle = 0f;
/**
* Instantiates a new draw triangle.
*/
public DrawTriangle() {
super("Dimitry Rakhlei");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setContentPane(new DrawTrianglePanel());
pack();
setLocationRelativeTo(null);
setVisible(true);
}
/**
* The Class DrawTrianglePanel.
*/
private class DrawTrianglePanel extends JPanel implements MouseListener,
MouseMotionListener {
private Point clickPoint;
/**
* Instantiates a new draw triangle panel.
*/
public DrawTrianglePanel() {
addMouseListener(this);
addMouseMotionListener(this);
clickPoint = new Point(100, 100);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
protected Dimension getTriangleSize() {
int maxX = 0;
int maxY = 0;
for (int index = 0; index < xPoints.length; index++) {
maxX = Math.max(maxX, xPoints[index]);
}
for (int index = 0; index < yPoints.length; index++) {
maxY = Math.max(maxY, yPoints[index]);
}
return new Dimension(maxX, maxY);
}
/**
* Drawing the triangle.
*
* #param g the g
* #see javax.swing.JComponent#paintComponent(java.awt.Graphics)
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
AffineTransform at = new AffineTransform();
Dimension size = getTriangleSize();
int x = clickPoint.x - (size.width / 2);
int y = clickPoint.y - (size.height / 2);
at.translate(x, y);
at.rotate(Math.toRadians(angle), clickPoint.x - x, clickPoint.y - y);
g2d.setTransform(at);
g2d.drawPolygon(xPoints, yPoints, 3);
// Guide
g2d.setColor(Color.RED);
g2d.drawLine(size.width / 2, 0, size.width / 2, size.height / 2);
g2d.dispose();
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mousePressed (java.awt.event.MouseEvent)
*/
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Mouse pressed called");
// clickPoint = e.getPoint();
repaint();
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mouseReleased (java.awt.event.MouseEvent)
*/
#Override
public void mouseReleased(MouseEvent e) {
System.out.println("Mouse released called");
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseMotionListener#mouseDragged (java.awt.event.MouseEvent)
*/
public void mouseDragged(MouseEvent e) {
System.out.println("Mouse dragged called");
angle = -Math.toDegrees(Math.atan2(e.getPoint().x - clickPoint.x, e.getPoint().y - clickPoint.y)) + 180;
repaint();
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mouseEntered (java.awt.event.MouseEvent)
*/
public void mouseEntered(MouseEvent e) {
System.out.println("Mouse Entered.");
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mouseExited (java.awt.event.MouseEvent)
*/
public void mouseExited(MouseEvent e) {
System.out.println("Mouse exited.");
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseListener#mouseClicked (java.awt.event.MouseEvent)
*/
public void mouseClicked(MouseEvent e) {
}
/**
* (non-Javadoc).
*
* #param e the e
* #see java.awt.event.MouseMotionListener#mouseMoved (java.awt.event.MouseEvent)
*/
public void mouseMoved(MouseEvent e) {
}
}
/**
* The main method.
*
* #param args the arguments
*/
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
new DrawTriangle();
}
});
}
}
Now, before you jump all over me and complain that the solution is "too complex", understand that I'm an idiot, seriously, my 2 year old has a better grasp on basic mathematics then I do, this is the most simplistic solution I can come up with that doesn't melt my brain and uses the dual array polygon API. Personally, I'd use the Shape API, but that's not what you started with...
I am trying to draw a curved Line arrow on a stacked bar graph.I have been able to draw the curved line and arrow.But i am not able to connect the arrow to the end of the curved line.I am using affine transformation to draw the curved line.The below link describes the curved line and arrow that i have been able to draw http://i58.tinypic.com/2m422hy.png.Can anyone guide me as to how to connect the arrow to the end of the curved line.
Here is the code
package Stack;
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/**
*
* #author OSPL-B4
/
/
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Paint;
import java.awt.Polygon;
import java.awt.Shape;
import java.awt.Stroke;
import java.awt.font.TextLayout;
import java.awt.geom.AffineTransform;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import java.awt.geom.QuadCurve2D;
import java.awt.geom.Rectangle2D;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import org.jfree.chart.annotations.CategoryAnnotation;
import org.jfree.chart.axis.CategoryAnchor;
import org.jfree.chart.axis.CategoryAxis;
import org.jfree.chart.axis.ValueAxis;
import org.jfree.chart.event.AnnotationChangeListener;
import org.jfree.chart.plot.CategoryPlot;
import org.jfree.chart.plot.Plot;
import org.jfree.chart.plot.PlotOrientation;
import org.jfree.data.category.CategoryDataset;
import org.jfree.io.SerialUtilities;
import org.jfree.ui.RectangleEdge;
import org.jfree.util.ObjectUtilities;
import org.jfree.util.PaintUtilities;
//import java.awt.Font;
/**
* A line annotation that can be placed on a
* {#link org.jfree.chart.plot.CategoryPlot}.
*/
public class CategoryLineAnnotation_demo1 implements CategoryAnnotation,
Cloneable, Serializable {
/** The category for the start of the line. */
private Comparable category1;
/** The value for the start of the line. */
private double value1;
/** The category for the end of the line. */
private Comparable category2;
/** The value for the end of the line. */
private double value2;
private final int ARR_SIZE = 4;
/** The line color. */
private transient Paint paint = Color.black;
/** The line stroke. */
private transient Stroke stroke = new BasicStroke(1.0f);
/**
* Creates a new annotation that draws a line between (category1, value1)
* and (category2, value2).
*
* #param category1 the category (<code>null</code> not permitted).
* #param value1 the value.
* #param category2 the category (<code>null</code> not permitted).
* #param value2 the value.
*/
public CategoryLineAnnotation_demo1(Comparable category1, double value1,
Comparable category2, double value2,
Paint paint, Stroke stroke) {
if (category1 == null) {
throw new IllegalArgumentException("Null 'category1' argument.");
}
if (category2 == null) {
throw new IllegalArgumentException("Null 'category2' argument.");
}
if (paint == null) {
throw new IllegalArgumentException("Null 'paint' argument.");
}
if (stroke == null) {
throw new IllegalArgumentException("Null 'stroke' argument.");
}
this.category1 = category1;
System.out.println("First Category value is "+category1);
this.value1 = value1;
this.category2 = category2;
System.out.println("Second Category value is "+category2);
this.value2 = value2;
this.paint = paint;
this.stroke = stroke;
}
/**
* Returns the category for the start of the line.
*
* #return The category for the start of the line (never <code>null</code>).
*/
public Comparable getCategory1() {
return this.category1;
}
/**
* Sets the category for the start of the line.
*
* #param category the category (<code>null</code> not permitted).
*/
public void setCategory1(Comparable category) {
if (category == null) {
throw new IllegalArgumentException("Null 'category' argument.");
}
this.category1 = category;
}
/**
* Returns the y-value for the start of the line.
*
* #return The y-value for the start of the line.
*/
public double getValue1() {
return this.value1;
}
/**
* Sets the y-value for the start of the line.
*
* #param value the value.
*/
public void setValue1(double value) {
this.value1 = value;
}
/**
* Returns the category for the end of the line.
*
* #return The category for the end of the line (never <code>null</code>).
*/
public Comparable getCategory2() {
return this.category2;
}
/**
* Sets the category for the end of the line.
*
* #param category the category (<code>null</code> not permitted).
*/
public void setCategory2(Comparable category) {
if (category == null) {
throw new IllegalArgumentException("Null 'category' argument.");
}
this.category2 = category;
}
/**
* Returns the y-value for the end of the line.
*
* #return The y-value for the end of the line.
*/
public double getValue2() {
return this.value2;
}
/**
* Sets the y-value for the end of the line.
*
* #param value the value.
*/
public void setValue2(double value) {
this.value2 = value;
}
/**
* Returns the paint used to draw the connecting line.
*
* #return The paint (never <code>null</code>).
*/
public Paint getPaint() {
return this.paint;
}
/**
* Sets the paint used to draw the connecting line.
*
* #param paint the paint (<code>null</code> not permitted).
*/
public void setPaint(Paint paint) {
if (paint == null) {
throw new IllegalArgumentException("Null 'paint' argument.");
}
this.paint = paint;
}
/**
* Returns the stroke used to draw the connecting line.
*
* #return The stroke (never <code>null</code>).
*/
public Stroke getStroke() {
// System.out.println("In Stacked bar Stroke is "+getStroke());
return this.stroke;
}
/**
* Sets the stroke used to draw the connecting line.
*
* #param stroke the stroke (<code>null</code> not permitted).
*/
public void setStroke(Stroke stroke) {
if (stroke == null) {
throw new IllegalArgumentException("Null 'stroke' argument.");
}
this.stroke = stroke;
}
/**
* Draws the annotation.
*
* #param g2 the graphics device.
* #param plot the plot.
* #param dataArea the data area.
* #param domainAxis the domain axis.
* #param rangeAxis the range axis.
*/
public void draw(Graphics2D g2, CategoryPlot plot, Rectangle2D dataArea,
CategoryAxis domainAxis, ValueAxis rangeAxis) {
CategoryDataset dataset = plot.getDataset();
int catIndex1 = dataset.getColumnIndex(this.category1);
int catIndex2 = dataset.getColumnIndex(this.category2);
int catCount = dataset.getColumnCount();
double lineX1 = 0.0f;
double lineY1 = 0.0f;
double lineX2 = 0.0f;
double lineY2 = 0.0f;
PlotOrientation orientation = plot.getOrientation();
RectangleEdge domainEdge = Plot.resolveDomainAxisLocation(
plot.getDomainAxisLocation(), orientation);
RectangleEdge rangeEdge = Plot.resolveRangeAxisLocation(
plot.getRangeAxisLocation(), orientation);
if (orientation == PlotOrientation.HORIZONTAL) {
lineY1 = domainAxis.getCategoryJava2DCoordinate(
CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea,
domainEdge);
lineX1 = rangeAxis.valueToJava2D(this.value1, dataArea, rangeEdge);
lineY2 = domainAxis.getCategoryJava2DCoordinate(
CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea,
domainEdge);
lineX2 = rangeAxis.valueToJava2D(this.value2, dataArea, rangeEdge);
}
else if (orientation == PlotOrientation.VERTICAL) {
lineX1 = domainAxis.getCategoryJava2DCoordinate(
CategoryAnchor.MIDDLE, catIndex1, catCount, dataArea,
domainEdge);
lineY1 = rangeAxis.valueToJava2D(this.value1, dataArea, rangeEdge);
lineX2 = domainAxis.getCategoryJava2DCoordinate(
CategoryAnchor.MIDDLE, catIndex2, catCount, dataArea,
domainEdge);
lineY2 = rangeAxis.valueToJava2D(this.value2, dataArea, rangeEdge);
}
g2.setPaint(this.paint);
g2.setStroke(this.stroke);
drawArrow(g2,(int) lineX1, (int) lineY1, (int) lineX2, (int) lineY2);
}
void drawArrow(Graphics g1, int x1, int y1, int x2, int y2) {
Graphics2D g = (Graphics2D) g1.create();
double dx = x2 - x1, dy = y2 - y1;
System.out.println("Value of DX "+dx);
System.out.println("Value of DY "+dy);
double angle = Math.atan2(dy, dx);
System.out.println("Getting angle "+angle);
int len = (int) Math.sqrt(dx*dx + dy*dy);
AffineTransform at = AffineTransform.getTranslateInstance(x1, y1);
at.concatenate(AffineTransform.getRotateInstance(angle));
g.transform(at);
System.out.println("Affine transform X co-ordinate value is "+at.getScaleX());
System.out.println("Affine transform Y co-ordinate value is "+at.getScaleY());
float center1=(x1+x2)/2-40;
float center2= (y1+y2)/2-40;
QuadCurve2D q=new QuadCurve2D.Float(0,0,center1,center2,x2,y2);
g.draw(q);
g.setColor(Color.RED);
System.out.println("Length of arrow is "+len);
System.out.println("Get Start point 2D "+q.getP1());
System.out.println("Get End point 2D "+q.getP2());
g.fillPolygon(new int[] {len, len-ARR_SIZE, len-ARR_SIZE-10, len-60},
new int[] {0, -ARR_SIZE, ARR_SIZE-20, 5}, 4);
}
public void paintComponent(Graphics g) {
for (int x = 15; x < 200; x += 16)
drawArrow(g, x, x, x, 150);
drawArrow(g, 30, 300, 300, 190);
}
/**
* Tests this object for equality with another.
*
* #param obj the object (<code>null</code> permitted).
*
* #return <code>true</code> or <code>false</code>.
*/
public boolean equals(Object obj) {
if (obj == this) {
return true;
}
if (!(obj instanceof CategoryLineAnnotation_demo1)) {
return false;
}
CategoryLineAnnotation_demo1 that = (CategoryLineAnnotation_demo1) obj;
if (!this.category1.equals(that.getCategory1())) {
return false;
}
if (this.value1 != that.getValue1()) {
return false;
}
if (!this.category2.equals(that.getCategory2())) {
return false;
}
if (this.value2 != that.getValue2()) {
return false;
}
if (!PaintUtilities.equal(this.paint, that.paint)) {
return false;
}
if (!ObjectUtilities.equal(this.stroke, that.stroke)) {
return false;
}
return true;
}
/**
* Returns a hash code for this instance.
*
* #return A hash code.
*/
public int hashCode() {
// TODO: this needs work
return this.category1.hashCode() + this.category2.hashCode();
}
/**
* Returns a clone of the annotation.
*
* #return A clone.
*
* #throws CloneNotSupportedException this class will not throw this
* exception, but subclasses (if any) might.
*/
public Object clone() throws CloneNotSupportedException {
return super.clone();
}
/**
* Provides serialization support.
*
* #param stream the output stream.
*
* #throws IOException if there is an I/O error.
*/
private void writeObject(ObjectOutputStream stream) throws IOException {
stream.defaultWriteObject();
SerialUtilities.writePaint(this.paint, stream);
SerialUtilities.writeStroke(this.stroke, stream);
}
/**
* Provides serialization support.
*
* #param stream the input stream.
*
* #throws IOException if there is an I/O error.
* #throws ClassNotFoundException if there is a classpath problem.
*/
private void readObject(ObjectInputStream stream)
throws IOException, ClassNotFoundException {
stream.defaultReadObject();
this.paint = SerialUtilities.readPaint(stream);
this.stroke = SerialUtilities.readStroke(stream);
}
#Override
public void addChangeListener(AnnotationChangeListener al) {
}
#Override
public void removeChangeListener(AnnotationChangeListener al) {
}
}
You can create the arrow head based on the last line segment (which might already be transformed using an AffineTransform)
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Shape;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Path2D;
import java.awt.geom.Point2D;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class ArrowPainter
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JPanel panel = new ArrowPaintPanel();
f.getContentPane().add(panel);
f.setSize(500,500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
class ArrowPaintPanel extends JPanel implements MouseMotionListener
{
private Point2D startPoint = null;
private Point2D endPoint = null;
ArrowPaintPanel()
{
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
if (startPoint == null)
{
startPoint = new Point(getWidth()/2, getHeight()/2);
}
if (endPoint == null)
{
return;
}
Line2D line = new Line2D.Double(startPoint, endPoint);
Shape arrowHead = createArrowHead(line, 30, 20);
g.draw(line);
g.fill(arrowHead);
}
#Override
public void mouseDragged(MouseEvent e)
{
endPoint = e.getPoint();
repaint();
}
#Override
public void mouseMoved(MouseEvent e)
{
endPoint = e.getPoint();
repaint();
}
private static Shape createArrowHead(Line2D line, double length, double width)
{
Point2D p0 = line.getP1();
Point2D p1 = line.getP2();
double x0 = p0.getX();
double y0 = p0.getY();
double x1 = p1.getX();
double y1 = p1.getY();
double dx = x1 - x0;
double dy = y1 - y0;
double invLength = 1.0 / Math.sqrt(dx*dx+dy*dy);
double dirX = dx * invLength;
double dirY = dy * invLength;
double ax = x1 - length * dirX;
double ay = y1 - length * dirY;
double offsetX = width * -dirY * 0.5;
double offsetY = width * dirX * 0.5;
double c0x = ax + offsetX;
double c0y = ay + offsetY;
double c1x = ax - offsetX;
double c1y = ay - offsetY;
Path2D arrowHead = new Path2D.Double();
arrowHead.moveTo(x1, y1);
arrowHead.lineTo(c0x, c0y);
arrowHead.lineTo(c1x, c1y);
arrowHead.closePath();
return arrowHead;
}
}
EDIT: Update for the above EDIT and the comments: That's a lot of code, but still nothing that can be tested easily. What happens when you replace your line
drawArrow(g2,(int) lineX1, (int) lineY1, (int) lineX2, (int) lineY2);
with
g.fill(createArrowHead(new Line2D.Double(lineX1, lineY1, lineX2, lineY2), 30, 20));
?