Graphics - How do I make drawn strings transparent? - java

I tried changing the alpha of a drawn string by doing this:
g2d.drawString("HelloWorld", 50, 50, alphaValue);
This resulted in a compilation error.
All I'm trying to do is make a drawn string slowly turn transparent.

You need to set the color
Color curr = g2d.getColor();
// Color curr = Color.GREEN;
g2d.setColor(new Color(curr.getRed(), curr.getGreen(), curr.getBlue(), alphaValue));
g2d.drawString("HelloWorld", 50, 50);

As Sergiy said, you need to change the alpha of the Color used by the graphics.
Here's an example of one way can fade out the text.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class ExampleFrame extends JFrame {
private JPanel drawPanel = new DrawPanel();
private Timer timer;
private int alpha = 255;
private final int TIMER_TICK = 50;
private final int ALPHA_TICK_VALUE = 3;
private class DrawPanel extends JPanel {
final int PANEL_HEIGHT = 80;
final int PANEL_WIDTH = 100;
final int TEXT_MARGIN = 20;
DrawPanel() {
setPreferredSize(new Dimension(PANEL_WIDTH, PANEL_HEIGHT));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Color color = new Color(0, 0, 0, alpha);
g.setColor(color);
g.drawString("Hello World", TEXT_MARGIN,
PANEL_HEIGHT / 2 + g.getFontMetrics().getHeight() / 2);
}
}
public void createAndShow() {
getContentPane().add(drawPanel);
timer = new Timer(TIMER_TICK, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
alpha -= ALPHA_TICK_VALUE;
if (alpha >= 0) {
drawPanel.repaint();
} else {
alpha = 0;
timer.stop();
}
}
});
pack();
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
timer.start();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
ExampleFrame ef = new ExampleFrame();
ef.createAndShow();
}
});
}
}

Related

How to achieve non-destructive painting in Java?

I want to plot many sinusoids with different frequencies on a panel, but because painting is destructive, I cannot achieve this. I can only see the last sinusoid plotted.
In the code, the generateSinus() method is called only three times, but it might be called many times and I don't want to call g.fillOval() method for each sinusoid in the paintComponent() method.
Is there a way to achieve non-destructive painting and see all sinusioids with different frequencies?
Please see the code below:
package testdrawsinus;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class TestDrawSinus extends JPanel
{
private static double[] x;
private static double[] y;
private static boolean buttonClicked = false;
private static JPanel panel = new TestDrawSinus();
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.setColor(Color.GREEN);
if (buttonClicked)
{
for (int i=0; i<x.length; i++)
{
g.fillOval((int)x[i] + panel.getWidth()/2, -1*((int)y[i]) + panel.getHeight()/2, 10, 10);
}
buttonClicked = false;
}
}
private static void generateSinus(int freq)
{
x = new double[200];
y = new double[200];
for (int i=0; i<=199; i++)
{
x[i]= (double)i;
y[i] = 100*Math.sin(2*Math.PI*freq*i/200);
}
}
public static void main(String[] args)
{
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setSize(800, 600);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
panel.setLayout(null);
panel.setBounds(20,20, 700,400);
panel.setBackground(Color.BLACK);
frame.add(panel);
JButton button1 = new JButton();
button1.setText("plot");
button1.setBounds(300, 500, 150, 50);
frame.add(button1);
button1.addActionListener(new ActionListener()
{
#Override
public void actionPerformed(ActionEvent e)
{
buttonClicked = true;
generateSinus(1);
panel.repaint();
generateSinus(2);
panel.repaint();
generateSinus(3);
panel.repaint();
}
});
}
}
Thanks for your help.
There are a number of ways you might do this, one way would be to seperate each series into its own model, then have the panel paint each model, for example...
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.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
public static void main(String[] args) {
new Main();
}
private Color[] masterColors = new Color[]{
Color.BLACK, Color.BLUE, Color.CYAN, Color.DARK_GRAY,
Color.GREEN, Color.MAGENTA, Color.ORANGE, Color.PINK,
Color.RED, Color.WHITE, Color.YELLOW
};
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
TestDrawSinus sinusPane = new TestDrawSinus();
JFrame frame = new JFrame();
frame.add(sinusPane);
JButton button1 = new JButton();
button1.setText("plot");
frame.add(button1, BorderLayout.SOUTH);
button1.addActionListener(new ActionListener() {
private List<Color> colors = new ArrayList<>();
private int freq = 0;
#Override
public void actionPerformed(ActionEvent e) {
sinusPane.addSinusPlot(generateSinus(++freq, nextColor()));
sinusPane.addSinusPlot(generateSinus(++freq, nextColor()));
sinusPane.addSinusPlot(generateSinus(++freq, nextColor()));
}
protected Color nextColor() {
if (colors.isEmpty()) {
colors.addAll(Arrays.asList(masterColors));
Collections.shuffle(colors);
}
return colors.remove(0);
}
});
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
private Random rnd = new Random();
private SinusPlot generateSinus(int freq, Color color) {
double[] x = new double[200];
double[] y = new double[200];
for (int i = 0; i < 200; i++) {
x[i] = (double) i;
y[i] = 100 * Math.sin(2d * Math.PI * freq * i / 200d);
}
return new SinusPlot(x, y, color);
}
public class SinusPlot {
private double[] x;
private double[] y;
private Color color;
public SinusPlot(double[] x, double[] y, Color color) {
this.x = x;
this.y = y;
this.color = color;
}
public int getSize() {
return x.length;
}
public double getXAt(int index) {
return x[index];
}
public double getYAt(int index) {
return y[index];
}
public Color getColor() {
return color;
}
}
public class TestDrawSinus extends JPanel {
private List<SinusPlot> plots = new ArrayList<>(8);
public void addSinusPlot(SinusPlot plot) {
plots.add(plot);
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
for (SinusPlot plot : plots) {
System.out.println(plot.getColor());
g2d.setColor(plot.getColor());
for (int i = 0; i < plot.getSize(); i++) {
Ellipse2D dot = new Ellipse2D.Double(((getWidth() - plot.getSize()) / 2) + plot.getXAt(i), plot.getYAt(i) + getHeight() / 2, 10, 10);
g2d.fill(dot);
}
}
g2d.dispose();
}
}
}
null layouts aren't going to help you, take the time to learn understand how the layout management system works, it will save you lot of time and effort. See Laying Out Components Within a Container for more details.
static is not your friend (especially in this context), make the effort to understand how to live without it (and when to use it)

List of graphics2D

Howcome this code below wont work? I want to add new Ovals to the ArrayList every 200 ms and display them and run them one by one. It works fine when Im running one particle s.runner(); but it doesnt seem to run all my particles.
MAIN:
import java.awt.EventQueue;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.JFrame;
import javax.swing.Timer;
public class ExempelGraphics extends JFrame implements ActionListener {
Timer t;
private int inc = 0;
ArrayList<Surface> particle = new ArrayList<>();
Surface s;
public ExempelGraphics() {
t = new Timer(10, this);
t.start();
s = new Surface(10, 10);
initUI();
}
private void initUI() {
add(s);
setSize(350, 250);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void actionPerformed(ActionEvent e) {
// s.runner();
// add
if (inc++ % 20 == 0) {
particle.add(new Surface(10, 10));
}
// display
for (int i = 0; i < particle.size(); i++) {
Surface p = particle.get(i);
p.runner();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
ExempelGraphics ex = new ExempelGraphics();
ex.setVisible(true);
}
});
}
}
GRAPHICS:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.JPanel;
public class Surface extends JPanel {
private int locX = 0;
private int locY = 0;
public Surface(int locX, int locY) {
this.locX = locX;
this.locY = locY;
}
public void runner() {
locX = locX + 1;
repaint();
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fillOval(locX, locY, 10, 10);
}
}
I think that you're program structure is broken. You should have only one JPanel here that does the drawing, that has its paintComponent overridden, and your Surface class should be a logical class and not a component class -- in other words, don't have it extend JPanel, and give it a public void draw(Graphics g) method where you draw the oval. Then have the drawing JPanel hold an ArrayList of these surfaces, and in the main JPanel's paintComponent method, iterate through the surfaces, calling each one's draw method.
Also your Timer's delay is not realistic and is too small. 15 would be much more realistic.
Also, don't call repaint() from within surface, since that will generate too many repaint calls unnecessarily. Instead call it from within the Timer's ActionListener after calling the runner methods on all the Surface objects.
Also note that every time you add a component to a JFrame's contentPane in a default fashion, you cover up the previously added components. If you go by my recommendations above, this isn't an issue since you'd only be adding that single JPanel to it.
For example:
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
#SuppressWarnings("serial")
public class ExampleGraphics2 extends JPanel {
private static final int PREF_W = 650;
private static final int PREF_H = 500;
private static final int TIMER_DELAY = 20;
private List<Surface> surfaces = new ArrayList<>();
public ExampleGraphics2() {
new Timer(TIMER_DELAY, new TimerListener()).start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Surface surface : surfaces) {
surface.draw(g);
}
}
#Override
public Dimension getPreferredSize() {
if (isPreferredSizeSet()) {
return super.getPreferredSize();
}
return new Dimension(PREF_W, PREF_H);
}
private class TimerListener implements ActionListener {
private int index = 0;
#Override
public void actionPerformed(ActionEvent e) {
index++;
index %= 20;
if (index == 0) {
surfaces.add(new Surface(10, 10));
}
for (Surface surface : surfaces) {
surface.runner();
}
repaint();
}
}
private static void createAndShowGui() {
JFrame frame = new JFrame("Example Graphics 2");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ExampleGraphics2());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(() -> createAndShowGui());
}
}
package foo1;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class Surface {
private int locX = 0;
private int locY = 0;
public Surface(int locX, int locY) {
this.locX = locX;
this.locY = locY;
}
public void runner() {
locX = locX + 1;
}
public void draw(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.RED);
g2d.fillOval(locX, locY, 10, 10);
}
}

Java applet repaint a moving circle

I've just moved over from Pygame so Java 2D in an applet is a little new to me, especially when it comes to repainting the screen. In pygame you can simply do display.fill([1,1,1]) but how do I do this in an applet in Java? I understand the use of repaint() but that doesn't clear the screen - any moving object is not 'removed' from the screen so you just get a long line of painted circles.
Here's my code that I've been testing with:
package circles;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.Random;
public class circles extends Applet implements Runnable {
private static final long serialVersionUID = -6945236773451552299L;
static Random r = new Random();
String msg = "Click to play!";
static int w = 800, h = 800;
int[] txtPos = { (w/2)-50,(h/2)-50 };
int[] radiusRange = { 5,25 };
int[] circles;
static int[] posRange;
int x = 0, y = 0;
int radius = 0;
int cursorRadius = 10;
boolean game = false;
public static int[] pos() {
int side = r.nextInt(5-1)+1;
switch(side) {
case 1:
posRange = new int[]{ 1,r.nextInt(w),r.nextInt((h+40)-h)+h,r.nextInt(270-90)+90 };
break;
case 2:
posRange = new int[]{ 2,r.nextInt((w+40)-w)+w,r.nextInt(h),r.nextInt(270-90)+90 };
break;
case 3:
posRange = new int[]{ 3,r.nextInt(w),r.nextInt(40)-40,r.nextInt(180) };
break;
case 4:
posRange = new int[]{ 4,r.nextInt(40)-40,r.nextInt(h),r.nextInt(180) };
break;
}
System.out.println(side);
return posRange;
}
public void start() {
setSize(500,500);
setBackground(Color.BLACK);
new Thread(this).start();
}
public void run() {
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics e) {
Graphics2D g = (Graphics2D) e;
if(System.currentTimeMillis()%113==0) {
x+=1;
y+=1;
}
g.setColor(Color.BLUE);
g.fillOval(x,y,20,20);
repaint();
}
}
You need to call super.paint(g); in your paint method, as to not leave paint artifacts.
Never call repaint() from inside the paint method
Don't explicitly call paint, as you do in update(), when you mean to call reapaint()
just update the x and y values from inside the update() method, then call repaint()
You don't need to take a Graphics argument in update()
You need to call update() somewhere repeatedly in a loop, as it updates the x and y and reapint()s
If your class is going to be a Runnable, then you should put some code in the run() method. That's probably where you should have your loop
import java.applet.Applet;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
public class circles extends Applet implements Runnable {
int x = 0, y = 0;
public void start() {
setSize(500, 500);
setBackground(Color.BLACK);
new Thread(this).start();
}
public void run() {
while (true) {
try {
update();
Thread.sleep(50);
} catch (InterruptedException ex) {
}
}
}
public void update() {
x += 5;
y += 6;
repaint();
}
public void paint(Graphics e) {
super.paint(e);
Graphics2D g = (Graphics2D) e;
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
}
Side Notes
Why use Applets in the first place. If you must, why use AWT Applet and not Swing JApplet? Time for an upgrade.
Here's how I'd redo the whole thing in Swing, using a Swing Timer instead of a loop and Thread.sleep, as you should be doing.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Circle extends JPanel{
private static final int D_W = 500;
private static final int D_H = 500;
int x = 0;
int y = 0;
public Circle() {
setBackground(Color.BLACK);
Timer timer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new Circle());
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}
See How to use Swing Timers
See Create GUIs with Swing
Here's more advanced example for you to look at and ponder.
UPDATE
"Problem is, that's a JPANEL application. I specifically want to make an applet easily usable on a web page. "
You can still use it. Just use the JPanel. Take out the main method, and instead of Applet, use a JApplet and just add the JPanel to your applet. Easy as that.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JApplet;
import javax.swing.JPanel;
import javax.swing.Timer;
public class CircleApplet extends JApplet {
#Override
public void init() {
add(new Circle());
}
public class Circle extends JPanel {
private static final int D_W = 500;
private static final int D_H = 500;
int x = 0;
int y = 0;
public Circle() {
setBackground(Color.BLACK);
Timer timer = new Timer(50, new ActionListener() {
public void actionPerformed(ActionEvent e) {
x += 5;
y += 5;
repaint();
}
});
timer.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.BLUE);
g.fillOval(x, y, 20, 20);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}
}
}

Repaint in Panel method not updated

I am trying to make a program that work like this:
In Window class every time I click on the button, the method panel2 of Panel is called: first it is drawing a first circle, then a second one (after the time defined in the timer). Then, I click again on the button, and it is drawing a fist circle, then a second one then a third one. etc.
The problem is that it when I click to obtain 3 circles appearing one after the other, the two first circles drawn at the previous step (before I pressed a second time the button) stay on the screen and only the third circle is drawn when i press the button (instead of having : first circle drawn, second circle drawn, third circle drawn). I hope I am clear.
Here is a simple code:
Window
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Window extends JFrame implements ActionListener{
int h = 2;
Panel b = new Panel();
JPanel container = new JPanel();
JButton btn = new JButton("Start");
JButton bouton = new JButton();
Panel boutonPane = new Panel();
public Window(){
this.setTitle("Animation");
this.setSize(300, 300);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
this.setLocationRelativeTo(null);
container.setBackground(Color.white);
container.setLayout(new BorderLayout());
JPanel top = new JPanel();
btn.addActionListener(this);
top.add(btn);
container.add(top);
this.setContentPane(container);
this.setVisible(true);
}
public void window2(){
this.setTitle("ADHD");
this.setSize(1000,700);
this.setLocationRelativeTo(null);
if (h < 11){
boutonPane.panel2(h);
bouton.addActionListener(this);
boutonPane.add(bouton);
this.add(boutonPane);
this.setContentPane(boutonPane);
updateWindow2();
}
this.setVisible(true);
}
public void updateWindow2(){
boutonPane.panel2(h);
this.revalidate();
this.repaint();
}
public void actionPerformed(ActionEvent e){
if ((JButton) e.getSource() == btn){
System.out.println("pressed0");
window2();
}
if ((JButton) e.getSource() == bouton){
h++;
System.out.println("pressed" + h);
updateWindow2();
}
}
public static void main(String[] args){
Window w = new Window();
}
}
Panel
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Panel extends JPanel implements ActionListener{
int m;
int u=0;
int lgi, lrgi;
int [] ta;
Timer timer1 = new Timer(300, this);
Panel(){
}
public void panel2(int n){
m=n;
ta = new int [n];
for(int it=0; it<m;it++){
ta[it]=100*it;
}
timer1.start();
}
public void paintComponent(Graphics gr){
super.paintComponent(gr);
gr.setColor(Color.red);
for(int i=0;i<m;i++){
gr.fillOval(ta[i],ta[i], 150, 150);
}
}
#Override
public void actionPerformed(ActionEvent arg0) {
if(u<m){
u++;
revalidate();
repaint();
}
}
}
Your code needs use two int values to decide how many circles to draw and when:
The first int should be the count of current circles to draw, say called, currentCirclesToDraw.
The second int will be the number of circles to draw total.
If you use a List<Ellipse2D> like I suggest, then this number will be the size of the list. So if the List is called ellipseList, then the 2nd number will be ellipseList.size().
The first variable will be incremented in the timer up to the size of the list, but no larger, and will be used by paintComponent method to decide how many circles to draw.
Key point here: the first number, the currentCirclesToDraw, must be re-set to 0 when the button is pressed. This way your paintComponent method will start out drawing 0 circles, then 1, then 2, ...
For example, the paintComponent method could look like so:
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(CIRCLE_COLOR);
for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
g2.fill(ellipseList.get(i));
}
}
I use the second term in the for loop conditional statement, i < currentCirclesToDraw && i < ellipseList.size() as an additional fail-safe to be sure that we don't try to draw more circles then we have in our list.
My Timer's ActionListener would increment the currentCirclesToDraw variable and call repaint. It would stop the Timer once currentCirclesToDraw reaches the size of the ellipseList:
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (currentCirclesToDraw < ellipseList.size()) {
currentCirclesToDraw++;
repaint();
} else {
// stop the Timer
((Timer)e.getSource()).stop();
}
}
}
And my button's actionPerformed method would reset currentCirclesToDraw to 0, would add a new Ellipse2D to my ellipseList (if we've not yet reached the MAX_CIRCLE_INDEX), would call repaint() to clear the JPanel, and would construct and start the Timer:
public void actionPerformed(java.awt.event.ActionEvent arg0) {
currentCirclesToDraw = 0; // this is key -- reset the index used to control how many circles to draw
if (ellipseList.size() < MAX_CIRCLE_INDEX) {
double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
double y = x;
double w = CIRCLE_WIDTH;
double h = CIRCLE_WIDTH;
ellipseList.add(new Ellipse2D.Double(x, y, w, h));
}
repaint(); // clear image
new Timer(TIMER_DELAY, new TimerListener()).start();
};
Edit 3/30/14
Note it all can be put together like this:
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.geom.Ellipse2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
/**
* http://stackoverflow.com/a/22714405/522444
* http://stackoverflow.com/questions/22712655/repaint-in-panel-method-not-updated
* #author Pete
*
*/
#SuppressWarnings("serial")
public class TimerCircles extends JPanel {
private static final int PREF_W = 1000;
private static final int PREF_H = 700;
private static final Color CIRCLE_COLOR = Color.RED;
public static final int MAX_CIRCLE_INDEX = 11;
public static final int TIMER_DELAY = 300;
public static final int CIRCLE_WIDTH = 100;
private final List<Ellipse2D> ellipseList = new ArrayList<>();
private int currentCirclesToDraw = 0;
public TimerCircles() {
add(new JButton(new ButtonAction("New Circle", KeyEvent.VK_C)));
}
#Override
public Dimension getPreferredSize() {
return new Dimension(PREF_W, PREF_H);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setColor(CIRCLE_COLOR);
for (int i = 0; i < currentCirclesToDraw && i < ellipseList.size(); i++) {
g2.fill(ellipseList.get(i));
}
}
private class ButtonAction extends AbstractAction {
public ButtonAction(String name, int mnemonic) {
super(name);
putValue(MNEMONIC_KEY, mnemonic);
}
public void actionPerformed(java.awt.event.ActionEvent arg0) {
currentCirclesToDraw = 0; // this is key -- reset the index used to control how many circles to draw
if (ellipseList.size() < MAX_CIRCLE_INDEX) {
double x = (ellipseList.size()) * CIRCLE_WIDTH / Math.pow(2, 0.5);
double y = x;
double w = CIRCLE_WIDTH;
double h = CIRCLE_WIDTH;
ellipseList.add(new Ellipse2D.Double(x, y, w, h));
}
repaint(); // clear image
new Timer(TIMER_DELAY, new TimerListener()).start();
};
}
private class TimerListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
if (currentCirclesToDraw < ellipseList.size()) {
currentCirclesToDraw++;
repaint();
} else {
// stop the Timer
((Timer)e.getSource()).stop();
}
}
}
private static void createAndShowGui() {
TimerCircles mainPanel = new TimerCircles();
JFrame frame = new JFrame("TimerCircles");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(mainPanel);
frame.pack();
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}

How to make animation move its legs while moving?

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);
}
}
}

Categories

Resources