So I've got this challenge to make one of those spirograph drawrings in an applet. The problem is, I'm new to Java and have absolutely no idea how to get any components appearing on the screen. I'm hoping someone can just add an extra line of code to fix this, though I'm really grateful for any answers :)
import javax.swing.JApplet;
import javax.swing.JOptionPane;
import javax.swing.JTextField;
import java.awt.BorderLayout;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.awt.Dimension;
import java.awt.Toolkit;
public class SpaceCadets3_WB extends JApplet {
int numOfPoints = 1080;
int[] x = new int[numOfPoints];
int[] y = new int[numOfPoints];
int x1, x2, y1, y2, width, height, animationSleep = 0;
int R = 75;
int r = 10;
int O = 75;
JTextField changeNumOfPoints = new JTextField(15);
public SpaceCadets3_WB() {
}
public void start() {
this.setSize(600, 600);
this.setBackground(new Color(100,100,255));
this.getContentPane().setLayout(null);
this.getContentPane().add(changeNumOfPoints);
changeNumOfPoints.setVisible(true);
changeNumOfPoints.setLocation(width - changeNumOfPoints.getSize().width - 25, 25);
}
public void calculatePoints(){
width = SpaceCadets3_WB.this.getWidth();
height = SpaceCadets3_WB.this.getHeight();
for(int t = 0; t<numOfPoints; t++){
x[t] = (int) ((R+r)*Math.cos(t) - (r+O)*Math.cos(((R+r)/r)*t) + width/2);
y[t] = (int) ((R+r)*Math.sin(t) - (r+O)*Math.sin(((R+r)/r)*t) + height/2);
}
}
public void paint(Graphics g){
g.setColor(Color.RED);
calculatePoints();
for(int i = 0; i<numOfPoints;i++){
x1 = x[i];
x2 = x[i+1];
y1 = y[i];
y2 = y[i+1];
g.drawLine(x1, y1, x2, y2);
try {
Thread.sleep(animationSleep);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Your sleeping the paint method, something you should never do. It's bad to call Thread.sleep(...) on the Swing event thread, but it's a sin to do it in any painting method. So now the applet can't draw. Remember that any painting method must do nothing but painting, and must do it blazingly fast. It shouldn't do any program logic, it shouldn't directly do animation, it should just paint. Use a Swing timer as any similar question will show you how to do.
Also, never draw directly in the applet but instead in the paintComponent method of a JPanel that the applet holds.
So create your JPanel, override its paintComponent method, and draw using fields of the class. Then change the state of those fields in your Timer and call repaint() on the JPanel.
Edit
And yes, you should avoid using null layouts as that's preventing you from easily and adequately adding components to your GUI. Instead in this situation, a FlowLayout, the default layout for JPanel, would work great.
For example:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferedImage;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.lang.reflect.InvocationTargetException;
import java.util.concurrent.ExecutionException;
import javax.swing.*;
#SuppressWarnings("serial")
public class SpaceCadets extends JApplet {
#Override
public void init() {
try {
SwingUtilities.invokeAndWait(new Runnable() {
public void run() {
SpaceCadetsPanel panel = new SpaceCadetsPanel();
getContentPane().add(panel);
setSize(SpaceCadetsPanel.PREF_W, SpaceCadetsPanel.PREF_H);
}
});
} catch (InvocationTargetException | InterruptedException e) {
e.printStackTrace();
}
}
}
#SuppressWarnings("serial")
class SpaceCadetsPanel extends JPanel {
public static final int PREF_W = 600;
public static final int PREF_H = 600;
public final static int TOTAL_POINTS = 1080;
private static final int R = 75;
private static final int R2 = 10;
private static final int O = 75;
private static final Color DRAW_COLOR = Color.RED;
private static final int ANIMATION_DELAY = 20;
private Point[] pts;
private JSpinner pointCountSpinner = new JSpinner(new SpinnerNumberModel(
800, 100, 1080, 1));
private JButton doItButton = new JButton(new DoItBtnAction("Do It!"));
private BufferedImage bufImg = new BufferedImage(PREF_W, PREF_H,
BufferedImage.TYPE_INT_ARGB);
private Timer timer;
public int imageIndex;
private CalcWorker calcWorker;
public Graphics2D g2;
public SpaceCadetsPanel() {
System.out.println(pointCountSpinner.getEditor().getClass());
add(pointCountSpinner);
add(doItButton);
setBackground(Color.white);
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (bufImg != null) {
g.drawImage(bufImg, 0, 0, this);
}
}
class DoItBtnAction extends AbstractAction {
public DoItBtnAction(String name) {
super(name);
int mnemonic = (int) name.charAt(0);
putValue(MNEMONIC_KEY, mnemonic);
}
#Override
public void actionPerformed(ActionEvent e) {
imageIndex = 0;
if (timer != null && timer.isRunning()) {
timer.stop();
}
if (g2 != null) {
g2.dispose();
}
pts = new Point[0];
bufImg = new BufferedImage(PREF_W, PREF_H, BufferedImage.TYPE_INT_ARGB);
g2 = bufImg.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(DRAW_COLOR);
int totalPoints = 0;
totalPoints = ((Integer) pointCountSpinner.getValue()).intValue();
timer = new Timer(ANIMATION_DELAY, new TimerListener(totalPoints));
calcWorker = new CalcWorker(totalPoints);
calcWorker.addPropertyChangeListener(new CalcWorkerListener());
calcWorker.execute();
}
}
class CalcWorker extends SwingWorker<Point[], Void> {
private int totalPoints;
public CalcWorker(int totalPoints) {
this.totalPoints = totalPoints;
}
#Override
protected Point[] doInBackground() throws Exception {
Point[] pts2 = new Point[totalPoints];
for (int i = 0; i < pts2.length; i++) {
int x = (int) ((R + R2) * Math.cos(i) - (R2 + O)
* Math.cos(((R + R2) / R2) * i) + PREF_W / 2);
int y = (int) ((R + R2) * Math.sin(i) - (R2 + O)
* Math.sin(((R + R2) / R2) * i) + PREF_H / 2);
pts2[i] = new Point(x, y);
}
return pts2;
}
}
class CalcWorkerListener implements PropertyChangeListener {
#Override
public void propertyChange(PropertyChangeEvent evt) {
if (SwingWorker.StateValue.DONE == evt.getNewValue()) {
try {
pts = calcWorker.get();
timer.start();
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}
}
}
class TimerListener implements ActionListener {
private int totalPoints;
public TimerListener(int totalPoints) {
this.totalPoints = totalPoints;
}
#Override
public void actionPerformed(ActionEvent e) {
int x1 = pts[imageIndex].x;
int y1 = pts[imageIndex].y;
int x2 = pts[imageIndex + 1].x;
int y2 = pts[imageIndex + 1].y;
g2.drawLine(x1, y1, x2, y2);
repaint();
imageIndex++;
if (imageIndex == totalPoints - 1) {
((Timer) e.getSource()).stop();
}
}
}
}
Which displays as,
Related
There is one Paraview user interface as follow attracted me.
I think this interface can be used to assign value into array. It works like this :
I want to implement this into a Java program but I found no Java API can support my idea. The closest design from me would be adding multiple JSlider like this :
But what if it is a 100 size array, I wouldn't want to add 100 JSliders. Do you have better solution for this ?
Okay, so this is a pretty basic example. It needs a lot more work and optimisation, but should get you moving in the right direction
Have a look at Painting in AWT and Swing, Performing Custom Painting, 2D Graphics and How to Write a Mouse Listener for more details
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.Shape;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Path2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestGraph {
public static void main(String[] args) {
new TestGraph();
}
public TestGraph() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
ex.printStackTrace();
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new GraphPane(0, 100, new int[100]));
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public static class GraphPane extends JPanel {
protected static final int COLUMN_WIDTH = 10;
protected static final int VERTICAL_INSETS = 10;
private int[] data;
private int minValue, maxValue;
private Path2D.Double graph;
private List<Shape> buttons;
private Point mousePoint;
public GraphPane(int minValue, int maxValue, int[] data) {
this.data = data;
this.minValue = minValue;
this.maxValue = maxValue;
buttons = new ArrayList<>(data == null ? 25 : data.length);
updateView();
MouseAdapter ma = new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
updateData(e);
}
#Override
public void mouseDragged(MouseEvent e) {
updateData(e);
}
};
addMouseListener(ma);
addMouseMotionListener(ma);
}
protected void updateData(MouseEvent e) {
// Which "column" was clicked on
int column = (int) Math.round(((double) e.getX() / (double) COLUMN_WIDTH)) - 1;
// Get the "height" of the clickable area
int clickRange = getHeight() - (VERTICAL_INSETS * 2);
// Adjust the y click point for the margins...
int yPos = e.getY() - VERTICAL_INSETS;
// Calculate the vertical position that was clicked
// this ensures that the range is between 0 and clickRange
// You could choose to ignore values out side of this range
int row = Math.min(Math.max(clickRange - yPos, 0), clickRange);
// Normalise the value between 0-1
double clickNormalised = row / (double) clickRange;
// Calculate the actual row value...
row = minValue + (int) (Math.round(clickNormalised * maxValue));
// Update the data
data[column] = row;
mousePoint = new Point(
COLUMN_WIDTH + (column * COLUMN_WIDTH),
getHeight() - (VERTICAL_INSETS + (int) Math.round((data[column] / 100d) * clickRange)));
updateView();
repaint();
}
#Override
public void invalidate() {
super.invalidate();
updateView();
}
protected Shape createButton(int xPos, int yPos) {
return new Ellipse2D.Double(xPos - COLUMN_WIDTH / 2, yPos - COLUMN_WIDTH / 2, COLUMN_WIDTH, COLUMN_WIDTH);
}
protected void updateView() {
graph = new Path2D.Double();
buttons.clear();
if (data != null && data.length > 0) {
int verticalRange = getHeight() - (VERTICAL_INSETS * 2);
int xPos = COLUMN_WIDTH;
int yPos = getHeight() - (VERTICAL_INSETS + (int) Math.round((data[0] / 100d) * verticalRange));
graph.moveTo(xPos, yPos);
if (data[0] > 0) {
buttons.add(createButton(xPos, yPos));
}
for (int index = 1; index < data.length; index++) {
xPos = (index * COLUMN_WIDTH) + COLUMN_WIDTH;
yPos = getHeight() - (VERTICAL_INSETS + (int) Math.round((data[index] / 100d) * verticalRange));
graph.lineTo(xPos, yPos);
if (data[index] > 0) {
buttons.add(createButton(xPos, yPos));
}
}
}
}
#Override
public Dimension getPreferredSize() {
return new Dimension(data == null ? 0 : (data.length + 1) * COLUMN_WIDTH, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (data != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setColor(new Color(64, 64, 64, 32));
for (int index = 0; index < data.length; index++) {
int xPos = (index * COLUMN_WIDTH) + COLUMN_WIDTH;
g2d.drawLine(xPos, VERTICAL_INSETS, xPos, getHeight() - VERTICAL_INSETS);
}
g2d.setColor(Color.BLACK);
g2d.draw(graph);
for (Shape button : buttons) {
g2d.fill(button);
}
if (mousePoint != null) {
g2d.setColor(new Color(255, 192, 203));
Ellipse2D dot = new Ellipse2D.Double((mousePoint.x - COLUMN_WIDTH / 2) - 2, (mousePoint.y - COLUMN_WIDTH / 2) - 2, COLUMN_WIDTH + 4, COLUMN_WIDTH + 4);
g2d.draw(dot);
g2d.setColor(new Color(255, 192, 203, 128));
g2d.fill(dot);
}
g2d.dispose();
}
}
}
}
Before anyone says I didn't put the "fill" in, I deliberately used a Path2D to make it much simpler to achieve ;)
here is a small example how to create this using polygon class .i sorted x coordinate and use polygon class to make this.
GraphPane.class
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Polygon;
import java.awt.RenderingHints;
import java.util.ArrayList;
import java.util.Collections;
import javax.swing.JPanel;
public class GraphPane extends JPanel {
ArrayList<XYpoints> poinList = new ArrayList();
private int px;
private int py;
private XYpoints last;
private boolean drag;
private static Color graphColor=new Color(32, 178, 170);
public GraphPane() {
initComponents();
poinList.add(new XYpoints(50, 400));
poinList.add(new XYpoints(450, 50));
poinList.add(new XYpoints(600, 400));
}
private void initComponents() {
setBackground(new java.awt.Color(255, 255, 255));
addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
System.out.println("drag");
if (drag) {
last.setY(evt.getY());
GraphPane.this.repaint();
}
}
});
addMouseListener(new java.awt.event.MouseAdapter() {
public void mousePressed(java.awt.event.MouseEvent evt) {
int x = evt.getX();
int y = evt.getY();
for (XYpoints poinList1 : poinList) {
px = poinList1.getpX();
py = poinList1.getpY();
if (x < px + 5 && x > px - 5 && y < py + 5 && y > py - 5) {
System.out.println("inter");
poinList1.setIntersect(true);
last = poinList1;
drag = true;
GraphPane.this.repaint();
return;
}
}
poinList.add(new XYpoints(x, y));
Collections.sort(poinList, new XComp());
GraphPane.this.repaint();
}
public void mouseReleased(java.awt.event.MouseEvent evt) {
if (drag) {
drag = false;
last.setIntersect(false);
GraphPane.this.repaint();
}
}
});
}
#Override
protected void paintComponent(Graphics gr) {
super.paintComponent(gr);
Graphics2D g = (Graphics2D) gr.create();
Polygon p = new Polygon();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (XYpoints poinList1 : poinList) {
px = poinList1.getpX();
py = poinList1.getpY();
p.addPoint(px, py);
}
g.setColor(graphColor);
g.fillPolygon(p);
for (XYpoints poinList1 : poinList) {
px = poinList1.getpX();
py = poinList1.getpY();
g.setColor(Color.red);
if (poinList1.isIntersect()) {
g.setColor(Color.blue);
}
g.fillOval(px - 5, py - 5, 10, 10);
}
g.dispose();
}
}
XYpoints.class
import java.awt.Polygon;
import java.util.Comparator;
public class XYpoints extends Polygon {
private int x;
private int y;
private boolean inter;
public XYpoints(int x, int y) {
this.x = x;
this.y = y;
}
public void setIntersect(boolean state) {
inter = state;
}
public void setY(int y){
this.y=y;
}
public boolean isIntersect() {
return inter;
}
public int getpX() {
//System.out.println("send " + this.x);
return this.x;
}
public int getpY() {
return this.y;
}
}
XComp .class
class XComp implements Comparator<XYpoints> {
#Override
public int compare(XYpoints t, XYpoints t1) {
if (t.getpX() < t1.getpX()) {
return -1;
} else {
return 1;
}
}
}
myframe.class
import javax.swing.JFrame;
public class myframe extends JFrame {
public myframe() {
GraphPane pane = new GraphPane();
setContentPane(pane);
setSize(650, 500);
setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
new myframe();
}
});
}
}
So right now when you press go it just moves across the screen, but I also want it to move its legs so its doing something and not just standing still. Any tips on how to do that? I know it would be moving the 2nd leg pointers back and forth but I'm not sure how to do that
Class Stick2 :
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class Stick2 extends JFrame implements ActionListener {
// Declares constants
public static final int FRAME_WIDTH = 500;
private static final int FRAME_HEIGHT = 500;
private static final int FRAME_X_ORIGIN = 150;
private static final int FRAME_Y_ORIGIN = 200;
private static final int BUTTON_WIDTH =80;
private static final int BUTTON_HEIGHT = 30;
JPanel buttonPanel, panel;
MovingBanner2 myBanner;
JButton startButton, stopButton;
Thread thrd;
public static void main(String[] args)
{
Stick2 frame = new Stick2();
frame.setVisible(true);
}
public Stick2(){
Container contentPane= getContentPane();
// Sets Frame
setSize(FRAME_WIDTH,FRAME_HEIGHT);
setResizable(false);
setTitle("Animation");
setLocation(FRAME_X_ORIGIN, FRAME_Y_ORIGIN);
// Sets layout manager
contentPane.setLayout(new BorderLayout(10,0));
buttonPanel = new JPanel();
JButton startButton = new JButton("Start");
startButton.setSize(BUTTON_WIDTH, BUTTON_HEIGHT);
buttonPanel.add(startButton);
startButton.addActionListener(this);
JButton stopButton = new JButton("Stop");
stopButton.setSize(BUTTON_WIDTH,BUTTON_HEIGHT);
buttonPanel.add(stopButton);
stopButton.addActionListener(this);
contentPane.add (buttonPanel, BorderLayout.SOUTH);
// Creates a balloon
myBanner = new MovingBanner2();
panel = myBanner;
panel.setBorder(BorderFactory.createLineBorder(Color.BLUE));
contentPane.add(panel, BorderLayout.CENTER);
}
public void actionPerformed (ActionEvent event){
JButton clickedButton = (JButton) event.getSource();
String buttonText = clickedButton.getText();
if (buttonText.equals("Stop")) {
myBanner.stopAnimation();
thrd = null;
}
else {
myBanner.startAnimation();
thrd = new Thread (myBanner);
thrd.start();
}
}
}
Class MovingBanner2:
class MovingBanner2 extends JPanel implements Runnable {
private int x;
private Boolean animate;
int bodyX = 250;
int bodyY1 = 160;
int bodyY2 = 210;
int armHeight = 190;
int armLength = bodyX + 30;
int armLength1 = bodyX - 30;
int legY = 340;
public MovingBanner2() {
x=10;
animate = true;
}
// Draws the String
public void paintComponent (Graphics g) {
super.paintComponent(g);
g.setColor(Color.RED);
//g.drawString("I love Java", x,50);
g.drawLine(bodyX + x, bodyY1, bodyX + x, bodyY2); //body
g.drawOval(bodyX + x - 15, bodyY1 - 40, 40, 40); //head
g.drawLine(armLength + x,armHeight, armLength1 + x, armHeight); //arms
g.drawLine(bodyX + x, bodyY2, bodyX + 20 + x,legY); //leg
g.drawLine(bodyX + x, bodyY2, bodyX - 20 + x, legY); //leg
}
public void run() {
while (animate) {
changeX();
repaint();
try {Thread.sleep(100); } catch(Exception e){};
}
}
public void changeX() {
if (x <= Stick2.FRAME_WIDTH - 240)
x++;
else x = 10;
}
public void stopAnimation() {
animate = false;
}
public void startAnimation() {
animate = true;
}
}
There are probably many answers to the question. The best I can come up with is basically to devise some kind of "cycle".
A cycle is a known period of time over which animation can run. In this example, it's 1 second.
You then need to provide a series of Animatable objects that are notified of a change to the cycle over time, which allows them to make changes accordingly. You can also ignore the cycle for those elements that don't need to cycle.
The intention is provide a single animation engine that can be responsible for updating the entire state in one go, rather then trying to use multiple threads/timers which may reduce the systems performance.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.GridBagLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class TestAnimation06 {
public static void main(String[] args) {
new TestAnimation06();
}
public TestAnimation06() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Testing");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new TestPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public interface Animatable {
public void update(float progress);
}
public class AnimationEngine {
private List<Animatable> animations;
private int cycleTime = 1000;
private long startTime = -1;
public AnimationEngine() {
animations = new ArrayList<>(25);
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (startTime < 0) {
startTime = System.currentTimeMillis();
}
long duration = System.currentTimeMillis() - startTime;
float progress = (float)duration / (float)cycleTime;
if (duration >= cycleTime) {
progress = 1f;
startTime = System.currentTimeMillis();
}
for (Animatable animatable : animations) {
animatable.update(progress);
}
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
public void add(Animatable animatable) {
animations.add(animatable);
}
}
public class TestPane extends JPanel {
private AnimationEngine engine;
public TestPane() {
setLayout(null);
engine = new AnimationEngine();
Legs legs = new Legs();
Walker walker = new Walker(legs);
engine.add(legs);
engine.add(walker);
walker.setSize(walker.getPreferredSize());
add(walker);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.dispose();
}
}
public class Walker extends JPanel implements Animatable {
private int speed = 2;
public Walker(Legs legs) {
setLayout(new GridBagLayout());
add(legs);
}
#Override
public void update(float progress) {
Container parent = getParent();
int width = parent.getWidth();
int xPos = getX() + speed;
if (xPos <= 0) {
speed *= -1;
xPos = 0;
} else if (xPos + getWidth() >= width) {
speed *= -1;
xPos = width - getWidth();
}
System.out.println(xPos);
setLocation(xPos, (parent.getHeight() - getHeight()) / 2);
repaint();
}
}
public class Legs extends JPanel implements Animatable {
private float frameProgress;
public Legs() {
setOpaque(false);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(25, 50);
}
#Override
public void update(float progress) {
frameProgress = progress;
repaint();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int width = getWidth();
int height = getHeight();
g.setColor(Color.BLACK);
g.drawLine(width / 2, 0, (int)(width * frameProgress), height);
g.drawLine(width / 2, 0, width - (int)(width * frameProgress), height);
}
}
}
newbie programmer here.
I'm making a program that renders user-inputted equations in a Cartesian coordinate system. At the moment I'm having some issues with letting the user move the view around freely in the coordinate. Currently with mouseDragged the user can drag the view around a bit, but once the user releases the mouse and tries to move the view again the origin snaps back to the current position of the mouse cursor. What is the best way to let the user move around freely? Thanks in advance!
Here's the code for the drawing area.
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Point;
import java.awt.Rectangle;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.awt.geom.Line2D;
import java.awt.geom.Point2D;
import javax.swing.JPanel;
public class DrawingArea extends JPanel implements MouseMotionListener {
private final int x_panel = 350; // width of the panel
private final int y_panel = 400; // height of the panel
private int div_x; // width of one square
private int div_y; // height of one square
private int real_y;
private int real_x;
private Point origin; // the origin of the coordinate
private Point temp; // temporary point
private static int y = 0;
private static int x = 0;
DrawingArea() {
setBackground(Color.WHITE);
real_x = x_panel;
real_y = y_panel;
setDivisionDefault();
setOrigin(new Point((real_x / 2), (real_y / 2)));
setSize(x_panel, y_panel);
addMouseMotionListener(this);
}
DrawingArea(Point origin, Point destination) {
this.origin = origin;
this.destination = destination;
panel = new JPanel();
panel.setSize(destination.x, destination.y);
panel.setLocation(origin);
this.panel.setBackground(Color.red);
panel.setLayout(null);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D line = (Graphics2D) g;
temp = new Point(origin.x, origin.y);
line.setColor(Color.red);
drawHelpLines(line);
line.setColor(Color.blue);
drawOrigin(line);
line.setColor(Color.green);
for (int i = 0; i < 100; i++) { // This is a test line
//temp = this.suora();
temp.x++;
temp.y++;
line.drawLine(temp.x, temp.y, temp.x, temp.y);
}
}
public void setOrigin(Point p) {
origin = p;
}
public void drawOrigin(Graphics2D line) {
line.drawLine(origin.x, 0, origin.x, y_panel);
line.drawLine(0, origin.y, x_panel, origin.y);
}
public void drawHelpLines(Graphics2D line) {
int xhelp= origin.x;
int yhelp= origin.y;
for (int i = 0; i < 20; i++) {
xhelp+= div_x;
line.drawLine(xhelp, 0, xhelp, y_panel);
}
xhelp= origin.x;
for (int i = 0; i < 20; i++) {
xhelp-= div_x;
line.drawLine(xhelp, 0, xhelp, y_panel);
}
for (int i = 0; i < 20; i++) {
yhelp-= div_y;
line.drawLine(0, yhelp,x_panel, yhelp);
}
yhelp= origin.y;
for (int i = 0; i < 20; i++) {
yhelp+= div_y;
line.drawLine(0, yhelp, x_panel, yhelp);
}
}
public void setDivisionDefault() {
div_x = 20;
div_y = 20;
}
#Override
public void mouseDragged(MouseEvent e) {
//Point temp_point = new Point(mouse_x,mouse_y);
Point coords = new Point(e.getX(), e.getY());
setOrigin(coords);
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
}
}
Based on this example, the following program allows the user to drag the axes' intersection to an arbitrary point, origin, which starts at the center of the panel.
import java.awt.Cursor;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Point;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionAdapter;
import javax.swing.JFrame;
import javax.swing.JPanel;
/**
* #see https://stackoverflow.com/a/15576413/230513
* #see https://stackoverflow.com/a/5312702/230513
*/
public class MouseDragTest extends JPanel {
private static final String TITLE = "Drag me!";
private static final int W = 640;
private static final int H = 480;
private Point origin = new Point(W / 2, H / 2);
private Point mousePt;
public MouseDragTest() {
this.setCursor(Cursor.getPredefinedCursor(Cursor.HAND_CURSOR));
this.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
mousePt = e.getPoint();
repaint();
}
});
this.addMouseMotionListener(new MouseMotionAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
int dx = e.getX() - mousePt.x;
int dy = e.getY() - mousePt.y;
origin.setLocation(origin.x + dx, origin.y + dy);
mousePt = e.getPoint();
repaint();
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(W, H);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawLine(0, origin.y, getWidth(), origin.y);
g.drawLine(origin.x, 0, origin.x, getHeight());
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame(TITLE);
f.add(new MouseDragTest());
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
});
}
}
Hi!
I have the code below using previous Stackoverflow posts.
I want to just rotate the rectangle by some angle and make it move in sin wave.
This code rotates the whole sin wave too.
I understand why it is happening , but I don't know how to achieve my intention.
please help!!!
Thanks a lot for taking time.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.geom.AffineTransform;
import java.util.Timer;
import java.util.TimerTask;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Withrotation {
public static int i = 1;
public static Ticker t;
public static Repainter r;
public static int newx, newy;
public static void main(String[] args) {
final JFrame frame = new JFrame("Wavy!");
final WavyPanel wp = new WavyPanel();
frame.getContentPane().add(wp, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
t = new Ticker(wp);
r = new Repainter(wp);
frame.pack();
frame.setVisible(true);
final Timer tickTimer = new Timer();
final Timer paintTimer = new Timer();
paintTimer.schedule(r, 1000, 50);
tickTimer.schedule(t, 1000, 10);
}
private static class WavyPanel extends JPanel {
private final Dimension size = new Dimension(640, 480);
private int amplitude = 50;
private int frequency = 5;
private double x1 = 0;
private double y1 = 500;
private int yBase = 0;
WavyPanel() {
super(true);
}
#Override
protected void paintComponent(final Graphics g) {
final Graphics2D g2 = (Graphics2D) g;
AffineTransform old = g2.getTransform();
g2.rotate(Math.toRadians(-30));
g2.clearRect(0, 0, this.getSize().width, this.getSize().height);
g2.setColor(Color.BLACK);
g2.fillRect((int) x1, (int) y1, 20, 80);
g2.setTransform(old);
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
public Dimension getMinimumSize() {
return size;
}
#Override
public Dimension getMaximumSize() {
return size;
}
public void tick() {
x1 = x1 + 1;
final int waveLength = size.width / frequency;
yBase = (++yBase) % waveLength;
final double normalized = (double) yBase / (double) waveLength;
final double radians = normalized * Math.PI * 2;
final double sine = Math.sin(radians);
y1 = (int) (sine * amplitude);
}
}
private static class Ticker extends TimerTask {
private final WavyPanel panel;
Ticker(final WavyPanel panel) {
this.panel = panel;
}
#Override
public void run() {
panel.tick();
}
}
private static class Repainter extends TimerTask {
private final WavyPanel panel;
Repainter(final WavyPanel panel) {
this.panel = panel;
}
#Override
public void run() {
panel.repaint();
}
}
}
+1 for SSCCE
1) Dont forget to have call to super.paintComponent(); as first statement in your overridden paintComponent(..) method.
2) Swing UI should be created on EDT and used in conjunction with Swing Timers
3) Java variable naming convention for classes is uppercase letter for each new word i.e WithRotation.
4) No need for frame.getContentPane.add(..) simply use add(..) as all calls are forwarded to its contentPane.
Here is the example I made (basically your code with above fixes implemented), which only rotates the rectangle which follows the graph and not the whole graphics object using AffineTransform#createTransformedShape():
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.Shape;
import java.awt.event.ActionEvent;
import java.awt.geom.AffineTransform;
import javax.swing.AbstractAction;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class WithRotation {
private JFrame frame;
private WavyPanel wp;
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new WithRotation();
}
});
}
public WithRotation() {
initComponents();
}
private void initComponents() {
frame = new JFrame("Wavy!");
wp = new WavyPanel();
frame.add(wp, BorderLayout.CENTER);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
createAndStartTimers();
}
private void createAndStartTimers() {
new Timer(50, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
wp.repaint();
}
}).start();
new Timer(10, new AbstractAction() {
#Override
public void actionPerformed(ActionEvent ae) {
wp.tick();
}
}).start();
}
class WavyPanel extends JPanel {
private final Dimension size = new Dimension(640, 480);
private int amplitude = 50;
private int frequency = 5;
private double x1 = 0;
private double y1 = 500;
private int yBase = 0;
WavyPanel() {
super(true);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.clearRect(0, 0, this.getSize().width, this.getSize().height);
g2.setColor(Color.BLACK);
Rectangle rect = new Rectangle((int) x1, (int) y1, 20, 80);
AffineTransform transform = new AffineTransform();
transform.rotate(Math.toRadians(-30), rect.getX() + rect.width / 2, rect.getY() + rect.height / 2);
Shape transformed = transform.createTransformedShape(rect);
g2.fill(transformed);
}
#Override
public Dimension getPreferredSize() {
return size;
}
#Override
public Dimension getMinimumSize() {
return size;
}
#Override
public Dimension getMaximumSize() {
return size;
}
public void tick() {
x1 = x1 + 1;
final int waveLength = size.width / frequency;
yBase = (++yBase) % waveLength;
final double normalized = (double) yBase / (double) waveLength;
final double radians = normalized * Math.PI * 2;
final double sine = Math.sin(radians);
y1 = (int) (sine * amplitude);
}
}
}
I'm starting to learn java programming and I think it's cool to learn java through game development. I know how to draw image and listen to a keypress then move that image. But is it possible to make the image move back and forth to the window while the window is listening to a keypress? Like for example, while the image or object(like spaceship) is moving left to right in the window, then if I press space key, a laser will fire at the bottom of the screen( cool huh :D ). But basically I just want to know how to make the image move left to right while the window is listening to a keypress.
I'm thinking that I will add a key listener to my window then fire an infinite loop to move the image. Or do I need to learn about threading so that another thread will move the object?
Please advise.
Many thanks.
Yep, a Swing Timer and Key Bindings would work well. Here's another example (mine) :)
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
public class AnimationWithKeyBinding {
private static void createAndShowUI() {
AnimationPanel panel = new AnimationPanel(); // the drawing JPanel
JFrame frame = new JFrame("Animation With Key Binding");
frame.getContentPane().add(panel);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
java.awt.EventQueue.invokeLater(new Runnable() {
public void run() {
createAndShowUI();
}
});
}
}
#SuppressWarnings("serial")
class AnimationPanel extends JPanel {
public static final int SPRITE_WIDTH = 20;
public static final int PANEL_WIDTH = 400;
public static final int PANEL_HEIGHT = 400;
private static final int MAX_MSTATE = 25;
private static final int SPIN_TIMER_PERIOD = 16;
private static final int SPRITE_STEP = 3;
private int mState = 0;
private int mX = (PANEL_WIDTH - SPRITE_WIDTH) / 2;
private int mY = (PANEL_HEIGHT - SPRITE_WIDTH) / 2;
private int oldMX = mX;
private int oldMY = mY;
private boolean moved = false;
// an array of sprite images that are drawn sequentially
private BufferedImage[] spriteImages = new BufferedImage[MAX_MSTATE];
public AnimationPanel() {
// create and start the main animation timer
new Timer(SPIN_TIMER_PERIOD, new SpinTimerListener()).start();
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
setBackground(Color.white);
createSprites(); // create the images
setupKeyBinding();
}
private void setupKeyBinding() {
int condition = JComponent.WHEN_IN_FOCUSED_WINDOW;
InputMap inMap = getInputMap(condition);
ActionMap actMap = getActionMap();
// this uses an enum of Direction that holds ints for the arrow keys
for (Direction direction : Direction.values()) {
int key = direction.getKey();
String name = direction.name();
// add the key bindings for arrow key and shift-arrow key
inMap.put(KeyStroke.getKeyStroke(key, 0), name);
inMap.put(KeyStroke.getKeyStroke(key, InputEvent.SHIFT_DOWN_MASK), name);
actMap.put(name, new MyKeyAction(this, direction));
}
}
// create a bunch of buffered images and place into an array,
// to be displayed sequentially
private void createSprites() {
for (int i = 0; i < spriteImages.length; i++) {
spriteImages[i] = new BufferedImage(SPRITE_WIDTH, SPRITE_WIDTH,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = spriteImages[i].createGraphics();
g2.setColor(Color.red);
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
double theta = i * Math.PI / (2 * spriteImages.length);
double x = SPRITE_WIDTH * Math.abs(Math.cos(theta)) / 2.0;
double y = SPRITE_WIDTH * Math.abs(Math.sin(theta)) / 2.0;
int x1 = (int) ((SPRITE_WIDTH / 2.0) - x);
int y1 = (int) ((SPRITE_WIDTH / 2.0) - y);
int x2 = (int) ((SPRITE_WIDTH / 2.0) + x);
int y2 = (int) ((SPRITE_WIDTH / 2.0) + y);
g2.drawLine(x1, y1, x2, y2);
g2.drawLine(y1, x2, y2, x1);
g2.dispose();
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(spriteImages[mState], mX, mY, null);
}
public void incrementX(boolean right) {
oldMX = mX;
if (right) {
mX = Math.min(getWidth() - SPRITE_WIDTH, mX + SPRITE_STEP);
} else {
mX = Math.max(0, mX - SPRITE_STEP);
}
moved = true;
}
public void incrementY(boolean down) {
oldMY = mY;
if (down) {
mY = Math.min(getHeight() - SPRITE_WIDTH, mY + SPRITE_STEP);
} else {
mY = Math.max(0, mY - SPRITE_STEP);
}
moved = true;
}
public void tick() {
mState = (mState + 1) % MAX_MSTATE;
}
private class SpinTimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
tick();
int delta = 20;
int width = SPRITE_WIDTH + 2 * delta;
int height = width;
// make sure to erase the old image
if (moved) {
int x = oldMX - delta;
int y = oldMY - delta;
repaint(x, y, width, height);
}
int x = mX - delta;
int y = mY - delta;
// draw the new image
repaint(x, y, width, height);
moved = false;
}
}
}
enum Direction {
UP(KeyEvent.VK_UP), DOWN(KeyEvent.VK_DOWN), LEFT(KeyEvent.VK_LEFT), RIGHT(KeyEvent.VK_RIGHT);
private int key;
private Direction(int key) {
this.key = key;
}
public int getKey() {
return key;
}
}
// Actions for the key binding
#SuppressWarnings("serial")
class MyKeyAction extends AbstractAction {
private AnimationPanel draw;
private Direction direction;
public MyKeyAction(AnimationPanel draw, Direction direction) {
this.draw = draw;
this.direction = direction;
}
#Override
public void actionPerformed(ActionEvent e) {
switch (direction) {
case UP:
draw.incrementY(false);
break;
case DOWN:
draw.incrementY(true);
break;
case LEFT:
draw.incrementX(false);
break;
case RIGHT:
draw.incrementX(true);
break;
default:
break;
}
}
}
Here is another example that uses this sprite sheet:
obtained from this site.
Again it's an example of drawing within a JPanel's paintComponent method and using Key Bindings to tell which direction to move.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.List;
import java.util.Map;
import javax.imageio.ImageIO;
import javax.swing.*;
#SuppressWarnings("serial")
public class Mcve3 extends JPanel {
private static final int PREF_W = 800;
private static final int PREF_H = 640;
private static final int TIMER_DELAY = 50;
private int spriteX = 400;
private int spriteY = 320;
private SpriteDirection spriteDirection = SpriteDirection.RIGHT;
private MySprite sprite = null;
private Timer timer = null;
public Mcve3() {
try {
sprite = new MySprite(spriteDirection, spriteX, spriteY);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
}
setBackground(Color.WHITE);
setKeyBindings(SpriteDirection.LEFT, KeyEvent.VK_LEFT);
setKeyBindings(SpriteDirection.RIGHT, KeyEvent.VK_RIGHT);
setKeyBindings(SpriteDirection.FORWARD, KeyEvent.VK_DOWN);
setKeyBindings(SpriteDirection.AWAY, KeyEvent.VK_UP);
timer = new Timer(TIMER_DELAY, new TimerListener());
timer.start();
}
private void setKeyBindings(SpriteDirection dir, int keyCode) {
int condition = WHEN_IN_FOCUSED_WINDOW;
InputMap inputMap = getInputMap(condition);
ActionMap actionMap = getActionMap();
KeyStroke keyPressed = KeyStroke.getKeyStroke(keyCode, 0, false);
KeyStroke keyReleased = KeyStroke.getKeyStroke(keyCode, 0, true);
inputMap.put(keyPressed, keyPressed.toString());
inputMap.put(keyReleased, keyReleased.toString());
actionMap.put(keyPressed.toString(), new MoveAction(dir, false));
actionMap.put(keyReleased.toString(), new MoveAction(dir, true));
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
sprite.draw(g);
}
private class MoveAction extends AbstractAction {
private SpriteDirection dir;
private boolean released;
public MoveAction(SpriteDirection dir, boolean released) {
this.dir = dir;
this.released = released;
}
#Override
public void actionPerformed(ActionEvent e) {
if (released) {
sprite.setMoving(false);
} else {
sprite.setMoving(true);
sprite.setDirection(dir);
}
}
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (sprite.isMoving()) {
sprite.tick();
}
repaint();
}
}
private static void createAndShowGui() {
Mcve3 mainPanel = new Mcve3();
JFrame frame = new JFrame("MCVE");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
class MySprite {
private static final String SPRITE_SHEET_PATH = "http://"
+ "orig12.deviantart.net/7db3/f/2010/338/3/3/"
+ "animated_sprite_sheet_32x32_by_digibody-d3479l2.gif";
private static final int MAX_MOVING_INDEX = 4;
private static final int DELTA = 4;
private SpriteDirection direction;
private Map<SpriteDirection, Image> standingImgMap = new EnumMap<>(SpriteDirection.class);
private Map<SpriteDirection, List<Image>> movingImgMap = new EnumMap<>(SpriteDirection.class);
private int x;
private int y;
private boolean moving = false;
private int movingIndex = 0;
public MySprite(SpriteDirection direction, int x, int y) throws IOException {
this.direction = direction;
this.x = x;
this.y = y;
createSprites();
}
public void draw(Graphics g) {
Image img = null;
if (!moving) {
img = standingImgMap.get(direction);
} else {
img = movingImgMap.get(direction).get(movingIndex);
}
g.drawImage(img, x, y, null);
}
private void createSprites() throws IOException {
URL spriteSheetUrl = new URL(SPRITE_SHEET_PATH);
BufferedImage img = ImageIO.read(spriteSheetUrl);
// get sub-images (sprites) from the sprite sheet
// magic numbers for getting sprites from sheet, all obtained by trial and error
int x0 = 0;
int y0 = 64;
int rW = 32;
int rH = 32;
for (int row = 0; row < 4; row++) {
SpriteDirection dir = SpriteDirection.values()[row];
List<Image> imgList = new ArrayList<>();
movingImgMap.put(dir, imgList);
int rY = y0 + row * rH;
for (int col = 0; col < 5; col++) {
int rX = x0 + col * rW;
BufferedImage subImg = img.getSubimage(rX, rY, rW, rH);
if (col == 0) {
// first image is standing
standingImgMap.put(dir, subImg);
} else {
// all others are moving
imgList.add(subImg);
}
}
}
}
public SpriteDirection getDirection() {
return direction;
}
public void setDirection(SpriteDirection direction) {
if (this.direction != direction) {
setMoving(false);
}
this.direction = direction;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
public boolean isMoving() {
return moving;
}
public void setMoving(boolean moving) {
this.moving = moving;
if (!moving) {
movingIndex = 0;
}
}
public void tick() {
if (moving) {
switch (direction) {
case RIGHT:
x += DELTA;
break;
case LEFT:
x -= DELTA;
break;
case FORWARD:
y += DELTA;
break;
case AWAY:
y -= DELTA;
}
movingIndex++;
movingIndex %= MAX_MOVING_INDEX;
}
}
public int getMovingIndex() {
return movingIndex;
}
public void setMovingIndex(int movingIndex) {
this.movingIndex = movingIndex;
}
}
enum SpriteDirection {
FORWARD, LEFT, AWAY, RIGHT
}
As an alternative to KeyListener, consider using actions and key bindings, discussed here. Derived from this example, the program below moves a line left, down, up or right using either buttons or keys.
import java.awt.BasicStroke;
import java.awt.BorderLayout;
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.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import javax.swing.AbstractAction;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
/**
* #see https://stackoverflow.com/questions/6991648
* #see https://stackoverflow.com/questions/6887296
* #see https://stackoverflow.com/questions/5797965
*/
public class LinePanel extends JPanel {
private MouseHandler mouseHandler = new MouseHandler();
private Point p1 = new Point(100, 100);
private Point p2 = new Point(540, 380);
private boolean drawing;
public LinePanel() {
this.setPreferredSize(new Dimension(640, 480));
this.addMouseListener(mouseHandler);
this.addMouseMotionListener(mouseHandler);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
g2d.setRenderingHint(
RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2d.setStroke(new BasicStroke(8,
BasicStroke.CAP_ROUND, BasicStroke.JOIN_BEVEL));
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
private class MouseHandler extends MouseAdapter {
#Override
public void mousePressed(MouseEvent e) {
drawing = true;
p1 = e.getPoint();
p2 = p1;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
drawing = false;
p2 = e.getPoint();
repaint();
}
#Override
public void mouseDragged(MouseEvent e) {
if (drawing) {
p2 = e.getPoint();
repaint();
}
}
}
private class ControlPanel extends JPanel {
private static final int DELTA = 10;
public ControlPanel() {
this.add(new MoveButton("\u2190", KeyEvent.VK_LEFT, -DELTA, 0));
this.add(new MoveButton("\u2191", KeyEvent.VK_UP, 0, -DELTA));
this.add(new MoveButton("\u2192", KeyEvent.VK_RIGHT, DELTA, 0));
this.add(new MoveButton("\u2193", KeyEvent.VK_DOWN, 0, DELTA));
}
private class MoveButton extends JButton {
KeyStroke k;
int dx, dy;
public MoveButton(String name, int code, final int dx, final int dy) {
super(name);
this.k = KeyStroke.getKeyStroke(code, 0);
this.dx = dx;
this.dy = dy;
this.setAction(new AbstractAction(this.getText()) {
#Override
public void actionPerformed(ActionEvent e) {
LinePanel.this.p1.translate(dx, dy);
LinePanel.this.p2.translate(dx, dy);
LinePanel.this.repaint();
}
});
ControlPanel.this.getInputMap(
WHEN_IN_FOCUSED_WINDOW).put(k, k.toString());
ControlPanel.this.getActionMap().put(k.toString(), new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
MoveButton.this.doClick();
}
});
}
}
}
private void display() {
JFrame f = new JFrame("LinePanel");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(this);
f.add(new ControlPanel(), BorderLayout.SOUTH);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
new LinePanel().display();
}
});
}
}
But basically I just want to know how to make the image move left to right while the window is listening to a keypress
You can use a Swing Timer to animate an image:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class TimerAnimation extends JLabel implements ActionListener
{
int deltaX = 2;
int deltaY = 3;
int directionX = 1;
int directionY = 1;
public TimerAnimation(
int startX, int startY,
int deltaX, int deltaY,
int directionX, int directionY,
int delay)
{
this.deltaX = deltaX;
this.deltaY = deltaY;
this.directionX = directionX;
this.directionY = directionY;
setIcon( new ImageIcon("dukewavered.gif") );
// setIcon( new ImageIcon("copy16.gif") );
setSize( getPreferredSize() );
setLocation(startX, startY);
new javax.swing.Timer(delay, this).start();
}
public void actionPerformed(ActionEvent e)
{
Container parent = getParent();
// Determine next X position
int nextX = getLocation().x + (deltaX * directionX);
if (nextX < 0)
{
nextX = 0;
directionX *= -1;
}
if ( nextX + getSize().width > parent.getSize().width)
{
nextX = parent.getSize().width - getSize().width;
directionX *= -1;
}
// Determine next Y position
int nextY = getLocation().y + (deltaY * directionY);
if (nextY < 0)
{
nextY = 0;
directionY *= -1;
}
if ( nextY + getSize().height > parent.getSize().height)
{
nextY = parent.getSize().height - getSize().height;
directionY *= -1;
}
// Move the label
setLocation(nextX, nextY);
}
public static void main(String[] args)
{
JPanel panel = new JPanel();
JFrame frame = new JFrame();
frame.setContentPane(panel);
frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );
frame.getContentPane().setLayout(null);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
frame.getContentPane().add( new TimerAnimation(300, 100, 3, 2, -1, 1, 20) );
// frame.getContentPane().add( new TimerAnimation(0, 000, 5, 0, 1, 1, 20) );
frame.getContentPane().add( new TimerAnimation(0, 200, 5, 0, 1, 1, 80) );
frame.setSize(400, 400);
frame.setLocationRelativeTo( null );
frame.setVisible(true);
// frame.getContentPane().add( new TimerAnimation(10, 10, 2, 3, 1, 1, 10) );
// frame.getContentPane().add( new TimerAnimation(10, 10, 3, 0, 1, 1, 10) );
}
}
You can add a KeyListener to the panel and it will operate independently of the image animation.